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


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

    image-20240117155829560

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

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

    image-20240117164551916

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

    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所示:

    image-20240117165301999

    根据笔者分析短链与Cookie携带信息不是一一对应关系,只要在Cookie有效期内就可以提供给任意短链获取信息。既然请求链接和请求参数我们都知道,那么就可以书写代码了,我这里是拿取阅读量:

      public AjaxResponse getNumberNewWay(String url) throws IOException {
            String realUrl = this.GetRealAddress(url);
            if (StringUtils.hasText(realUrl)) {
                // 使用分隔符 "&" 将参数字符串分割成参数对
                String[] parameterPairs = realUrl.split("&");
                // 创建一个 Map 来存储参数名和参数值
                Map 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 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所示:

    image-20240117171438590

    这之后只要根据接口给Java接口传入任意公众号短链都可以获取该短链的各项信息。

    本方法算是给不会Hook的同学的权宜之计,仅供学习参考使用,不可用于商业与违法途径。

  • 相关阅读:
    docker 记录下go用alpine作为基础镜像出现 no found的问题
    Flutter | 验证码的倒计时进入后台停止了怎么办?
    报考PMP怕上当,都会遇到哪些坑,我该如何避开这些坑?
    WPF-依赖属性和附加属性《十》
    11、IOC 之使用 JSR 330 标准注释
    Android11去掉Settings中的网络和互联网一级菜单
    谈谈我对云原生与软件供应链安全的思考
    Node.js教程
    java毕业设计云南美食管理系统Mybatis+系统+数据库+调试部署
    从现在开始:让AI写代码,你只负责敲tab键
  • 原文地址:https://www.cnblogs.com/jianpansangejian/p/17970546