pyspider 快速上手与架构解析:用 Python 编写脚本驱动的高性能爬虫系统
2026/9/21 16:21:15 网站建设 项目流程

pyspider 快速上手与架构解析:用 Python 编写脚本驱动的高性能爬虫系统

【免费下载链接】pyspiderA Powerful Spider(Web Crawler) System in Python.项目地址: https://gitcode.com/gh_mirrors/py/pyspider

pyspider 是一个用 Python 编写的强大爬虫(Web Crawler)系统,它通过消息队列把调度、抓取、解析与结果存储解耦为可独立扩展的组件,并自带集成了脚本编辑器、任务监控、项目管理与结果查看的 WebUI。阅读本文后,你将掌握 pyspider 的安装启动方式、基于BaseHandler编写爬虫脚本的核心 API 用法,以及其分布式架构与存储/消息队列选型背后的设计原理。

项目概览:pyspider 是什么

pyspider 定位为 "A Powerful Spider(Web Crawler) System in Python",是一套完整的爬虫解决方案,而非单纯的抓取库。它在单一进程内串联了调度(Scheduler)、抓取(Fetcher)、解析(Processor)、结果落地(Result Worker)与可视化控制台(WebUI)等组件,让用户可以用极少的代码管理成百上千个站点的采集任务。

从仓库根目录的 README.md 可以看到,pyspider 的核心卖点包括:

  • 用 Python 编写脚本:解析逻辑是普通的 Python 代码,配合 PyQuery 等库即可完成页面提取;
  • 强大的 WebUI:内置脚本编辑器、任务监控、项目管理与结果查看器,甚至支持逐步调试脚本;
  • 多样化的存储后端:MySQL、MongoDB、Redis、SQLite、Elasticsearch,以及通过 SQLAlchemy 接入的 PostgreSQL;
  • 多样化的消息队列:RabbitMQ、Redis 与 Kombu,也支持内置的进程内队列;
  • 丰富的任务语义:任务优先级(priority)、失败重试(retry)、定时周期任务(periodical)、按过期时间重爬(recrawl by age)等;
  • 分布式与动态渲染:组件间通过消息队列解耦、可多实例横向扩展,并支持抓取 JavaScript 渲染的页面。

当前仓库中的版本号为0.4.0(见 pyspider/init.py),入口命令由 pyspider/run.py 提供。

快速安装与启动

README 给出的安装与启动方式极其简单:

pip install pyspider pyspider

执行pyspider命令后,所有组件会以默认配置在本地启动,随后访问http://localhost:5000/即可进入 WebUI。

安全警告:务必配置访问认证

这是 README 中特别强调的一点,也是实际部署中最容易踩的坑:

WARNING:WebUI is open to the public by default, it can be used to execute any command which may harm your system. Please use it in an internal network or [enableneed-authfor webui].

也就是说,WebUI 默认是公开可访问的,它不仅能管理任务,还能在服务器上执行任意命令,可能危害你的系统。因此要么只在内网使用,要么显式开启认证。开启方式是在启动参数或配置文件中设置--need-auth并配合--username/--password,仓库根目录的 config_example.json 给出了完整示例:

{ "taskdb": "couchdb+taskdb://user:password@couchdb:5984", "projectdb": "couchdb+projectdb://user:password@couchdb:5984", "resultdb": "couchdb+resultdb://user:password@couchdb:5984", "message_queue": "amqp://rabbitmq:5672/%2F", "webui": { "username": "username", "password": "password", "need-auth": true, "scheduler-rpc": "http://scheduler:23333", "fetcher-rpc": "http://fetcher:24444" } }

配置文件通过-c/--config传入(pyspider -c config.json),其内容以 JSON 形式为各子命令提供默认值。认证逻辑可以在 pyspider/webui/login.py(app.config.get('need_auth', False))与 pyspider/webui/webdav.py 中看到实际生效点。

各组件与all模式

不带子命令直接运行pyspider时,等价于执行pyspider all——把所有组件以子进程(Windows 上为线程)的方式一次性拉起(见 pyspider/run.py 中all命令的实现)。其中fetcher_numprocessor_numresult_worker_num分别控制抓取器、处理器与结果工作者的实例数量,默认均为 1:

pyspider all --fetcher-num 2 --processor-num 4 --result-worker-num 1

后续的 命令行参考 一节会给出各子命令的完整参数说明。

用 BaseHandler 编写你的第一个爬虫

README 给出了一个可直接运行的示例脚本,它涵盖了 pyspider 脚本的核心骨架:定时入口、页面解析、链接提取与结果返回。

from pyspider.libs.base_handler import * class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('http://scrapy.org/', callback=self.index_page) @config(age=10 * 24 * 60 * 60) def index_page(self, response): for each in response.doc('a[href^="http"]').items(): self.crawl(each.attr.href, callback=self.detail_page) def detail_page(self, response): return { "url": response.url, "title": response.doc('title').text(), }

下面逐段拆解这段脚本的语义,并结合 pyspider/libs/base_handler.py 的源码说明其底层机制。

入口方法on_start@every

每个项目的起点是名为on_start的回调。在 WebUI 中点击项目的Run按钮时,系统会生成一个on_start任务交给调度器,作为项目任务流的入口。

@every(minutes=24 * 60)装饰器表示让该方法周期性执行——这里是每 24 小时触发一次,非常适合作为入口不断刷新首页、发现新链接。从源码看,every装饰器会为函数打上is_cronjob=True标记并计算tick(统一换算成秒);BaseHandlerMeta元类则收集所有 cronjob,并求其间隔的最大公约数min_tick。调度器只需每min_tick秒下发一次_on_cronjob任务,再在处理器侧按各函数的tick判断是否真正执行,从而显著减少定时任务的数量(对应实现见 base_handler.py 中everyBaseHandlerMeta的注释)。

提取链接与response.doc

response.doc(...)返回的是一个 PyQuery 对象(底层是 lxml),因此你可以用 CSS 选择器选取元素。示例中a[href^="http"]选取所有以http开头的超链接,each.attr.href取出链接地址,再通过self.crawl(each.attr.href, callback=self.detail_page)生成新的抓取任务——这是典型的"广度优先"爬取模式:由入口页发现列表页,再由列表页发现详情页。

@config与按年龄重爬

@config(age=10 * 24 * 60 * 60)的含义是:该回调产出的新任务在10 天后会被视为过期,需要重新抓取。从源码看,config装饰器把配置写入函数的_config属性,_crawl在为任务组装schedule字段时会读取它。实际上,BaseHandler把任务参数划分成了三组(见 base_handler.py 中的schedule_fieldsfetch_fieldsprocess_fields):

  • 调度相关priority(优先级)、retries(重试次数)、exetime(定时执行时间)、age(过期重爬)、itagauto_recrawlcancel等;
  • 抓取相关methodheadersuser_agentdatatimeoutallow_redirectscookiesproxyetaglast_modifiedsave,以及js_run_atjs_scriptload_images等 JavaScript 渲染参数;
  • 处理相关callbackprocess_time_limit

每个任务还会以md5(url)生成全局唯一的taskid(对应get_taskid方法),调度器据此判断任务是全新的、需要重爬还是可以跳过。

返回结果与on_result

detail_page返回一个字典,处理器会把它交给on_result回调(见 base_handler.py 的on_result实现):若当前不在调试器环境且配置了result_queue,结果会被放入结果队列,由 Result Worker 写入resultdb。也就是说,回调函数的返回值就是一条"结果",pyspider 负责完成从解析到入库的整条链路;你还可以覆写on_result来对接自己的业务系统。

分布式架构:消息队列连接的六大组件

README 强调 pyspider 采用"分布式架构",其本质在于:所有组件通过消息队列相互连接,每个组件(包括消息队列本身)都运行在独立的进程/线程中,并且是可替换的。这意味着当解析成为瓶颈时,你可以启动多个 Processor 实例充分利用多核 CPU,甚至把组件部署到多台机器上。完整的组件职责与数据流设计可以进一步阅读 docs/Architecture.md,下图是其架构总览:

各组件职责如下:

组件职责
Schedulernewtask_queue接收新任务,判定是新任务还是需要重爬;按优先级排序并通过令牌桶(token bucket)算法做流量控制后交给 Fetcher;负责定时任务、丢失任务与失败任务的延迟重试。注意:当前实现只允许一个 Scheduler 实例。
Fetcher负责实际抓取网页并送回 Processor;支持 Data URI 与 JavaScript 渲染页面(通过 phantomjs),抓取方法、headers、cookies、proxy、etag 等均可由脚本控制。
Phantomjs Fetcher以代理形式工作,接入通用 Fetcher,负责渲染启用 JavaScript 的页面后输出普通 HTML 回传给 Fetcher。
Processor运行用户编写的解析脚本,捕获异常与日志,向 Scheduler 回传任务状态(track)与新任务,并把结果发给 Result Worker。
Result Worker(可选)从 Processor 接收结果并写入resultdb;内置实现可直接使用,也支持覆写以满足自定义落地需求。
WebUI一切的可视化前端:脚本编辑器与调试器、项目管理、任务监控、结果查看与导出。

典型的数据流是:

  1. 在 WebUI 点击Run,向 Scheduler 提交on_start任务作为项目入口;
  2. Scheduler 把on_start任务以 Data URI 的形式分发给 Fetcher(对 Data URI 会构造一个假的请求/响应,但流程与普通任务无异);
  3. Fetcher 发出请求得到响应,交给 Processor;
  4. Processor 调用on_start,产生一批新 URL,通过消息队列把"任务完成"状态与新任务发回 Scheduler;
  5. Scheduler 查库判定新任务是否需要抓取,按序调度;
  6. 循环往复,直到所有任务完成;Scheduler 还会周期性检查定时任务以持续抓取最新数据。

从 pyspider/run.py 可以看到消息队列的真实连接细节:系统维护newtask_queuestatus_queuescheduler2fetcherfetcher2processorprocessor2result五条队列,默认使用内置的multiprocessing.Queue;一旦指定了--message-queue,则全部替换为外部队列实现。

存储后端与消息队列选型

pyspider 将数据分为三类数据库,分别对应三条独立的连接 URL:

  • taskdb:任务状态库;
  • projectdb:项目(脚本)配置库;
  • resultdb:结果库。

默认情况下(未指定任何 URL),三者都会以 SQLite 形式落在--data-path(默认./data)目录下。若想接入其他后端,参考 docs/Command-Line.md 中的 URL 格式:

mysql: mysql+type://user:passwd@host:port/database sqlite: sqlite+type:///path/to/database.db # 相对路径 sqlite+type:////path/to/database.db # 绝对路径 sqlite+type:// # 内存数据库 mongodb: mongodb+type://[username:password@]host1[:port1][,...] couchdb: couchdb+type://[username:password@]host[:port] sqlalchemy: sqlalchemy+postgresql+type://user:passwd@host:port/database local: local+projectdb://filepath,filepath

其中type必须是taskdbprojectdbresultdb三者之一。对应实现分散在 pyspider/database 目录下,支持 mysql、mongodb、redis、sqlite、elasticsearch、couchdb、sqlalchemy 与 local 等子包。

消息队列的 URL 格式同样重要:

rabbitmq: amqp://username:password@host:5672/%2F redis: redis://host:6379/db (Redis 3.x 集群模式可用逗号分隔多节点) kombu: kombu+transport://userid:password@hostname:port/virtual_host builtin: (默认,进程内队列)

对应实现见 pyspider/message_queue 目录下的kombu_queue.pyrabbitmq.pyredis_queue.py。依赖清单见仓库根目录的 requirements.txt 与 setup.py(extras_require['all']汇总了各存储/队列的可选依赖)。

此外,pyspider/run.py 中还兼容了 Docker 部署环境变量:当检测到MYSQL_NAMEMONGODB_NAMECOUCHDB_NAMERABBITMQ_NAME等环境变量时,会自动从*_PORT_*_TCP_ADDR形式的地址拼接数据库或队列连接串——这也是 docker-compose.yaml 与 Dockerfile 能够开箱即用的原因。

任务能力:优先级、重试与按龄重爬

README 提到的 "Task priority, retry, periodical, recrawl by age" 等能力全部通过self.crawl的调度参数暴露,即前文schedule_fields对应的字段:

  • priority:任务优先级,Scheduler 按优先级排序出队;
  • retries:失败重试次数;
  • exetime:定时执行时间(Unix 时间戳),用于"推迟到某个时刻再抓";
  • age:任务有效期,超过该秒数即视为过期并重新抓取,这是保持数据新鲜度的关键;
  • itag:内容标记,用于按内容判断是否需要重抓;
  • auto_recrawl:是否自动重爬;
  • cancel:取消任务。

定时周期任务则由@every装饰器与 Scheduler 的_on_cronjob机制配合实现(详见 pyspider/scheduler/scheduler.py 与 base_handler.py 中的_on_cronjob)。更完整的任务状态机说明可以阅读 docs/About-Tasks.md。

渲染 JavaScript 页面

针对依赖 JavaScript 动态渲染的站点,pyspider 提供了两种无头浏览器接入方式:

  • phantomjs:通过pyspider phantomjs启动独立的 phantomjs 代理服务(默认端口 25555),再用--phantomjs-proxy接入;all模式默认会自动尝试拉起;
  • puppeteer:通过pyspider puppeteer启动(默认端口 22222),脚本位于 pyspider/fetcher/puppeteer_fetcher.js;
  • 另有splash接入(--splash-endpoint),脚本见 pyspider/fetcher/splash_fetcher.lua。

脚本侧通过fetch_typejs_scriptjs_run_atjs_viewport_width/heightload_images等抓取参数控制渲染行为。入门教程可以参考 docs/tutorial/Render-with-PhantomJS.md,更多抓取参数见 docs/apis/self.crawl.md。

命令行参考

所有命令的帮助都可以通过pyspider --helppyspider <子命令> --help获取。全局选项适用于所有子命令(以下为 docs/Command-Line.md 与 pyspider/run.py 中整理出的完整列表):

Usage: pyspider [OPTIONS] COMMAND [ARGS]... A powerful spider system in python. Options: -c, --config FILENAME a json file with default values for subcommands. --logging-config TEXT logging config file for built-in python logging module --debug debug mode --queue-maxsize INTEGER maxsize of queue(0 表示不限制) --taskdb TEXT database url for taskdb, default: sqlite --projectdb TEXT database url for projectdb, default: sqlite --resultdb TEXT database url for resultdb, default: sqlite --message-queue TEXT connection url to message queue, default: builtin multiprocessing.Queue --amqp-url TEXT [deprecated] amqp url for rabbitmq,请改用 --message-queue --beanstalk TEXT [deprecated] beanstalk 配置,请改用 --message-queue --phantomjs-proxy TEXT phantomjs proxy ip:port --puppeteer-proxy TEXT puppeteer proxy ip:port --data-path TEXT data dir path(SQLite 数据库与 counter dump 文件保存路径) --version Show the version and exit.

各子命令要点如下:

  • pyspider all:以子进程/线程方式运行全部组件,可选--fetcher-num--processor-num--result-worker-num--run-in [subprocess|thread](Windows 上始终用线程);
  • pyspider one [SCRIPTS]...:单进程调试模式,所有组件跑在同一个进程的 tornado.ioloop 上,此模式不启动 WebUI;结果默认输出到 stdout(可用pyspider one > result.txt重定向);脚本路径可直接作为参数传入,此时项目状态为 RUNNING,可通过脚本注释# rate: 1.0# burst: 3设置抓取速率;-i/--interactive开启交互控制台,提供crawl(url, project=None, **kwargs)quit_interactive()quit_pyspider()等命令;
  • pyspider bench:基准测试模式,使用内存 SQLite 数据库替代磁盘数据库,可选--total--show等参数;
  • pyspider scheduler:仅运行调度器(只允许一个实例),可选--inqueue-limit(每个项目任务队列大小上限,溢出忽略)、--delete-time--active-tasks--loop-limit--fail-pause-num(连续失败 N 个任务后自动暂停项目,0 表示禁用)、--scheduler-cls等;
  • pyspider fetcher:仅运行抓取器,可选--poolsize(最大并发抓取数,默认 100)、--proxy--user-agent--timeout--phantomjs-endpoint--puppeteer-endpoint--splash-endpoint--fetcher-cls等;
  • pyspider processor:仅运行处理器,可选--process-time-limit(脚本处理时间上限,默认 30 秒)、--processor-cls
  • pyspider result_worker:仅运行结果工作者,可选--result-cls
  • pyspider webui:仅运行 WebUI,可选--host(默认 0.0.0.0)、--port(默认 5000)、--cdn(JS/CSS CDN 服务,需兼容 cdnjs)、--scheduler-rpc--fetcher-rpc--max-rate/--max-burst--username/--password--need-auth--webui-instance等;
  • pyspider phantomjs/pyspider puppeteer:启动无头浏览器抓取服务,支持--auto-restart崩溃自重启。

全局选项也支持通过环境变量注入,例如TASKDBPROJECTDBRESULTDBAMQP_URLWEBUI_HOSTWEBUI_PORTDEBUG等(见 pyspider/run.py 中各个envvar声明)。

快速开始与延伸阅读

  • 官方教程入口:docs/tutorial/index.md(HTML/CSS 选择器、AJAX 与 HTTP 进阶、PhantomJS 渲染等);
  • 快速上手:docs/Quickstart.md;
  • 项目与任务模型:docs/About-Projects.md、docs/About-Tasks.md;
  • API 参考:docs/apis/index.md(self.crawlself.send_messageResponse等);
  • 部署方案:docs/Deployment.md、docs/Deployment-demo.pyspider.org.md、docs/Running-pyspider-with-Docker.md(仓库根目录同时提供了 Dockerfile 与 docker-compose.yaml);
  • 命令行详解:docs/Command-Line.md。

贡献与 License

README 建议的参与方式包括:实际使用它、在 Issue 中反馈问题并提交 PR、加入用户组参与讨论。当前版本(v0.4.0)的 TODO 中列出的方向是可视化抓取界面(类似 portia)。项目采用 Apache License, Version 2.0 开源协议(见 LICENSE),仓库内还附带 tox.ini 与 tests 目录下的完整测试套件,便于开发者理解各模块行为并贡献代码。

【免费下载链接】pyspiderA Powerful Spider(Web Crawler) System in Python.项目地址: https://gitcode.com/gh_mirrors/py/pyspider

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询