微信公众号文章爬取之:服务端数据采集 | 珊瑚贝

本文转载自:陈文管的博客 – 微信公众号文章爬取之:服务端数据采集 本篇内容介绍微信公众号文章服务端数据爬取的实现,配合上一篇微信公众号文章采集之:微信自动化,构成完整的微信公众号文章数据采集系统。

公众号文章爬取系统架构图

公众号文章爬取系统架构图

一、AnyProxy 配置(Mac)

AnyProxy 是一个开放式的 HTTP 代理服务器,官方文档:http://anyproxy.io/cn/ Github 主页:https://github.com/alibaba/anyproxy 主要特性包括: 基于 Node.js,开放二次开发能力,允许自定义请求处理逻辑 支持 Https 的解析 提供 GUI 界面,用以观察请求

1、安装 NodeJS

在安装 Anyproxy 之前,需要先安装 Nodejs。Nodejs 下载地址:http://nodejs.cn/download/。 下载安装完之后可以在终端执行以下命令查看所安装的版本:

1
2
 node --version       查看node安装版本
npm -v               查看npm安装版本

2、AnyProxy 安装配置

1) Mac 端的安装配置

AnyProxy 不要安装最新的版本,因为接口变动较大,不便于在原来的基础上重写接口,如果已经安装最新的版本,先执行以下命令卸载:

1
sudo npm uninstall -g anyproxy

之后安装 3.X 版本:

1
sudo npm install  anyproxy@3.x  -g

接着安装相应的证书:

1
anyproxy --root
2) AnyProxy rule_default.js 文件的配置

直接拷贝如下的配置覆盖 AnyProxy rule_default.js 配置文件即可,具体可参考知乎大神的文章:微信公众号内容的批量采集与应用 ,其中关于图片的优化,配置的 fs.readFileSync () 参数替换成自己的图片放置路径。将公众号里面的所有图片替换成本地图片的目的是减轻网络传输压力和浏览器占用的内存,有效的提高运行效率,可以自己制作一张 1×1 像素的 png 透明图片。 这边跟知乎文章不同的是,在 replaceServerResDataAsync 中只需要把拦截的微信文章 URL 地址转发到自己的服务器,因为自动化浏览脚本是直接进入到公众号文章的详情页面,就不需要像知乎文章介绍的那样那么麻烦。 TIPS: 在 2019.5.6-2019.5.12 时间段之间,微信公众号更新了公众号文章的请求加载方式。 在 replaceServerResDataAsync 接口中拦截 URL 的方式已经行不通, 通过 AnyProxy 拦截的 URL 参数可以看到已经没有了”/s?__biz=” 开头的 URL, 但是从

1
/mp/getappmsgext?”和“/mp/getappmsgad?“

开头的请求链接点击进去还是可以看到文章的请求链接地址。 如果是 2019.5.12 号之前的时间点,拦截 URL 接口在 replaceServerResDataAsync,对应的 AnyProxy rule_default.js 配置文件为:rule_default_before20190512.js 在 2019.5.12 号之后的时间点,拦截 URL 的接口变动到 shouldUseLocalResponse : function (req,reqBody),只要把 request body 发送到后台服务器,再加上”https://mp.weixin.qq.com/s?” 前缀进行拼接就行,对应的 AnyProxy rule_default.js 配置文件应该改为:rule_default_after20190512.js 如果忘记了 AnyProxy 的安装路径,用命令查找 rule_default.js 文件即可:

1
find ~ -iname "rule_default.js"
3)AnyProxy 启动

在终端执行命令启动 AnyProxy:

1
anyproxy -i

如果遇到如下的异常说明缺少写文件夹的权限:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
the default rule for AnyProxy.
Anyproxy rules initialize finished, have fun!
The WebSocket will not work properly in the https intercept mode :(
fs.js:885
  return binding.mkdir(pathModule._makeLong(path),
                 ^
Error: EACCES: permission denied, mkdir '/Users/chenwenguan/.anyproxy/cache_r929590'
    at Object.fs.mkdirSync (fs.js:885:18)
    at Object.module.exports.generateCacheDir (/Users/chenwenguan/.nvm/versions/node/v8.9.3/lib/node_modules/anyproxy/lib/util.js:54:8)
    at new Recorder (/Users/chenwenguan/.nvm/versions/node/v8.9.3/lib/node_modules/anyproxy/lib/recorder.js:16:31)
    at /Users/chenwenguan/.nvm/versions/node/v8.9.3/lib/node_modules/anyproxy/proxy.js:116:43
    at ChildProcess.exithandler (child_process.js:282:5)
    at emitTwo (events.js:126:13)
    at ChildProcess.emit (events.js:214:7)
    at maybeClose (internal/child_process.js:925:16)
    at Socket.stream.socket.on (internal/child_process.js:346:11)
    at emitOne (events.js:116:13)

用以下命令修改下文件夹权限即可:

1
sudo chown -R `whoami` /Users/chenwenguan/.anyproxy
4)Android 虚拟机上的配置

AnyProxy 启动完成后,访问 GUI 地址:http://192.168.1.101:8002

下载AnyProxy证书文件

下载 AnyProxy 证书文件

点击下载 rootCA.crt 文件,可以在虚拟机 SD 卡根目录下新建一个 rootCA 文件夹,把文件用 adb 命令的方式 Push 到虚拟机的 sdcard 目录下:

1
adb push rootCA.crt /sdcard/rootCA/

之后进入 Android 虚拟机系统设置界面,进入安全设置项,选择从 SD 卡安装(从 SD 卡安装证书)设置项,选择 Push 到 sd 卡下的证书文件安装,如果没有做这个操作,在微信加载 WebView 的时候会不断地弹出警告弹窗。 如果没有在模拟器找到系统设置或 WI-FI 网络设置的入口,可用 adb 命令调用进入,直接进入网络设置页面命令如下:

1
adb shell am start -a android.intent.action.MAIN -n com.android.settings/.wifi.WifiSettings

进入模拟器系统设置页面命令:

1
adb shell am start com.android.settings/com.android.settings.Settings

在 Android 模拟器上还要设置网络代理,长按 WIFI 网络设置项,弹窗选择修改网络选项,IP 地址就写电脑的 IP,端口填 8001。

安卓虚拟机网络代理设置

安卓虚拟机网络代理设置

在以上都配置完毕之后,进入微信应用查看公众号文章,就可以在 GUI 界面上看到 AnyProxy 拦截到的所有请求 URL 地址信息。 如文章前面的说明,在 2019.5.12 时间点之前还可以看到”/s?__biz=” 开头的 URL 请求参数。

AnyProxy 拦截的URL信息

AnyProxy 拦截的 URL 信息

上面 /s?__biz = 开头的 URL 就是微信公众号文章详细的 URL 地址,可以点击查看具体的详细信息:

微信公众号文章URL详细信息

微信公众号文章 URL 详细信息

页面往下滑动,查看请求到的公众号文章详细字段信息,服务端爬虫就是从这些字段参数定义的值来截取需要的信息。

AnyProxy解析的公众号文章详细信息

AnyProxy 解析的公众号文章详细信息

目前在服务端实现保存的字段只是一些基本的信息,如标题、作者、文章发布时间等,如果需要其他信息可以参考上图的一些字段做正则匹配。 在 2015.5.12 时间点,微信变动公众号文章加载方式之后,文章的实际地址参数在 “/mp/getappmsgext?” 开头的请求链接里面,包括点赞和阅读数据也在这个请求返回的结构体里面。在 “ /mp/getappmsgad?“开头的请求链接 request body 也是文章的链接地址,但选择 “/mp/getappmsgext?” 开头的 URL 来拦截处理比较好。

拦截getappmsgext的请求结构体就是文章实际地址

拦截 getappmsgext 的请求结构体就是文章实际地址

在 getappmsgext 拦截的页面往下滑动到 response body 就可以看到文章的阅读和点赞数据,因为这边没有阅读和点赞的数据解析需求,有需要的自行研究下从 rule_default.js 配置文件哪个接口拦截转发数据。

拦截getappmsgext的请求返回的数据包括阅读和点赞数

拦截 getappmsgext 的请求返回的数据包括阅读和点赞数

二、JavaWeb 服务端实现

1、运行环境配置

Intellij IDEA 官网下载地址:https://www.jetbrains.com/idea/ 破解方法参考:IntelliJ IDEA 2017 完美注册方法 TIPS:要先打开 IDEA 之后再做如下配置,否则会被识别为文件已损坏

1
-javaagent:/Applications/IntelliJ IDEA.app/Contents/bin/JetbrainsCrack-2.7-release-str.jar

2、服务端实现

爬虫服务端实现 GitHub 源码地址:

1
[https://github.com/wenguan0927/WechatSpider](https://github.com/wenguan0927/WechatSpider)
1)实现类说明

公众号爬虫服务端实现源码类说明

公众号爬虫服务端实现源码类说明

WechatController 类做 AnyProxy 转发的文章链接接收和 JSP 页面显示的逻辑处理。 mapper 文件夹下的两个类是数据库操作的映射操作类,通过配置文件自动生成,只是手动加了几个数据查询方法的实现,PostKeyWordMapper 用来操作存储公众号文章关键词的数据,WechatPostMapper 用来操作存储公众号文章详细数据。 model 文件夹下 PostJSP 只是用来 JSP 页面显示数据的一个中间类,在 JSP 页面中去拼接包含较多特殊字符的文本内容容易出问题,我这边的实现是要直接生成 MarkDown 文档的格式,所以做了一层转化处理。PostKeyWord 是公众号关键字的类,WechatPost 是公众号文章详细数据类。 spider 文件夹下的类就是公众号文章关键字和公众号文章详细信息的爬取解析处理类。 util 文件夹下放的是工具类,SimHash 只是用来测试通过关键字计算公众号文章关联性实现类,有兴趣可以自己做下挖掘。

2)配置文件说明

公众号爬虫服务端实现配置文件说明

公众号爬虫服务端实现配置文件说明

mybatis-mapper 文件夹下的两个文件是数据库映射 XML 资源文件,通过 generator.properties 和 generatorConfig.xml 两个配置文件自动生成,具体可参考:数据库表反向生成 (一) MyBatis-generator 与 IDEA 的集成 。 这边需要注意的是,如果要在反向生成的数据库映射操作文件中增加方法实现,不要在 Mapper.xml 文件里面添加方法,要加的话在 Mapper.java 的类中加,可参考 WechatPostMapper.java 类中末尾几个方法,通过在函数上添加注解的方式实现。 generator.properties 文件中的 jdbc.driverLocation 改成自己电脑的 connector 实际路径,jdbc.userId 和 jdbc.password 改成自己数据库的用户名和密码。 jdbc.properties 文件中的数据库参数也改成自己配置的值。 其他文件只是常规的 Web 实现配置,此处不做多余赘述。

3)实现过程中遇到的问题

1)@Autowired 注解的 Mapper 类报 NullPointException 异常

1
2
3
4
    @Autowired
    private WechatPostMapper wechatPostMapper;
    @Autowired
    private PostKeywordMapper postKeywordMapper;

这边需要注意的是通过 @Autowired 注解声明的类不能在一个 new 出来的类中使用,@Autowired 只能在通过框架注解生成的类中使用,在一个 new 出来的类中使用注解在框架生成的类中是找不到的,所以会报空指针异常。其他异常可参考:@Autowired 注解和静态方法 2)Intellj(IDEA) warning no artifacts configured 异常 参考文章:【错误解决】Intellj(IDEA) warning no artifacts configured 3)Intellij 代理端口占用异常

1
2
3
错误: 代理抛出异常错误: 
java.rmi.server.ExportException: Port already in use: 1099; nested exception is: 
java.net.BindException: Address already in use

终端输入命令查看端口所在进程:

1
sudo lsof -i :1099

之后可看到如下类似的结果:

1
2
COMMAND PID        USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
java    582 chenwenguan   23u  IPv6 0x38b6c6251709a7d3      0t0  TCP *:rmiregistry (LISTEN)

终端输命令杀进程:kill 582 4)http://java.sun.com/jsp/jstl/core cannot be resolved 如果配置的 jstl 版本是 1.2, 不需要通过导入 jstl.jar 和 standard.jar 包的方式,如果配置的是 1.2 以下的版本,可参考文章: core cannot be resolved。 jar 包的下载地址:

1
[http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/](http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/)

5) Warning:The/usr/local/mysql/data directory is not owned by the ‘mysql’ or ‘_mysql’

如果因 Mac 系统更新导致 MySQL 提示以上异常,执行以下命令解决:

1
sudo chown -R  _mysql:wheel  /usr/local/mysql/data

参考博文:Mac 在偏好设置启动 MySQL 失败 6)注解中的数据库 IN 查询语句实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Select({"<script>",
         "select",
         "id, biz, appmsgid, title, digest, contenturl, sourceurl, cover, datetime, readnum, ",
         "likenum, isspider, author, nickname, weight, posttype, content",
         "from postTable where nickname in ",
         "<foreach item='item' collection='nickname' open='(' close=')' separator=','>",
         "#{item}",
         "</foreach>",
         " and datetime >=#{datetime,jdbcType=TIMESTAMP}",
         "order by weight DESC",
         "</script>"
})
@ResultMap("ResultMapWithBLOBs")
List<WechatPost> getATAPosts(@Param("nickname") List<String> nickname, @Param("datetime") Date time);

如果是要在注解中实现 IN 多条件查询,需要如上面的方式去实现,直接按照原生 SQL 语句的方式实现是行不通的。 参考博文:SpringBoot 使用 Mybatis 注解开发教程 – 分页 – 动态 sql

4) 数据库实现

公众号文章详情数据表实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE TABLE `postTable` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `biz` tinytext,
  `appmsgid` tinytext,
  `title` tinytext,
  `digest` longtext,
  `contenturl` longtext,
  `sourceurl` longtext,
  `cover` longtext,
  `datetime` datetime DEFAULT NULL,
  `readnum` int(11) DEFAULT NULL,
  `likenum` int(11) DEFAULT NULL,
  `isspider` int(11) DEFAULT NULL,
  `author` tinytext,
  `nickname` tinytext,
  `weight` int(11) DEFAULT NULL,
  `posttype` int(11) DEFAULT NULL,
  `content` longtext,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=199 DEFAULT CHARSET=utf8

公众号关键字数据表实现:

1
2
3
4
5
6
7
CREATE TABLE `keywordTable` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `wordtext` varchar(45) DEFAULT NULL,
  `wordfrequency` int(11) DEFAULT NULL,
  `wordtype` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3525 DEFAULT CHARSET=utf8
5)遗留问题

公众号文章的分类目前没有很好地实现,也就是,目前爬取的公众号文章我要分为三大类,新闻类、Android 开发、技术扩展,一开始的想法是根据以往发布的每周技术周报文章内容,提取每个类别文章的关键词数据,生成一个关键词数据库,之后爬取的文章,可以通过提取文章的关键词跟历史记录文章的关键词词库进行对比,计算它们的相关性来进行归类。 目前用 HanLP 开源代码来做测试,提取的关键词都是中文的关键词,在做关联性计算的时候并不能达到理想的效果,因为开发类的文章有很多英文的词汇,HanLP 里面并不包括英文词汇的词库,所以下一步要做的是做一个技术类文章的分词词库来实现文章的归类处理。 这边给出一些参考文章的链接资源,有兴趣可以自己做一下深挖。 TextRank 算法提取关键词的 Java 实现 TextRank 算法提取关键词和摘要 计算两组标签 / 关键词相似度算法 HanLP 自然语言处理包开源 文本关键词提取算法解析 NLP 点滴 —— 文本相似度 文本相似性计算总结(余弦定理,simhash)及代码 如何实现一个基本的微信文章分类器 HanLP GitHub 开源代码

三、其他参考资料

Mac OS X 上 IntelliJ IDEA 13 与 Tomcat 8 的 Java Web 开发环境搭建 IntelliJ IDEA 15 创建 maven 项目 MyBatis 官网 Intellij IDEA 使用教程 HTML 语言中括号 (尖括号) 的字符编码 mac 下 mongodb 的安装与配置 mac 下 mongodb 的安装和使用 (使用终端操作) Intellij Mongo 配置 Java 连接 MongoDB 进行增删改查 IntelliJ IDEA 手动配置连接 MySQL 数据库 MongoDB 中文教程 MongoDB 官方文档 WebMagic 爬虫框架

来源:https://cuiqingcai.com/6761.html

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?