querylist采集微信公众号文章(爬取大牛用微信公众号爬取程序的难点及解决办法)
优采云 发布时间: 2022-01-04 06:16querylist采集微信公众号文章(爬取大牛用微信公众号爬取程序的难点及解决办法)
最近需要爬取微信公众号的文章信息。上网查了一下,发现微信公众号的难点是公众号文章的链接在PC端打不开。必须使用微信自带的浏览器(获取微信客户端的补充参数,才能在其他平台打开),给爬虫程序带来很大的麻烦。后来在知乎上看到了一个大牛用php写的微信公众号爬虫程序,他按照大佬的思路直接做了java。改造过程中遇到了很多细节问题,分享给大家。
附上大牛的链接文章:写php或者只需要爬取思路的可以直接看这个。这些想法写得很详细。
----------------------------------------------- -------------------------------------------------- -------------------------------------------------- --------------------------
系统的基本思想是在安卓模拟器上运行微信。模拟器设置代理,通过代理服务器拦截微信数据,并将获取到的数据发送给自己的程序进行处理。
需要准备的环境:nodejs、anyproxy代理、Android模拟器
nodejs下载地址:我下载的是windows版本的,直接下载安装就可以了。安装完成后直接运行C:\Program Files\nodejs\npm.cmd自动配置环境。
anyproxy安装:上一步安装好nodejs后,在cmd中直接运行npm install -g anyproxy就会安装
互联网上只有一个 Android 模拟器,很多。
----------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------------------
首先安装代理服务器的证书。 Anyproxy 默认不解析 https 链接。安装证书后即可解决。在 cmd 中执行 anyproxy --root 将安装证书。之后,您必须在模拟器上下载证书。
然后输入anyproxy -i命令开启代理服务。 (记得加参数!)
记住这个ip和端口,然后安卓模拟器的代理就会用到这个。现在用浏览器打开网页::8002/ 这是anyproxy的网页界面,用于展示http传输数据。
点击上方红框中的菜单,会显示一个二维码。使用安卓模拟器扫码识别。模拟器(手机)会下载证书并安装。
现在我们准备为模拟器设置代理,代理模式设置为手动,代理ip为运行anyproxy的机器ip,端口为8001
准备工作到此基本完成。在模拟器上打开微信,开通公众号文章。您可以从刚打开的网页界面看到 anyproxy 捕获的数据:
上面红框里是微信文章的链接,点进去查看具体数据。如果响应正文中没有任何内容,则可能是证书安装有问题。
如果一切顺利,你就可以下去了。
这里我们依靠代理服务来抓取微信数据,但是我们不能抓取一条数据,只能自己操作微信。最好手动复制。所以我们需要微信客户端自己跳转到页面。这时候可以使用anyproxy来拦截微信服务器返回的数据,将页面跳转代码注入其中,然后将处理后的数据返回给模拟器,实现微信客户端的自动跳转。
在anyproxy中打开一个名为rule_default.js的js文件,windows下文件为:C:\Users\Administrator\AppData\Roaming\npm\node_modules\anyproxy\lib
文件中有一个方法叫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 {//防止报错退出程序
7 var reg = /msgList = (.*?);/;//定义历史消息正则匹配规则
8 var ret = reg.exec(serverResData.toString());//转换变量为string
9 HttpPost(ret[1],req.url,"/InternetSpider/getData/showBiz");//这个函数是后文定义的,将匹配到的历史消息json发送到自己的服务器
10 var http = require('http');
11 http.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 }
26 callback(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 != []) {
49 HttpPost(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的原理。
66 res.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/getmasssendmsg开头,另一种以/mp/profile_ext开头。历史页面可以关闭。如果被拒绝,则会触发一个js事件,发送获取json数据(下一页内容)的请求。还有公众号文章的链接,以及文章的阅读量和喜欢的链接(返回json数据)。这些链接的形式是固定的,可以通过逻辑判断来区分。这里的问题是,如果所有的历史页面都需要爬取,怎么做。我的想法是模拟鼠标滑过js触发提交加载列表下一部分的请求。或者直接使用anyproxy分析滑动加载请求,直接将请求发送到微信服务器。但是如何判断没有剩余数据一直存在问题。我正在抓取最新数据。我暂时没有这个需求,以后可能需要。有需要的可以试试。