Fiddler Everywhere实战PC端微信小程序数据抓取与Java自动化解析全流程最近在帮朋友分析一个微信小程序的数据获取需求时发现手动复制粘贴实在太低效。经过一番摸索终于搭建了一套完整的自动化工作流用Fiddler Everywhere抓包再用Java代码自动请求和解析数据。整个过程就像搭积木一样有趣特别是看到代码自动获取到数据的那一刻成就感爆棚。1. 环境准备与工具配置工欲善其事必先利其器。在开始之前我们需要准备好以下工具和环境Fiddler Everywhere比经典版Fiddler更现代化的抓包工具界面简洁功能强大Java开发环境JDK 8我习惯用IntelliJ IDEA但Eclipse或VS Code也可以项目依赖OkHttp用于网络请求Fastjson处理JSON数据1.1 Fiddler Everywhere基础设置安装好Fiddler Everywhere后第一次启动时会提示安装证书。这是必须的步骤否则无法捕获HTTPS流量。具体配置如下打开设置 → HTTPS → 勾选Capture HTTPS traffic在Connections中勾选Follow redirects automatically建议设置过滤规则只显示目标域名的请求避免信息过载注意首次使用时需要在PC和移动设备上安装Fiddler的根证书否则无法解密HTTPS流量。1.2 Java项目初始化创建一个Maven项目添加以下依赖到pom.xmldependencies !-- OkHttp -- dependency groupIdcom.squareup.okhttp3/groupId artifactIdokhttp/artifactId version4.9.3/version /dependency !-- Fastjson -- dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.83/version /dependency /dependencies2. 捕获微信小程序网络请求微信小程序的抓包有几个特殊点需要注意在PC端微信打开目标小程序确保Fiddler Everywhere正在捕获流量在小程序内进行操作触发目标API调用2.1 识别关键请求在Fiddler的会话列表中你会看到大量请求。重点关注请求方法为POST或GET的API调用响应内容为JSON格式的请求包含业务数据的响应体右键点击目标请求选择Edit in Composer这里可以看到完整的请求详情Headers包括User-Agent、Content-Type等重要信息Request Body通常包含查询参数或表单数据Query ParametersGET请求的URL参数2.2 分析请求结构一个典型的微信小程序API请求可能包含以下关键信息组件示例值说明Hostapi.weixin.qq.com请求域名User-AgentMicroMessenger/7.0微信特有标识Content-Typeapplication/json请求体格式Refererhttps://servicewechat.com/...小程序来源3. 从抓包到代码自动化实现有了抓包数据我们就可以开始编写自动化代码了。这个过程就像把手工操作翻译成Java代码。3.1 构建HTTP请求使用OkHttp构建请求的关键步骤// 1. 创建OkHttpClient实例 OkHttpClient client new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build(); // 2. 准备请求头 Headers headers new Headers.Builder() .add(Host, api.target.com) .add(User-Agent, MicroMessenger/7.0) .add(Content-Type, application/json) .build(); // 3. 准备请求体 String jsonBody {\page\:1,\size\:20}; RequestBody body RequestBody.create(jsonBody, MediaType.get(application/json)); // 4. 构建完整请求 Request request new Request.Builder() .url(https://api.target.com/data) .headers(headers) .post(body) .build();3.2 处理响应数据获取到响应后用Fastjson解析JSON数据// 执行请求 Response response client.newCall(request).execute(); String responseBody response.body().string(); // 解析JSON JSONObject json JSON.parseObject(responseBody); int total json.getIntValue(total); JSONArray items json.getJSONArray(data); // 遍历数据项 for (int i 0; i items.size(); i) { JSONObject item items.getJSONObject(i); String id item.getString(id); String name item.getString(name); System.out.println(id : name); }4. 高级技巧与实战问题解决在实际项目中我们经常会遇到一些特殊情况和挑战。以下是几个常见问题的解决方案。4.1 处理分页数据很多API采用分页返回数据我们需要循环请求直到获取所有数据int page 1; int pageSize 50; boolean hasMore true; ListJSONObject allItems new ArrayList(); while (hasMore) { String jsonBody String.format({\page\:%d,\size\:%d}, page, pageSize); RequestBody body RequestBody.create(jsonBody, MediaType.get(application/json)); Request request new Request.Builder() .url(apiUrl) .post(body) .build(); Response response client.newCall(request).execute(); JSONObject result JSON.parseObject(response.body().string()); JSONArray items result.getJSONArray(data); allItems.addAll(items.toJavaList(JSONObject.class)); hasMore page * pageSize result.getIntValue(total); page; } System.out.println(总共获取到 allItems.size() 条数据);4.2 处理签名验证一些小程序API会有签名验证需要在请求头中添加签名参数。通常签名算法可以在小程序前端代码中找到或者通过分析多个请求找出规律。// 示例签名方法 private String generateSignature(String params, String secret) { String raw params secret; return DigestUtils.md5Hex(raw); } // 使用签名 String params page1size20; String signature generateSignature(params, app_secret); Request request new Request.Builder() .url(apiUrl ? params) .addHeader(X-Signature, signature) .build();4.3 处理Cookie和Session有些API需要维护会话状态这时需要处理Cookie// 创建Cookie管理 CookieJar cookieJar new CookieJar() { private final HashMapString, ListCookie cookieStore new HashMap(); Override public void saveFromResponse(HttpUrl url, ListCookie cookies) { cookieStore.put(url.host(), cookies); } Override public ListCookie loadForRequest(HttpUrl url) { ListCookie cookies cookieStore.get(url.host()); return cookies ! null ? cookies : new ArrayList(); } }; // 使用带Cookie管理的Client OkHttpClient client new OkHttpClient.Builder() .cookieJar(cookieJar) .build();5. 工程化与最佳实践当代码规模变大时我们需要考虑更好的组织方式和工程实践。5.1 封装API客户端将API请求封装成专门的客户端类public class MiniProgramClient { private final OkHttpClient client; private final String baseUrl; public MiniProgramClient(String baseUrl) { this.client new OkHttpClient(); this.baseUrl baseUrl; } public JSONObject getData(int page, int size) throws IOException { String jsonBody String.format({\page\:%d,\size\:%d}, page, size); Request request new Request.Builder() .url(baseUrl /api/data) .post(RequestBody.create(jsonBody, MediaType.get(application/json))) .build(); Response response client.newCall(request).execute(); return JSON.parseObject(response.body().string()); } // 其他API方法... }5.2 异常处理与重试网络请求不稳定需要添加适当的异常处理和重试机制public JSONObject fetchDataWithRetry(String url, int maxRetries) { int retryCount 0; while (retryCount maxRetries) { try { Request request new Request.Builder().url(url).build(); Response response client.newCall(request).execute(); return JSON.parseObject(response.body().string()); } catch (IOException e) { retryCount; if (retryCount maxRetries) { throw new RuntimeException(请求失败达到最大重试次数, e); } try { Thread.sleep(1000 * retryCount); // 指数退避 } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } return null; }5.3 性能优化技巧处理大量数据时可以考虑以下优化使用连接池减少TCP连接开销启用响应缓存并行请求提高吞吐量// 创建高性能的OkHttpClient OkHttpClient client new OkHttpClient.Builder() .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES)) .cache(new Cache(new File(cache), 10 * 1024 * 1024)) // 10MB缓存 .build(); // 并行请求示例 ListString urls Arrays.asList(url1, url2, url3); ListCompletableFutureJSONObject futures urls.stream() .map(url - CompletableFuture.supplyAsync(() - { try { Request request new Request.Builder().url(url).build(); Response response client.newCall(request).execute(); return JSON.parseObject(response.body().string()); } catch (IOException e) { throw new RuntimeException(e); } })) .collect(Collectors.toList()); // 等待所有请求完成 ListJSONObject results futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList());