很多时候我们都有监测某个网站变化的需求,比如是否发布新通知了,是否有新更新了等。之前使用过Chrome的几个插件来实现这个功能,不过用起来都觉得不是很方便,于是就想自己用Python写一个,之后将程序部署在服务器上即可实现24小时自动监测了。
用了几天时间写了个还算可以用的程序出来,可以实现以下功能:
- 定时去检查网站的某页面(或某几个页面),获取此时发布的信息,并与数据库中的数据对比,如果检测到新发布的信息,则将其加入数据库中,并通过微信公众号进行自动发布;
- 可以实现关键词过滤功能,检查发布信息标题及内容页是否有指定关键词,如果没有的话忽略此信息;
- 实现了一个相对比较通用的程序架构,可以很方便的向其中添加新网站。
项目已上传至GitHub:https://github.com/g199209/Spider
这个系统其实和爬虫所用的技术基本是一样的,下面来总结下此系统的实现方法。
主程序
主程序为Spider.py
,使用时运行此程序即可,此脚本执行过程伪代码如下:
1 | Initialize(WebList) |
首先初始化日志及网站列表,之后进入死循环,依次从网站列表中取出各网站,检查是否有更新及发布新消息,若此过程发生异常需进行处理,最后挂起一段时间进行下一次循环。
此处的网站列表是各网站实例化对象的一个List,如果要添加新网站在此List中增加新元素即可。
WebsiteBase类
WebsiteBase
类是所有网站类的基类,其中抽象了所有网站的共性行为,此类本身不能实例化,只能用于继承。
WebsiteBase
类中的公用接口函数只有3个:
1 | # 构造函数 |
下面分别说明这3个函数的实现。
构造函数__init__()
的执行流程伪代码如下:
1 | def __init__(): |
进行微信发布时需要用到的corpid
及corpsecret
在此处从文件中读取得到。
用于发布新消息的Update()
函数的执行流程伪代码如下:
1 | def Update(): |
实际代码中还增加了很多异常处理相关的代码,此处不多说明了。
用于获取网站新消息的GET()
函数是此类的核心,其执行流程伪代码如下:
1 | def GET(): |
PageRange
指页面参数,有时需要访问同一个网站的若干页面,就可以通过此参数来进行循环,以简化程序代码。
Enclose
指感兴趣的目标区域,先进行一次筛选,去掉无用的部分以便于后续操作。
Tags
指只包含一条消息记录的HTML部分,一般很容易从感兴趣的目标区域中分离出每一条消息记录来,之后就可以循环对每一条消息进行处理了。
对每一条消息进行处理时,先获取标题,URL地址等信息,之后先判断此条消息对应的URL之前是否访问过,要是已经访问过就跳过。Addition Check用于执行一些附加检测,如果不符合要求也跳过此条消息。如果此条消息没有被检查过,就进行关键词检查,这一步是可选的,要是没有设置关键词就跳过。检查后不符合要求也跳过此条消息,不过会将此条消息加入已检查数据库中,下次就不再检查了。最后,如果此消息通过了上述所有检查,而且是一条尚未发布的新消息,那么就将其加入待发布数据库中,之后调用Update()
函数时会进行微信发布。
GET()
函数中包含了很多获取网页信息的方法,这些方法在此基类中都没有实现,需要具体的网站子类去实现。这类似于C++中的虚函数,不过Python中没有这个概念。
具体网站子类
每一个网站就是一个类,此类必须继承自WebsiteBase
类,WebsiteBase
类中封装了获取信息及发布信息的整个基本流程,子类中只需要实现一些与具体网站相关的方法即可,包含构造函数具体有10个。Template.py
文件就是具体网站子类实现的一个模板文件,如果需要添加新网站只需要复制此文件,在此基础上进行修改即可。具体需要修改的内容可以参考Github项目中的ReadMe文档。
异常处理机制
因为涉及到网络连接,难免会发生超时、无法访问等各种异常,在一个良好的设计中,此时程序是不应该崩溃的,而是能跳过此异常网站继续运行其他部分,并在之后网络正常后自动恢复。
而且因为这个程序是运行在服务器上的,需要在发生异常和从异常中恢复时能提示下我,以便及时去进行处理。为实现这一目的,在WebsiteBase
类中设置了一个err
成员用于记录错误状态,当err
由False
变为True
时自动发送一条错误报告;当err
由True
变为False
时自动发送一条错误恢复报告。之所以设置为边沿触发是考虑到异常可能会持续一段时间,每次都发送报告没有什么必要。
另外,在WebsiteBase
类中也有自己的try...except
语句,这是为了当一个页面发生异常时并不是退出整个网站的处理,而是继续处理下一条消息记录。最后用raise
把这个异常送至顶层进行统一处理。
数据库设计
每一个网站子类实例都使用了两个数据库,一个用于记录哪些URL访问过了,另一个用于记录待发布信息的状态。
如果一个URL之前已经访问过了,那之后就不需要再访问了,以此优化程序的性能。而待发布信息数据库中包含了待发布的消息相关信息,还有一个Published
字段用于记录此消息是否已经发布了,在GET()
方法添加记录到数据库中时此字段是0
,在Update()
方法中会检查Published
字段为0
的记录依次进行发布,并将Published
字段置为1
。
杂项
微信发布使用了微信企业号,这种类型的公众号没有每天发送消息的上限,极为适合用来做消息通知。企业号的另一个特性是不能自由关注,需要提前预先加到通信录中的人才能关注,这也很适合此系统的需要。关于微信的接口,其开发者帮助中有很详尽的说明,照着操作就可以了。
关于网站编码问题,有些网站用的是utf-8
编码,有些用的是gb2312
编码,实际测试表明,对于用gb2312
编码的网站,若是不手动指定编码格式读到的就是乱码,所以还是建议都手动指定一下编码格式。
和爬虫一样,如果抓取频率太高的话是有可能被网站给禁掉的,网上有一些使用代理等方法,不过最简单也是最道德的解决方案是降低抓取频率,在此系统中连续抓取频率限定为3s一次,这么低的频率和正常人类访问也差不多,一般来说是不会被禁掉的,也不会对对方网站服务器造成过大的压力。