用 Docker Compose 部署 PySpider
文章目录
[隐藏]
- Docker + LeanCloud
- 数据库和消息队列
- Docker Compose
- ResultWorker
- 还有话说
PySpider 是一个国人编写的强大的网络爬虫系统并带有强大的 WebUI。采用 Python 语言编写,分布式架构,支持多种数据库后端,强大的 WebUI 支持脚本编辑器,任务监视器,项目管理器以及结果查看器。
上面这段是从「PySpider 中文网」摘录的。总而言之,它就是一个 All-in-one 的爬虫系统,比 Scrapy 强的地方,主要就是上手更容易,打开 web 页面就可以开始写爬虫。但「开包即食」这一点,仅限于 demo 阶段,比如在 http://demo.pyspider.org 试用一下。真正到自己用的时候,还是要考虑一个部署的问题。尤其是要充分利用它的分布式架构的时候,怎么部署更是一个避不过去的问题。
我之前做过一个 side project,功能是从国内各大视频网站抓取动漫新番,如果发现更新,就推送通知到 iOS 客户端,iOS 客户端仅展示订阅的动漫列表,观看还需要跳转到各官方应用去。
做这个的动机是现在各大视频网站的版权竞争,导致要追一季新番,不得不来回切换各个视频网站,我又不想打开一堆通知,让它们给我推垃圾信息。所以就搞一个只推送动漫更新的应用吧。
这个项目爬虫端已经完成,iOS 客户端也提交了审核,然而就是审核没有通过,没过的原因有以下三点:
- 用了一些版权图片
- 在 webView 预览动漫的时候,某视频网站乱跳红包
- 审核人员不看动漫,把鸣人的儿子当成了我山寨的一个动漫形象。
后来发现自己已经有点脱宅了,也就没有再去和审核人员争论了,项目就这么流产,和许多其他 side project 一样。
下面是我在做这个 side project 的时候,部署 PySpider 的方案。
Docker + LeanCloud
现在要部署一个 web 项目,用 Docker 已经是首选了,可以节约不少时间。iOS 后端用现成的 LeanCloud 来做,不用写后端代码,又可以节约不少时间。
要用 LeanCloud,需要引入 LeanCloud 的 SDK,而 binux/pyspider 这个 image 并不包含。所以需要自己 build 一个 image 了。方法就是写一个 Dockerfile,内容如下:
FROM binux/pyspider:latest MAINTAINER suosuopuo <[email protected]> # include the LeanCloud SDK RUN pip install leancloud-sdk
然后执行 docker build -t my/pyspider – < Dockerfile,如果需要用到 LeanCloud SDK,只要用 my/pyspider 代替 binux/pyspider 就好了。
数据库和消息队列
尽管最终结果放在 LeanCloud 上,但 PySpider 各个组件运行还是需要数据库支持的。这部分主要参考这个 Deployment of demo.pyspider.org。数据库是 postgresql,消息队列用 redis。
数据库和消息队列手动用 docker 启动,不用 docker-compose 管理,这和 Deployment of demo.pyspider.org 也是一致的。
启动就两条命令:
docker run --name redis -d -p 6379:6379 redis docker run --name postgres -v /data/postgres/:/var/lib/postgresql/data -d -e POSTGRES_PASSWORD="" postgres
数据库和用户需要手动创建:
docker exec -it postgres bash
然后输入:
psql -U postgres CREATE USER myname WITH PASSWORD ‘mypassword’; CREATE DATABASE taskdb WITH OWNER= myname LC_COLLATE='en_US.utf8' LC_CTYPE='en_US.utf8' ENCODING='UTF8' TEMPLATE=template0; CREATE DATABASE projectdb WITH OWNER= myname LC_COLLATE='en_US.utf8' LC_CTYPE='en_US.utf8' ENCODING='UTF8' TEMPLATE=template0; CREATE DATABASE resultdb WITH OWNER= myname LC_COLLATE='en_US.utf8' LC_CTYPE='en_US.utf8' ENCODING='UTF8' TEMPLATE=template0;
Docker Compose
除了数据库和消息队列,其他组件都用 docker-compose 来管理了,
docker-compose.yml 的内容主要参考这个 Running pyspider with Docker,和这个 Deployment of demo.pyspider.org。
主要是让各个组件连接外部的数据库和消息队列,限制一下内存占用,另外设置一个 WebUI 的用户名和密码。Result worker 要使用之前 build 的 image,因为它需要用到 LeanCloud。
然后 docker-compose up -d 就可以启动各组件了。
phantomjs: image: binux/pyspider:latest command: phantomjs mem_limit: 256m restart: always result: image: my/pyspider external_links: - postgres - redis volumes: - ./share:/opt/share working_dir: /opt/share command: '--taskdb "sqlalchemy+postgresql+taskdb://username:password@postgres/taskdb" --projectdb "sqlalchemy+postgresql+projectdb://username:password@postgres/projectdb" --resultdb "sqlalchemy+postgresql+resultdb://username:password@postgres/resultdb" --message-queue "redis://redis:6379/1" result_worker --result-cls=resultWorker.VResultWorker' mem_limit: 128m restart: always processor: image: binux/pyspider:latest external_links: - postgres - redis command: '--projectdb "sqlalchemy+postgresql+projectdb://username:password@postgres/projectdb" --message-queue "redis://redis:6379/1" processor' mem_limit: 128m restart: always fetcher: image: binux/pyspider:latest external_links: - redis links: - phantomjs command : '--message-queue "redis://redis:6379/1" --phantomjs-proxy "phantomjs:80" fetcher --xmlrpc' mem_limit: 128m restart: always scheduler: image: binux/pyspider:latest external_links: - postgres - redis command: '--taskdb "sqlalchemy+postgresql+taskdb://username:password@postgres/taskdb" --projectdb "sqlalchemy+postgresql+projectdb://username:password@postgres/projectdb" --resultdb "sqlalchemy+postgresql+resultdb://username:password@postgres/resultdb" --message-queue "redis://redis:6379/1" scheduler' restart: always webui: image: binux/pyspider:latest external_links: - postgres - redis links: - scheduler - phantomjs command: '--taskdb "sqlalchemy+postgresql+taskdb://username:password@postgres/taskdb" --projectdb "sqlalchemy+postgresql+projectdb://username:password@postgres/projectdb" --resultdb "sqlalchemy+postgresql+resultdb://username:password@postgres/resultdb" --message-queue "redis://redis:6379/1" webui --need-auth --username ui_username --password ui_password' ports: - "5000:5000"
ResultWorker
关于 result worker,官方文档并没有给出 demo,我这里贴一下我的 result worker。首先初始化 LeanCloud SDK,每次抓到数据的时候,去 LeanCloud 查询是不是已经存在同名的动漫(一个动漫可以有多个别名)。如果不存在,就创建这个别名,如果已经存在,就继续执行其他的逻辑(简洁起见,不全部贴出了)。
resultWorker.py 放在和 docker-compose.yml 相同的位置,在启动 result worker 的时候已经通过选项指定自定义的 VResultWorker
#!/usr/bin/env python # -*- encoding: utf-8 -*- # vim: set et sw=4 ts=4 sts=4 ff=unix fenc=utf8: # Author: suosuopuo<[email protected]> # # Created on 2017-06-15 15:37:46 from pyspider.result import ResultWorker import leancloud from datetime import datetime appKey = '' appID = '' class VResultWorker(ResultWorker): def __init__(self, resultdb, inqueue): self.resultdb = resultdb self.inqueue = inqueue self._quit = False leancloud.init(appID, appKey) leancloud.use_region('CN') def on_result(self, task, result): if result['type'] == 'anime-update': self.handle_anime_update(task, result) def handle_anime_update(self, task, result): title = result['title'] try: alias_query = leancloud.Query('Alias') alias_query.include('anime') alias_query.equal_to('title', title) alias = alias_query.first() except leancloud.LeanCloudError as e: if e.code == 101: Alias = leancloud.Object.extend('Alias') Anime = leancloud.Object.extend('Anime') alias = Alias() alias.set('title', title) anime = Anime() anime.set('title', title) anime.set('area', result['area']) anime.set('cover', result['cover']) anime.set('weekday', result['weekday']) anime.set('ep_num', 12) anime.set('is_finished', result['is_finished']) anime.set('verified', False) anime.save(fetch_when_save = True) alias.set('targetAnime', anime) alias.save() return ...
还有话说
我是很喜欢这样的「全家桶」式的方案的,可以快速地试验一些想法,但是很遗憾 PySpider 的代码已经很久没有更新了。好在目前为止 PySpider 还足够强大。
我没有解释每一条命令、每一个选项,因为这不是 PySpider 的文档,如果需要更详细的解释,还是需要去挖掘官方文档。
写这篇的时间距离写这个 side project 已经隔了几个月。我也懒到没有去再次验证各个配置和命令,所以「仅供参考」。
另外提醒一下,如果有人也打算用 LeanCloud 做后端,需要注意 LeanCloud 是有访问次数限制的。可以根据抓取的页面特点,用 age, itag等功能减少对 LeanCloud 的访问次数。
不过就算没有访问次数限制,对一个远程服务高频访问,也不是好的策略。
原文出处:shellpanic -> http://shellpanic.com/2017/10/23/deploy-pyspider-with-docker-compose/