用Python写网络爬虫(3)-下载缓存和并发下载
由于并发下载实在没有啥写的东西,不如就合并到这一节吧。
下载缓存
当需要抓取的数据发生变化时,我们需要重新下载整个网站。对于大型网站而言,重新抓取可能需要耗费几个星期的时间。一种可行的方式是对已爬取网页进行缓存的方案,可以让每个网页之下载一次。
添加缓存支持
使用回调添加缓存支持后,下载的流程是这样的:
- 接收一个新的URL;
- 该URL是否已被缓存;
- 如果已经缓存,检查:
- 之前的下载时是否发生5xx错误;
- 缓存是否已经过期失效;
- 如果缓存可用返回缓存内容;
- 否则,进行限速检测,然后下载;
- 新下载内容和状态码写入缓存,或更新缓存。
磁盘缓存
将下载到的网页按照URL的路径存储到文件系统中。
- 需要对URL进行安全映射,屏蔽掉非法的文件名字符;
- 不能超过文件系统对文件名最大长度的限制;
- URL路径可能会以
/
结尾,此时/
后面的空字符串就会成为一个非法的文件名。但是如果移除这个/
,使用父字符串作为文件名,又会造成无法保存其他URL的问题。
DiskCache类需要实现以下几个方法:
__init__(self, cache_dir='cache')
:初始化,设定缓存目录,设定过期时间;url_to_path(self, url)
:URL到文件路径的转换方法;__getitem__(self, url)
:根据URL获取缓存;__setitem__(self, url, result)
:存储缓存。
为了节约磁盘空间,可以使用zlib
模块对序列化字符串逆行压缩。
**缺点:**受制于本地文件系统的限制。包括:文件名使用的字符、文件名长度、每个目录的最大文件数和文件系统可存储的文件总数等。
数据库缓存
爬取时,我们可能需要缓存大量的数据,而又无须任何复杂的连接操作,因为我们将选用NoSQL数据库,这种数据库比传统的SQL数据库更容易扩展。
MongoDB是一个面向文档的数据库,非常适合存储网页文件。MongoDB中的键值可以重复,当使用insert
插入时,若使用了同一个键值,将会产生多条记录。避免重复的方式是使用upsert
,若键值已经存在则会更新记录,而不是插入新的纪录。
MongoCache类同样要实现__getitem__
和__setitem__
方法,同时在构造方法中,我们创建了timestamp索引。在达到给定时间戳一定秒数之后,MongoDB便可以自动删除记录。
不过,MongoDB缓存无法按照给定时间精确清理过期记录,会存在至多一分钟的延迟,这是由MongoDB的运行机制造成的——MongoDB运行了一个后台任务每分钟会检查一次过期记录。一般来说,由于缓存过期时间通常设定为几周或几个月,所以这个相对较小的延时不会存在太大问题。
并发下载
多线程+多进程,可以显著提升效率。但提升不是无限的,因为线程越多,线程间切换的时间也越长。
爬虫网络请求是IO阻塞的,因此使用多线程能提高效率。若是CPU计算阻塞的,则只有多进程+多核CPU才能提升效率。