用 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/

本站所发布的一切资源仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如果侵犯你的利益,请发送邮箱到 [email protected],我们会很快的为您处理。