栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > PHP

微信公众号批量爬取java版(一)

PHP 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

微信公众号批量爬取java版(一)

本文出自ljw不想加班的博客,原文地址

最近需要爬取微信公众号的文章信息。在网上找了找发现微信公众号爬取的难点在于公众号文章链接在pc端是打不开的,要用微信的自带浏览器(拿到微信客户端补充的参数,才可以在其它平台打开),这就给爬虫程序造成很大困扰。后来在知乎上看到了一位大牛用php写的微信公众号爬取程序,就直接按大佬的思路整了整搞成java的了。改造途中遇到蛮多细节问题,拿出来分享一下。

附上大牛文章链接:https://zhuanlan.zhihu.com/c_65943221 写php的或者只需要爬取思路的可以直接看这个,思路写的非常详细。


系统的基本思路是在安卓模拟器上运行微信,模拟器设置代理,通过代理服务器拦截微信数据,将得到的数据发送给自己的程序进行处理。

需要准备的环境:nodejs,anyproxy代理,安卓模拟器

nodejs下载地址:http://nodejs.cn/download/,我下载的是windows版的,下好直接安装就行。安装好后,直接运行C:Program Filesnodejsnpm.cmd 会自动配置好环境。

anyproxy安装:按上一步安装好nodejs之后,直接在cmd运行 npm install -g anyproxy 就会安装了

安卓模拟器随便在网上下一个就好了,一大堆。


首先为代理服务器安装证书,anyproxy默认不解析https链接,安装证书后就可以解析了,在cmd执行anyproxy --root 就会安装证书,之后还得在模拟器也下载这个证书。

然后输入anyproxy -i 命令 打开代理服务。(记得加上参数!)

记住这个ip和端口,之后安卓模拟器的代理就用这个。现在用浏览器打开网页:http://localhost:8002/ 这是anyproxy的网页界面,用于显示http传输数据。

点击上面红框框里面的菜单,会出一个二维码,用安卓模拟器扫码识别,模拟器(手机)就会下载证书了,安装上就好了。

现在准备为模拟器设置代理,代理方式设置为手动,代理ip为运行anyproxy机器的ip,端口是8001

到这里准备工作基本完成,在模拟器上打开微信随便打开一个公众号的文章,就能从你刚打开的web界面中看到anyproxy抓取到的数据:

上面红框内就是微信文章的链接,点击进去可以看到具体的数据。如果response body里面什么都没有可能证书安装有问题。

如果上面都走通了,就可以接着往下走了。

这里我们靠代理服务抓微信数据,但总不能抓取一条数据就自己操作一下微信,那样还不如直接人工复制。所以我们需要微信客户端自己跳转页面。这时就可以使用anyproxy拦截微信服务器返回的数据,往里面注入页面跳转代码,再把加工的数据返回给模拟器实现微信客户端自动跳转。

打开anyproxy中的一个叫rule_default.js的js文件,windows下该文件在:C:UsersAdministratorAppDataRoamingnpmnode_modulesanyproxylib

在文件里面有个叫replaceServerResDataAsync: function(req,res,serverResData,callback)的方法,这个方法就是负责对anyproxy拿到的数据进行各种操作。一开始应该只有callback(serverResData);这条语句的意思是直接返回服务器响应数据给客户端。直接删掉这条语句,替换成大牛写的如下代码。这里的代码我并没有做什么改动,里面的注释也解释的给非常清楚,直接按逻辑看懂就行,问题不大。

 1 replaceServerResDataAsync: function(req,res,serverResData,callback){
 2  if(/mp/getmasssendmsg/i.test(req.url)){//当链接地址为公众号历史消息页面时(第一种页面形式)
 3      //console.log("开始第一种页面爬取");
 4      if(serverResData.toString() !== ""){
 5    6   try {//防止报错退出程序
 7var reg = /msgList = (.*?);/;//定义历史消息正则匹配规则
 8var ret = reg.exec(serverResData.toString());//转换变量为string
 9HttpPost(ret[1],req.url,"/InternetSpider/getData/showBiz");//这个函数是后文定义的,将匹配到的历史消息json发送到自己的服务器
10var http = require('http');
11http.get('http://xxx/getWxHis', function(res) {//这个地址是自己服务器上的一个程序,目的是为了获取到下一个链接地址,将地址放在一个js脚本中,将页面自动跳转到下一页。后文将介绍getWxHis.php的原理。
12    res.on('data', function(chunk){
13    callback(chunk+serverResData);//将返回的代码插入到历史消息页面中,并返回显示出来
14    })
15});
16   }catch(e){//如果上面的正则没有匹配到,那么这个页面内容可能是公众号历史消息页面向下翻动的第二页,因为历史消息第一页是html格式的,第二页就是json格式的。
17   //console.log("开始第一种页面爬取向下翻形式");
18 try {
19    var json = JSON.parse(serverResData.toString());
20    if (json.general_msg_list != []) {
21    HttpPost(json.general_msg_list,req.url,"/xxx/showBiz");//这个函数和上面的一样是后文定义的,将第二页历史消息的json发送到自己的服务器
22    }
23 }catch(e){
24   console.log(e);//错误捕捉
25 }
26callback(serverResData);//直接返回第二页json内容
27   }
28      }
29      //console.log("开始第一种页面爬取 结束");
30  }else if(/mp/profile_ext?action=home/i.test(req.url)){//当链接地址为公众号历史消息页面时(第二种页面形式)
31      try {
32   var reg = /var msgList = '(.*?)';/;//定义历史消息正则匹配规则(和第一种页面形式的正则不同)
33   var ret = reg.exec(serverResData.toString());//转换变量为string
34   HttpPost(ret[1],req.url,"/xxx/showBiz");//这个函数是后文定义的,将匹配到的历史消息json发送到自己的服务器
35   var http = require('http');
36   http.get('xxx/getWxHis', function(res) {//这个地址是自己服务器上的一个程序,目的是为了获取到下一个链接地址,将地址放在一个js脚本中,将页面自动跳转到下一页。后文将介绍getWxHis.php的原理。
37    res.on('data', function(chunk){
38    callback(chunk+serverResData);//将返回的代码插入到历史消息页面中,并返回显示出来
39    })
40});
41      }catch(e){
42   //console.log(e);
43   callback(serverResData);
44      }
45  }else if(/mp/profile_ext?action=getmsg/i.test(req.url)){//第二种页面表现形式的向下翻页后的json
46      try {
47   var json = JSON.parse(serverResData.toString());
48   if (json.general_msg_list != []) {
49HttpPost(json.general_msg_list,req.url,"/xxx/showBiz");//这个函数和上面的一样是后文定义的,将第二页历史消息的json发送到自己的服务器
50   }
51      }catch(e){
52   console.log(e);
53      }
54      callback(serverResData);
55  }else if(/mp/getappmsgext/i.test(req.url)){//当链接地址为公众号文章阅读量和点赞量时
56      try {
57   HttpPost(serverResData,req.url,"/xxx/getMsgExt");//函数是后文定义的,功能是将文章阅读量点赞量的json发送到服务器
58      }catch(e){
59 
60      }
61      callback(serverResData);
62  }else if(/s?__biz/i.test(req.url) || /mp/rumor/i.test(req.url)){//当链接地址为公众号文章时(rumor这个地址是公众号文章被辟谣了)
63      try {
64   var http = require('http');
65   http.get('http://xxx/getWxPost', function(res) {//这个地址是自己服务器上的另一个程序,目的是为了获取到下一个链接地址,将地址放在一个js脚本中,将页面自动跳转到下一页。后文将介绍getWxPost.php的原理。
66res.on('data', function(chunk){
67    callback(chunk+serverResData);
68})
69   });
70      }catch(e){
71   callback(serverResData);
72      }
73  }else{
74      callback(serverResData);
75  }
76  //callback(serverResData);
77     },

这里简单解释一下,微信公众号的历史消息页链接有两种形式:一种以 mp.weixin.qq.com/mp/getmasssendmsg 开头,另一种是 mp.weixin.qq.com/mp/profile_ext 开头。历史页是可以向下翻的,如果向下翻将触发js事件发送请求得到json数据(下一页内容)。还有公众号文章链接,以及文章的阅读量和点赞量的链接(返回的是json数据),这几种链接的形式是固定的可以通过逻辑判断来区分。这里有个问题就是历史页如果需要全部爬取到该怎么做到。我的思路是通过js去模拟鼠标向下滑动,从而触发提交加载下一部分列表的请求。或者直接利用anyproxy分析下滑加载的请求,直接向微信服务器发生这个请求。但都有一个问题就是如何判断已经没有余下数据了。我是爬取最新数据,暂时没这个需求,可能以后要。如果有需求的可以尝试一下。

下图是上文中的HttpPost方法内容。

1 function HttpPost(str,url,path) {//将json发送到服务器,str为json内容,url为历史消息页面地址,path是接收程序的路径和文件名
 2     console.log("开始执行转发操作");
 3     try{
 4     var http = require('http');
 5     var data = {
 6  str: encodeURIComponent(str),
 7  url: encodeURIComponent(url)
 8     };
 9     data = require('querystring').stringify(data);
10     var options = {
11  method: "POST",
12  host: "xxx",//注意没有http://,这是服务器的域名。
13  port: xxx,
14  path: path,//接收程序的路径和文件名
15  headers: {
16      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
17      "Content-Length": data.length
18  }
19     };
20     var req = http.request(options, function (res) {
21  res.setEncoding('utf8');
22  res.on('data', function (chunk) {
23      console.log('BODY: ' + chunk);
24  });
25     });
26     req.on('error', function (e) {
27  console.log('problem with request: ' + e.message);
28     });
29     
30     req.write(data);
31     req.end();
32     }catch(e){
33  console.log("错误信息:"+e);
34     }
35     console.log("转发操作结束");
36 }

做完以上工作,接下来就是按自己业务来完成服务端代码了,我们的服务用于接收代理服务器发过来的数据进行处理,进行持久化操作,同时向代理服务器发送需要注入到微信的js代码。针对代理服务器拦截到的几种不同链接发来的数据,我们就需要设计相应的方法来处理这些数据。从anyproxy处理微信数据的js方法replaceServerResDataAsync: function(req,res,serverResData,callback)中,我们可以知道至少需要对公众号历史页数据、公众号文章页数据、公众号文章点赞量和阅读量数据设计三种方法来处理。同时我们还需要设计一个方法来生成爬取任务,完成公众号的轮寻爬取。如果需要爬取更多数据,可以从anyproxy抓取到的链接中分析出更多需要的数据,然后往replaceServerResDataAsync: function(req,res,serverResData,callback)中添加判定,拦截到需要的数据发送到自己的服务器,相应的在服务端添加方法处理该类数据就行了。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/227716.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号