爬虫总结
项目展示的数据都是由爬虫爬取的,在使用过程中出现了很多没有注意和意料之外的一些问题,特此总结一下提醒自己。
历程
爬虫开发的技术路线经过了好几个阶段,将一一总结:
- 原生写法urllib
- Requests + Gevent + Celery
- Scrapy + Redis
原生写法urllib
这个阶段是刚开始学习爬虫,对如何构造和理解爬虫以及相关库的用法都不熟悉,是学习踩坑阶段。主要学习了响应的解析和处理,XPath和正则提取数据。还探索性使用自动化测试组件Selenium,调用浏览器直接爬取数据,用以对付一些反爬虫策略复杂的网站。在对爬虫有了基础的了解以后,开始准备在生产环境使用。爬虫根据站点变化经常需要调整,也需要定时任务定期执行,为了适应这种情况进行一次技术升级。
主要缺点:结构不清晰,写法繁琐。修改爬虫的时候不方便,模块化程度不高。
Requests + Gevent + Celery
此阶段完成开发主要为:
- 模块化。
- 爬虫定时启动管理。
- 提高爬取效率,初步反爬虫策略。
针对每点都做了以下工作:
- 重新设计,开发了爬虫基类,包含启动和队列函数。子类继承后,只需要补全格式化数据和存储部分即可。采用requests替代python标准库,提升开发效率。
- 采用Celery作为爬虫任务管理,定时启动爬虫、超时删除等,并开发Flask-Celery(py3版本)插件。
- 从试验结果看来,爬虫运行时间主要浪费在I/O读写(网络通信)上,还占用&浪费CPU工作时间。故引入了协程Gevent,在I/O读写时CPU可以继续其他数据的分析和存储任务,总体上缩短爬虫运行时间。采用的初步的反爬虫措施,设置user-agent,随机间隔几秒启动对站点的访问请求。
配合WEB展示部分,直播爬虫站点在这个时期上线了,达到了自行更新直播房间的情况。但仍然存在以下的问题:
- 模块化仍然有优化的需要,仅能先频道后房间的方式爬取。
- 爬虫一旦超时或发生错误,Celery只能重新启动爬虫任务,无法在某个特定频道/房间请求上重试。
- 由于生产环境机器性能不足(AWS免费套餐),导致爬虫进程在运行占用CPU的时候,展示WEB站点的请求几乎得不到正确的响应。
这些问题都影响到项目正常运行,所以还是进行技术升级修正。恰逢此时,爬虫包scrapy终于完成了对py3的兼容,研究后认为它的框架设计基本上能解决我遇到的问题,而且更灵活,配置和修改爬虫都非常方便。故采用Scrapy进行升级替换。
Scrapy + Redis
Scrapy的基本模块和运行方式可以参照下图:
按运行顺序说明:
- Spider(请求构造工厂):开发人员自定义的爬虫请求Request、数据分析以及构造Item。构造后的爬虫请求Request进入Scheduler,Item进入Pipeline。
- Scheduler(调度程序):接收到构造好的爬虫请求后安排队列,按队列先进先出方式提供请求给Downloader。默认队列为multiprocess.queue,此处我采用了redis替换,这样当进程意外终止甚至机器重启的情况下仍可以恢复爬虫继续。
- Downloader(下载器):接收到请求Request后访问站点,将得到的响应返还给Spider。此处引入了user-agent替换插件,可根据已存在user-agent文本随机替换请求相应值。
- Pipeline(管道):在Spider初步处理得到Item,Item会被发送到Pipeline进一步处理,并且多个Spider可以生成相同格式的Item交给Pipeline统一处理。Item流动经过的Pipeline顺序通过weight(权重)设置。
- 以上均由Engine操作执行。
Scrapy采用Twisted实行多爬虫异步爬取,Twisted的异步进程处理非常强大,在等待I/O过程中会释放占用的CPU,这样多个爬虫即使在单CPU的情况下仍然能高效的切换进行。
可以说Scrapy解决了我所有的项目痛点,在生产环境中的表现也很好,目前运行稳定。
不采用PySpider理由
PySpider研究后就我理解来说,它更像是一揽子完备的解决方案,自由度低。Scrapy相对只是个第三方库,使用的方式更自由,且插件丰富,自己定制开发也方便,更适合个人项目。
阶段总结
目前Scrapy已经很好胜任了我的爬虫任务需要,接下来的爬虫功能追加将会在弹幕读取上下功夫。如果Scrapy无法满足需要,优先开发Scrapy自定义插件,实在不行才考虑替换Scrapy。