基于Spring Cloud的全自动化微信公众号消息采集系统-正文和互动量(浏览量、点赞、在看)的爬取

373 阅读7分钟

前言

前面文章介绍了系统架构和PC端采集文章列表的实现,这篇文章主要介绍正文的爬取和文章浏览量、点赞数和在看数的获取。和之前一样,只介绍思路和逻辑,有需要的码友可以去gitee下载源码。

一、正文的爬取

前面PC端采集列表阶段已经获取到了文章的标题、时间、媒体、链接等信息,为了实现解耦合提高效率,将文章正文的采集逻辑提取出来一个模块来实现,并且通过rocketmq来通信,此般便可以查询到每一条新闻的爬取情况,便于管理,逻辑并不复杂,直接上流程:

  1. 继上篇文章核心流程7.2将文章基本信息存入数据库后将每一条数据的ID通过rocketmq发送给正文采集模块;
  2. 正文采集模块监听到mq里的消息后通过ID获取查询出文章的链接;
  3. 通过HttpClient请求链接,然后Jsoup解析HTML获取正文信息,将正文信息存储到数据库。

二、互动量的爬取

1、获取方式分析

玩过微信爬虫的朋友应该了解,微信互动量数据在非微信内置浏览器是看不到的,目前发现只有微信PC浏览器和微信手机端打开能看到这个数据,最简单的方式当然是抓包,研究发现互动量数据是异步加载的,和正文加载不是同一个请求,并且PC端好像有反爬机制,打开几个后就不显示互动量数据了,只能通过手机或者模拟器登录微信去访问,这加大了爬取的难度。 简单介绍下起初的获取方式:

  1. 将所有待爬取的数据(将ID当做一个参数拼装到链接的后面)放到一个队列(Q)里,
  2. 写一个中间页面用微信内置浏览器打开,一打开页面就访问后台从队列Q里POP出一个数据(其实就是一个带ID的链接),js自动打开这个链接,之后的事情主要就是Fiddler的工作。(此处省略处理上一次任务处理结果,第4步介绍)
  3. Fiddler主要负责两个事情:抓包和跳转。
    • 抓包:两个包需要抓
      • 微信正文链接,通过这个链接拿到ID,将ID发送给后台,后台将这个ID存到redis的一个Hash结构中
      • 互动量链接,将返回的json发送到后台,后台提取出互动量数据也存到这个Hash中
    • 跳转:针对微信新闻链接注入js控制一定时间后自动跳转到中间页面
  4. 再次进入这个页面后从redis中获取上一次处理的结果,将互动量通过ID存到数据库,然后获取下一个任务。
  5. 任务获取完成后和后端建立长连接等待。

这种方式亲测可行,可也不难发现这种方式的局限性:效率低、分布式下多模拟器实现复杂。但是这种方式是比较通用的,做过微信爬虫的朋友应该不难看出来,这种方式可以解决一个问题:搜狗微信爬虫的临时链接转换成永久链接,之后再详细介绍这个功能的实现。

既然这个方式不可行那该如何去做呢?首先发现问题已经成功了一半,就针对效率低、分布式下多模拟器实现复杂这两个问题深入研究。效率低是因为每一条都需要通过fiddler打开,这样稳定性也相对较差,分布式下需要多个模拟器,每个模拟器和代理,代理把数据发到后台后容易造成混乱。如果能不通过Fiddler直接能通过JAVA组装获取互动量的链接模拟请求该多好啊?

皇天不负有心人,通过研究微信链接和互动量请求链接,经历过九九八十一难之后发现微信互动量的获取链接是可以通过Fiddler获取出的用户信息、票据和新闻链接里的参数信息拼装出来的。但是过一段时间用户信息字段会失效,并且不同的公众号之间不能互通,所以失效或者切换公众号还是要通过fiddler去抓包获取用户信息和票据。即使这样也大大提高了工作效率。

互动量请求路径:mp.weixin.qq.com/mp/getappms… 需要的参数如下:

  • cookie:Fiddler获取,放在请求头中
  • ua:Fiddler获取,浏览器内核,放在请求头中
  • pass_ticket:Fiddler获取,票据
  • appmsg_token:Fiddler获取,用户信息
  • is_only_read:固定值“1”
  • is_temp_url:固定值“0”
  • appmsg_type:固定值“9”
  • key:固定值“777”
  • biz:Fiddler获取,链接里也有,就是公众号的唯一标识
  • sn:随机加密串,链接中存在
  • mid:图文消息id,链接中存在
  • idx:文章序号,链接中存在 大功告成:

image.png image.png

2、思路分析

数据结构: 1、每一个公众号以list的数据结构存到redis,key为:前缀+biz(每个公众号不同),value为:新闻链接;2、每一个公众号第一个新闻链接放到一个list中

自动化的实现: 模拟器集群和互动量获取模块集群通过nginx和socket建立长连接,每个模拟器都有编号,随机和一个模块节点建立连接,有任务时通过rocketmq广播给模块集群中的每一个节点,然后节点再通过socket发给空闲中的模拟器去打开一个公众号的文章的链接将需要的数据发给模块集群中任意一个节点去爬取互动量,此时此模拟器为忙碌状态,有任务来也不会再去使用它,然后自动跳转到中转页面,当这个公众号任务完成后将模拟器状态改为空闲,继续等待。

3、流程分析

还是先上流程图: 移动端逻辑.png 每一个模拟器随机跟一个节点建立长连接

  1. 继上篇文章核心流程7.2将文章基本信息存入数据库后,每个公众号完成之后都把其中一条新闻链接放入一个redis队列,称之为任务队列;
  2. PC端完成公众号正文爬取任务后马上发一个广播通知到每一个移动端模块节点;
  3. 节点收到通知后马上跟自己连接的空闲的模拟器发一个消息;
  4. 模拟器收到通知后去后台获取一个任务队列里的url并打开;
  5. Fiddler监测到感兴趣的链接后将需要的参数传递给后台;
  6. 后台得到参数后并给biz定位到biz队列,将模拟器改为忙碌状态,然后依次获取互动量并存储
    • 如果正常结束,就将模拟器改为空闲状态,然后再发广播执行步骤2
    • 如果中途出现失败情况,就将模拟器改为空闲状态,将url再次放回任务队列,然后再发广播执行步骤2 可以看出模拟器平时和节点是一直建立长连接的,有任务的时候会通过广播通知到然后主动去获取任务,执行完后再次建立长连接,以此来实现高效流的全自动化抓取。

附加功能

账号访问控制和频率控制:是为防止封号可以实时统计每个账号执行过多少任务并可以随时调节频率的,实现方式和PC端类似,不再赘述,有需求见代码。

总结

至此基于Spring Cloud的全自动化微信公众号消息采集系统的介绍已经完成,目前项目正在生产环境中运行,并且前台的管理页面也正在开发中,系统相对比较复杂,之后可能会用springboot搭建一个单节点的以便于日常快捷使用,也有想法写一写上面提到的临时链接转永久链接问题,届时欢迎码友们指正。

之前有朋友在gitee上加笔者微信,这个微信最近没有使用,前几个月一直在忙别的事情,很多添加信息已经过期,非常非常抱歉,之后我会整理好所有代码再次上传到gitee,溜了溜了。