写点什么

微信公众号短链实时获取阅读量、点赞数爬虫方案(不会 Hook 可用)

  • 2024-01-18
    福建
  • 本文字数:3504 字

    阅读完需:约 11 分钟

众所周知,微信分享的公众号分享出的一般都是短链,在这个短链下使用浏览器打开并不能获取微信公众的阅读量点赞数等这些信息,如图 1 所示。



但是实际拥有详细信息的则是这个链接下面,提取链接所需要提交的信息包括经过本人筛选有以下参数,并且携带 Cookie,如图 2 所示:



其中_biz、mid、sn 会根据不同的文章发生变动,其余的都是固定值,但都可以从原始链接中取得,且原始链接可以通过 F12 在谷歌浏览器前端获取,如图 3 所示:



所以我们现在就应当编写获取真是链接代码:


private String GetRealAddress(String url) throws IOException {        CloseableHttpClient httpClient = HttpClients.createDefault();        HttpGet httpGet = new HttpGet(url);
/// 解析html字符串 HttpResponse response = httpClient.execute(httpGet); String html = EntityUtils.toString(response.getEntity()); Document doc = Jsoup.parse(html); /// 获取GET参数 String realUrl = doc.select("meta[property='og:url']").attr("content"); //获取真实链接 httpClient.close(); return new URL(realUrl).getQuery(); }
复制代码


根据 Fiddler 软件抓包可以知道,阅读数等详细信息是在 https://mp.weixin.qq.com/mp/getappmsgext/...下,而要携带参数我们已经可以从真实长链中获取。别看图 2 的请求链接好像很长,但是实质上如果只是为了获取阅读量、点赞数等一些有用信息,只需要带上 appmsg_token 即可,如图 4 所示:



既然请求链接和请求参数我们都知道,那么就可以书写代码了:


  public AjaxResponse getNumberNewWay(String url) throws IOException {        String realUrl = this.GetRealAddress(url);        if (StringUtils.hasText(realUrl)) {            // 使用分隔符 "&" 将参数字符串分割成参数对            String[] parameterPairs = realUrl.split("&");            // 创建一个 Map 来存储参数名和参数值            Map<String, String> parameterMap = new HashMap<>();            // 遍历参数对,提取参数名和参数值,并放入 Map 中            for (String parameterPair : parameterPairs) {                String[] parts = parameterPair.split("=");                if (parts.length == 2) {                    String paramName = parts[0];                    String paramValue = parts[1];                    parameterMap.put(paramName, paramValue);                }            }            // 获取各个参数的值,因为上面使用"=="分割,所以biz的值后面的“==”也会被分割            String biz = parameterMap.get("__biz") + "==";            String mid = parameterMap.get("mid");            String idx = parameterMap.get("idx");            String sn = parameterMap.get("sn");            String appmsgToken = "";            if (StringUtils.hasText(cookie)){                //获取appmsgToken                String[] parts = cookie.split(";");                for (String part : parts) {                    if (part.startsWith("appmsg_token=")) {                        appmsgToken = part.substring("appmsg_token=".length());                        break;                    }                }            }else {                return AjaxResponse.error("cookie缺失");            }
String originUrl = "https://mp.weixin.qq.com/mp/getappmsgext?"; String postUrl = originUrl + "appmsg_token=" + appmsgToken + "&x5=0";

HttpPost httpPost = new HttpPost(postUrl); //模拟微信客户端 httpPost.setHeader("User-Agent", "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0Chrome/57.0.2987.132 MQQBrowser/6.2 Mobile"); httpPost.setHeader("Cookie", cookie); httpPost.setHeader("Origin", "https://mp.weixin.qq.com"); //httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
CloseableHttpClient client = HttpClientBuilder.create().build(); List<NameValuePair> parameters = new ArrayList<>(); parameters.add(new BasicNameValuePair("is_only_read", "1")); parameters.add(new BasicNameValuePair("is_temp_url", "0")); parameters.add(new BasicNameValuePair("appmsg_type", "9")); parameters.add(new BasicNameValuePair("__biz", biz)); parameters.add(new BasicNameValuePair("mid", mid)); parameters.add(new BasicNameValuePair("idx", idx)); parameters.add(new BasicNameValuePair("sn", sn)); HttpEntity entity = new UrlEncodedFormEntity(parameters,"utf-8"); httpPost.setEntity(entity);
// 发送请求并获取响应 try { HttpResponse response = client.execute(httpPost); HttpEntity responseEntity = response.getEntity(); String StringResult = EntityUtils.toString(responseEntity);
// 创建一个 ObjectMapper 对象 ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(StringResult);
// 提取 read_num 的值 JsonNode readJson = jsonNode.get("appmsgstat"); if (readJson != null) { return AjaxResponse.success(readJson.get("read_num").asText()); } return AjaxResponse.error("获取阅读量失败,请检查token是否过期"); } catch (IOException e) { e.printStackTrace(); return AjaxResponse.error("远程调用失败"); } } return AjaxResponse.error("获取真实地址失败"); }
复制代码


这里存在一个时效性的问题,由于微信设置的 Cookie 有时效性,且是由微信客户端自身生成,并且笔者不会 Hook。所以就想到了一个另类的办法:通过定时任务刷新微信文章来刷新 Cookie->利用 Fidder 拦截请求获取 Cookie->Cookie 传入 Java 代码中获取信息。


由于不会 Hook,所以笔者就用了 python 的 pyautogui 与 cv2 库识别微信浏览器的刷新图标,然后定时进行刷新,大体流程是:截一张桌面图像->识别刷新图标位置->鼠标模拟刷新。python 核心代码如下:


def refresh_funtion():    # 截一张桌面新的图片    newImg = pyautogui.screenshot()    newImg.save('C:\\Users\\chen\\Desktop\\newAll.png')    # 转换为OpenCV的图像格式    newImg_cv = cv2.cvtColor(cv2.imread('C:\\Users\\chen\\Desktop\\newAll.png'), cv2.COLOR_BGR2RGB)    # 寻找刷新按钮    # 如果文本框已经打开则进行刷新    refreshTemplate = cv2.imread('C:\\Users\\chen\\Desktop\\refresh.jpg')    # 在屏幕截图中搜索目标图像    refreshRes = cv2.matchTemplate(newImg_cv, refreshTemplate, cv2.TM_CCOEFF_NORMED)    # 获取匹配度最高的位置    refresh_min_val, refresh_max_val, refresh_min_loc, refresh_max_loc = cv2.minMaxLoc(refreshRes)    # 如果存在则刷新    # 如果匹配度最高的位置的阈值大于 0.8,则模拟鼠标点击    if refresh_max_val > 0.8:        # 计算鼠标点击的位置        x, y = refresh_max_loc[0] + refreshTemplate.shape[1] // 2, refresh_max_loc[1] + refreshTemplate.shape[0] // 2        # 模拟鼠标点击        pyautogui.click(x, y)
复制代码


这里就需要使用 FiddlerScript 脚本,在 Fiddler 软件的 FidderScipt 中的 OnBeforRequest 方法内插入图 5 中代码,将刷新的 Cookie 传递到 Java 代码中,再让 Java 代码根据传递到的 Cookie 解析并获取微信信息,如图 5 所示:


本方法仅提供不会 Hook 的同学的权宜之计,仅供学习参考使用,不可用于违法途径。


文章转载自:键盘三个键

原文链接:https://www.cnblogs.com/jianpansangejian/p/17970546

体验地址:http://www.jnpfsoft.com/?from=001

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
微信公众号短链实时获取阅读量、点赞数爬虫方案(不会Hook可用)_爬虫_不在线第一只蜗牛_InfoQ写作社区