Scrapy+Scrapyd+Gerapy:打造高效爬虫调度与管理框架
第1章:Scrapy 爬虫框架基础与核心机制详解
1.1 什么是 Scrapy?
Scrapy 是一个开源的 Python 爬虫框架,用于从网站抓取数据,并可自动处理请求、提取、清洗和存储流程。它以异步事件驱动为核心,具备高性能、模块化、易扩展的特点。
✅ Scrapy 的核心优势
- 异步非阻塞架构:基于 Twisted 网络库
- 可扩展中间件机制:支持请求、响应、异常等各类钩子
- 强大的选择器系统:XPath、CSS、正则混合使用
- 支持分布式和断点续爬
- 天然支持 Pipeline、Item 结构化存储
1.2 Scrapy 项目结构详解
一个 Scrapy 项目初始化结构如下:
$ scrapy startproject mycrawler
mycrawler/
├── mycrawler/ # 项目本体
│ ├── __init__.py
│ ├── items.py # 定义数据结构
│ ├── middlewares.py # 中间件处理
│ ├── pipelines.py # 数据处理
│ ├── settings.py # 配置文件
│ └── spiders/ # 爬虫脚本
│ └── example_spider.py
└── scrapy.cfg # 项目配置入口
1.3 Scrapy 的核心执行流程
Scrapy 的执行流程如下图所示:
flowchart TD
start(开始爬取) --> engine[Scrapy引擎]
engine --> scheduler[调度器 Scheduler]
scheduler --> downloader[下载器 Downloader]
downloader --> middleware[下载中间件]
middleware --> response[响应 Response]
response --> spider[爬虫 Spider]
spider --> item[Item 或 Request]
item --> pipeline[Pipeline 处理]
pipeline --> store[存储存入 DB/CSV/ES]
spider --> engine
🔁 说明:
- Engine 控制整个流程的数据流与调度;
- Scheduler 实现任务排队去重;
- Downloader 发出 HTTP 请求;
- Spider 处理响应,提取数据或发起新的请求;
- Pipeline 将数据持久化保存;
- Middlewares 拦截每个阶段,可插拔增强功能。
1.4 一个最简单的 Scrapy Spider 示例
# spiders/example_spider.py
import scrapy
class ExampleSpider(scrapy.Spider):
name = "example"
start_urls = ['https://quotes.toscrape.com']
def parse(self, response):
for quote in response.css('.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('.author::text').get()
}
next_page = response.css('li.next a::attr(href)').get()
if next_page:
yield response.follow(next_page, callback=self.parse)
✅ 输出结果(JSON):
{
"text": "The world as we have created it is a process of our thinking.",
"author": "Albert Einstein"
}
1.5 核心组件详解
组件 | 功能 | 说明 |
---|---|---|
Spider | 编写解析逻辑 | parse() 为主入口 |
Item | 数据结构 | 类似数据模型 |
Pipeline | 存储处理逻辑 | 可入库、清洗、格式化 |
Downloader | 请求下载 | 支持重试、UA、代理 |
Middleware | 请求/响应钩子 | 插件式增强能力 |
Scheduler | 排队与去重 | 支持断点续爬 |
Engine | 控制核心流程 | 所有组件的桥梁 |
1.6 Request 与 Response 深度解析
yield scrapy.Request(
url='https://example.com/page',
callback=self.parse_page,
headers={'User-Agent': 'CustomAgent'},
meta={'retry': 3}
)
meta
字典可在请求中传递信息至下个响应;dont_filter=True
表示不过滤重复请求。
1.7 XPath 与 CSS 选择器实战
# CSS 选择器
response.css('div.quote span.text::text').get()
# XPath
response.xpath('//div[@class="quote"]/span[@class="text"]/text()').get()
.get()
返回第一个结果;.getall()
返回列表。
1.8 项目配置 settings.py 常用参数
BOT_NAME = 'mycrawler'
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 1
CONCURRENT_REQUESTS = 16
COOKIES_ENABLED = False
RETRY_ENABLED = True
- 延迟访问:防止被封;
- 关闭 Cookie:绕过某些反爬策略;
- 并发控制:保证性能与安全。
1.9 数据持久化示例:Pipeline 到 CSV/MySQL/MongoDB
# pipelines.py
import csv
class CsvPipeline:
def open_spider(self, spider):
self.file = open('quotes.csv', 'w', newline='')
self.writer = csv.writer(self.file)
self.writer.writerow(['text', 'author'])
def process_item(self, item, spider):
self.writer.writerow([item['text'], item['author']])
return item
def close_spider(self, spider):
self.file.close()
1.10 调试技巧与日志配置
scrapy shell "https://quotes.toscrape.com"
# settings.py
LOG_LEVEL = 'DEBUG'
LOG_FILE = 'scrapy.log'
通过 shell 调试 XPath/CSS 表达式,可视化测试爬虫提取路径。
好的,以下是第2章:Scrapyd 服务化部署原理与实战的完整内容,已包含配置说明、API 示例、流程讲解和部署实战,直接复制即可使用:
第2章:Scrapyd 服务化部署原理与实战
2.1 什么是 Scrapyd?
Scrapyd 是一个专为 Scrapy 设计的爬虫部署服务,允许你将 Scrapy 爬虫“服务化”,并通过 HTTP API 实现远程启动、停止、部署和监控爬虫任务。
Scrapyd 核心作用是:将 Scrapy 脚本变为网络服务接口可以调度的“作业任务”,支持命令行或 Web 调度。
✅ Scrapyd 的主要能力包括:
- 后台守护运行爬虫;
- 支持多个项目的爬虫版本管理;
- 提供完整的 HTTP 调度 API;
- 输出日志、查看任务状态、取消任务;
- 与 Gerapy、CI/CD 系统(如 Jenkins)无缝集成。
2.2 安装与快速启动
安装 Scrapyd
pip install scrapyd
启动 Scrapyd 服务
scrapyd
默认监听地址是 http://127.0.0.1:6800
。
2.3 Scrapyd 配置文件详解
默认配置路径:
- Linux/macOS:
~/.scrapyd/scrapyd.conf
- Windows:
%APPDATA%\scrapyd\scrapyd.conf
示例配置文件内容:
[scrapyd]
bind_address = 0.0.0.0 # 允许外部访问
http_port = 6800
max_proc = 10 # 最大并发爬虫数量
poll_interval = 5.0
logs_dir = logs
eggs_dir = eggs
dbs_dir = dbs
你可以手动创建这个文件并重启 Scrapyd。
2.4 创建 setup.py 以支持打包部署
Scrapyd 需要项目打包为 .egg
文件。首先在项目根目录创建 setup.py
文件:
from setuptools import setup, find_packages
setup(
name='mycrawler',
version='1.0',
packages=find_packages(),
entry_points={'scrapy': ['settings = mycrawler.settings']},
)
然后执行:
python setup.py bdist_egg
会在 dist/
目录生成 .egg
文件,例如:
dist/
└── mycrawler-1.0-py3.10.egg
2.5 上传项目到 Scrapyd
通过 API 上传:
curl http://localhost:6800/addversion.json \
-F project=mycrawler \
-F version=1.0 \
-F egg=@dist/mycrawler-1.0-py3.10.egg
上传成功返回示例:
{
"status": "ok",
"spiders": 3
}
2.6 启动爬虫任务
调用 API 启动任务:
curl http://localhost:6800/schedule.json \
-d project=mycrawler \
-d spider=example
Python 调用:
import requests
resp = requests.post("http://localhost:6800/schedule.json", data={
"project": "mycrawler",
"spider": "example"
})
print(resp.json())
返回:
{"status": "ok", "jobid": "abcde123456"}
2.7 查询任务状态
Scrapyd 提供三个任务队列:
pending
:等待中running
:执行中finished
:已完成
查看所有任务状态:
curl http://localhost:6800/listjobs.json?project=mycrawler
返回结构:
{
"status": "ok",
"pending": [],
"running": [],
"finished": [
{
"id": "abc123",
"spider": "example",
"start_time": "2025-07-16 10:12:00",
"end_time": "2025-07-16 10:13:10"
}
]
}
2.8 停止任务
停止指定 job:
curl http://localhost:6800/cancel.json -d project=mycrawler -d job=abc123
2.9 查看可用爬虫、项目、版本
# 查看所有项目
curl http://localhost:6800/listprojects.json
# 查看项目的爬虫列表
curl http://localhost:6800/listspiders.json?project=mycrawler
# 查看项目的所有版本
curl http://localhost:6800/listversions.json?project=mycrawler
2.10 日志文件结构与查看方式
Scrapyd 默认日志路径为:
logs/
└── mycrawler/
└── example/
└── abc123456.log
查看日志:
tail -f logs/mycrawler/example/abc123456.log
也可以通过 Gerapy 提供的 Web UI 远程查看。
2.11 多节点部署与调度建议
在生产环境中,可以将 Scrapyd 安装在多台爬虫服务器上实现分布式调度。
部署建议:
- 多台机器相同配置(Python 环境、Scrapy 项目结构一致);
- 统一使用 Gerapy 作为调度平台;
- 项目统一使用 CI/CD 工具(如 Jenkins)上传 egg;
- 使用 Nginx 或其他服务网关统一管理多个 Scrapyd 节点;
- 日志通过 ELK 或 Loki 系统集中分析。
2.12 常见问题与解决方案
问题 | 说明 | 解决方案 |
---|---|---|
上传失败 | version 重复 | 升级版本号或删除旧版本 |
无法访问 | IP 被限制 | bind\_address 配置为 0.0.0.0 |
启动失败 | egg 配置错误 | 检查 entry_points 设置 |
运行失败 | 环境不一致 | 统一 Python 环境版本、依赖 |
第3章:Gerapy:可视化调度管理平台详解
3.1 Gerapy 是什么?
Gerapy 是由 Scrapy 官方衍生的开源项目,提供了一个 Web 管理面板,用于控制多个 Scrapyd 节点,实现爬虫任务可视化管理、项目上传、定时调度、日志查看等功能。
✅ Gerapy 的核心能力包括:
- 多节点 Scrapyd 管理(分布式支持);
- 爬虫项目在线上传、更新;
- 可视化任务调度器;
- 日志在线查看与状态监控;
- 多人协作支持。
3.2 安装与环境准备
1. 安装 Gerapy
pip install gerapy
建议安装在独立虚拟环境中,并确保 Python 版本在 3.7 以上。
2. 初始化 Gerapy 项目
gerapy init # 创建 gerapy 项目结构
cd gerapy
gerapy migrate # 初始化数据库
gerapy createsuperuser # 创建管理员账户
3. 启动 Gerapy 服务
gerapy runserver 0.0.0.0:8000
访问地址:
http://localhost:8000
3.3 项目结构介绍
gerapy/
├── projects/ # 本地 Scrapy 项目目录
├── db.sqlite3 # SQLite 存储
├── logs/ # 日志缓存
├── templates/ # Gerapy Web 模板
├── scrapyd_servers/ # 配置的 Scrapyd 节点
└── manage.py
3.4 添加 Scrapyd 节点
- 打开 Gerapy 页面(http://localhost:8000);
- 进入【节点管理】界面;
- 点击【添加节点】,填写信息:
字段 | 示例值 |
---|---|
名称 | 本地节点 |
地址 | http://127.0.0.1:6800 |
描述 | 本地测试 Scrapyd 服务 |
- 点击保存,即可自动测试连接。
3.5 上传 Scrapy 项目至 Scrapyd 节点
步骤:
- 将你的 Scrapy 项目放入
gerapy/projects/
目录; - 在【项目管理】页面点击【上传】;
- 选择节点(支持多节点)和版本号;
- 自动打包
.egg
并上传至目标 Scrapyd。
打包构建日志示例:
[INFO] Packing project: quotes_spider
[INFO] Generated egg: dist/quotes_spider-1.0-py3.10.egg
[INFO] Uploading to http://127.0.0.1:6800/addversion.json
[INFO] Upload success!
3.6 任务调度与自动运行
点击【任务调度】模块:
- 创建任务(选择节点、爬虫、项目、调度周期);
- 支持 Cron 表达式,例如:
表达式 | 含义 |
---|---|
* * * * * | 每分钟执行一次 |
0 0 * * * | 每天 0 点执行 |
0 8 * * 1 | 每周一 8 点执行 |
可以设定参数、任务间隔、日志保存策略等。
3.7 在线日志查看
每个任务完成后,可直接在 Web 页面查看其对应日志,示例:
[INFO] Spider opened
[INFO] Crawled (200) <GET https://quotes.toscrape.com> ...
[INFO] Spider closed (finished)
点击日志详情可查看每一行详细输出,支持下载。
3.8 用户系统与权限管理
Gerapy 使用 Django 的 Auth 模块支持用户认证:
gerapy createsuperuser
也可以通过 Admin 页面创建多个用户、设定权限组,便于团队协作开发。
3.9 Gerapy 后台管理(Django Admin)
访问 http://localhost:8000/admin/
使用管理员账户登录,可对以下内容进行管理:
- 用户管理
- Scrapyd 节点
- 项目上传记录
- 调度任务表
- Cron 调度历史
3.10 高级特性与插件扩展
功能 | 实现方式 | 描述 |
---|---|---|
节点负载均衡 | 多节点轮询调度 | 节点状态可扩展监控指标 |
数据可视化 | 自定义报表模块 | 与 matplotlib/pyecharts 集成 |
日志采集 | 接入 ELK/Loki | 更强大的日志监控能力 |
自动构建部署 | GitLab CI/Jenkins | 支持自动化更新 Scrapy 项目并部署 |
3.11 Gerapy 与 Scrapyd 关系图解
graph TD
U[用户操作界面] --> G[Gerapy Web界面]
G --> S1[Scrapyd 节点 A]
G --> S2[Scrapyd 节点 B]
G --> Projects[本地 Scrapy 项目]
G --> Cron[定时任务调度器]
S1 --> Logs1[日志/状态]
S2 --> Logs2[日志/状态]
3.12 常见问题处理
问题 | 原因 | 解决方案 |
---|---|---|
上传失败 | egg 打包错误 | 检查 setup.py 配置与版本 |
节点连接失败 | IP 被防火墙阻止 | 修改 Scrapyd 配置为 0.0.0.0 |
爬虫未显示 | 项目未上传成功 | 确保项目可运行并打包正确 |
日志无法查看 | 目录权限不足 | 检查 logs 目录权限并重启服务 |
第4章:项目结构设计:从模块划分到任务封装
4.1 为什么要重构项目结构?
Scrapy 默认生成的项目结构非常基础,适合快速开发单个爬虫,但在实际业务中通常存在以下问题:
- 多个爬虫文件之间高度重复;
- 无法共用下载中间件或通用处理逻辑;
- Pipeline、Item、Spider 无法复用;
- 调度逻辑零散,不易维护;
- 缺乏模块化与自动任务封装能力。
因此,我们需要一个更具层次化、组件化的架构。
4.2 推荐项目结构(模块化目录)
mycrawler/
├── mycrawler/ # 项目主目录
│ ├── __init__.py
│ ├── items/ # 所有 item 定义模块化
│ │ ├── __init__.py
│ │ └── quote_item.py
│ ├── pipelines/ # pipeline 分模块
│ │ ├── __init__.py
│ │ └── quote_pipeline.py
│ ├── middlewares/ # 通用中间件
│ │ ├── __init__.py
│ │ └── ua_rotate.py
│ ├── spiders/ # 各爬虫模块
│ │ ├── __init__.py
│ │ └── quote_spider.py
│ ├── utils/ # 公共工具函数
│ │ └── common.py
│ ├── commands/ # 自定义命令(封装入口)
│ │ └── run_task.py
│ ├── scheduler/ # 任务调度逻辑封装
│ │ └── task_manager.py
│ ├── settings.py # Scrapy 配置
│ └── main.py # 主启动入口(本地测试用)
├── scrapy.cfg
└── requirements.txt
这种结构有如下优势:
- 每一层关注单一职责;
- 逻辑复用更容易管理;
- 支持 CI/CD 和自动测试集成;
- 可以作为服务打包。
4.3 多爬虫设计与代码复用技巧
在 Spider 中实现通用基类:
# spiders/base_spider.py
import scrapy
class BaseSpider(scrapy.Spider):
custom_settings = {
'DOWNLOAD_DELAY': 1,
'CONCURRENT_REQUESTS': 8,
}
def log_info(self, message):
self.logger.info(f"[{self.name}] {message}")
继承该基类:
# spiders/quote_spider.py
from mycrawler.spiders.base_spider import BaseSpider
class QuoteSpider(BaseSpider):
name = 'quote'
start_urls = ['https://quotes.toscrape.com']
def parse(self, response):
for q in response.css('div.quote'):
yield {
'text': q.css('span.text::text').get(),
'author': q.css('.author::text').get()
}
4.4 Items 模块封装
统一管理所有 Item,便于维护与共享:
# items/quote_item.py
import scrapy
class QuoteItem(scrapy.Item):
text = scrapy.Field()
author = scrapy.Field()
4.5 Pipelines 分模块处理
模块化每类 pipeline,配置在 settings.py
中动态启用:
# pipelines/quote_pipeline.py
class QuotePipeline:
def process_item(self, item, spider):
item['text'] = item['text'].strip()
return item
配置使用:
ITEM_PIPELINES = {
'mycrawler.pipelines.quote_pipeline.QuotePipeline': 300,
}
4.6 通用中间件封装
通用代理、UA、异常处理:
# middlewares/ua_rotate.py
import random
class UARotateMiddleware:
USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64)',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
]
def process_request(self, request, spider):
request.headers['User-Agent'] = random.choice(self.USER_AGENTS)
配置启用:
DOWNLOADER_MIDDLEWARES = {
'mycrawler.middlewares.ua_rotate.UARotateMiddleware': 543,
}
4.7 utils:封装通用函数与解析器
# utils/common.py
from hashlib import md5
def generate_id(text):
return md5(text.encode('utf-8')).hexdigest()
在 Spider 或 Pipeline 中调用:
from mycrawler.utils.common import generate_id
4.8 调度模块:scheduler/task\_manager.py
集中封装所有爬虫任务的调度管理:
import requests
class TaskManager:
SCRAPYD_HOST = 'http://localhost:6800'
@staticmethod
def start_task(project, spider, version='default'):
url = f"{TaskManager.SCRAPYD_HOST}/schedule.json"
data = {'project': project, 'spider': spider}
return requests.post(url, data=data).json()
4.9 自定义命令入口(封装脚本执行)
# commands/run_task.py
from scrapy.commands import ScrapyCommand
from mycrawler.scheduler.task_manager import TaskManager
class Command(ScrapyCommand):
requires_project = True
def short_desc(self):
return "Run spider task by name"
def add_options(self, parser):
ScrapyCommand.add_options(self, parser)
parser.add_option("--spider", dest="spider")
def run(self, args, opts):
spider = opts.spider
if not spider:
self.exitcode = 1
self.stderr.write("Spider name is required")
else:
result = TaskManager.start_task("mycrawler", spider)
self.stdout.write(f"Task Result: {result}")
4.10 main.py:本地开发调试入口
# main.py
from scrapy.cmdline import execute
if __name__ == '__main__':
execute(['scrapy', 'crawl', 'quote'])
第5章:分布式爬虫部署:Docker + Scrapyd 多节点架构实战
5.1 为什么需要分布式爬虫?
在大型爬虫场景中,单台机器资源有限,且运行不稳定。因此,我们需要:
- 多节点部署提升并发吞吐;
- 弹性调度、自动容灾;
- 节点间分摊负载,减少爬虫 IP 被封风险;
- 与 Gerapy 联动统一管理。
5.2 Scrapyd 多节点部署原理图
graph TD
G[Gerapy UI 管理平台]
G --> N1[Scrapyd Node 1]
G --> N2[Scrapyd Node 2]
G --> N3[Scrapyd Node 3]
N1 -->|任务调度| Spider1
N2 -->|任务调度| Spider2
N3 -->|任务调度| Spider3
说明:
- Gerapy 控制多个 Scrapyd 实例;
- Scrapyd 通过 HTTP 接口接收指令;
- 每个 Scrapyd 节点可并发运行多个任务。
5.3 构建 Scrapyd 的 Docker 镜像
我们使用官方推荐方式制作 Scrapyd 镜像。
编写 Dockerfile:
FROM python:3.10-slim
RUN pip install --no-cache-dir scrapyd
EXPOSE 6800
CMD ["scrapyd"]
构建镜像:
docker build -t scrapyd-node:latest .
5.4 使用 Docker Compose 启动多个节点
创建 docker-compose.yml
文件:
version: '3'
services:
scrapyd1:
image: scrapyd-node:latest
ports:
- "6801:6800"
container_name: scrapyd-node-1
scrapyd2:
image: scrapyd-node:latest
ports:
- "6802:6800"
container_name: scrapyd-node-2
scrapyd3:
image: scrapyd-node:latest
ports:
- "6803:6800"
container_name: scrapyd-node-3
启动容器:
docker-compose up -d
三个节点地址分别为:
5.5 上传项目至多个 Scrapyd 节点
可以使用 Gerapy 或命令行依次上传:
curl http://localhost:6801/addversion.json -F project=mycrawler -F version=1.0 -F egg=@dist/mycrawler.egg
curl http://localhost:6802/addversion.json -F project=mycrawler -F version=1.0 -F egg=@dist/mycrawler.egg
curl http://localhost:6803/addversion.json -F project=mycrawler -F version=1.0 -F egg=@dist/mycrawler.egg
5.6 任务调度至不同节点
在 Gerapy 中添加多个节点:
名称 | 地址 |
---|---|
节点1 | http://localhost:6801 |
节点2 | http://localhost:6802 |
节点3 | http://localhost:6803 |
然后你可以手动或定时调度任务给不同 Scrapyd 节点。
5.7 日志统一采集方案(可选)
每个 Scrapyd 节点会产生日志文件,结构如下:
/logs
└── mycrawler/
└── spider1/
└── jobid123.log
统一日志的方式:
- 使用
docker volume
将日志挂载到宿主机; - 配置 Filebeat 采集日志 → 推送到 Logstash → Elasticsearch;
- 使用 Grafana / Kibana 实时查看爬虫运行状态。
5.8 部署架构图
graph TD
CI[CI/CD 构建服务] --> Upload[构建 egg 上传]
Upload --> S1[Scrapyd 6801]
Upload --> S2[Scrapyd 6802]
Upload --> S3[Scrapyd 6803]
Gerapy[Gerapy Web调度] --> S1
Gerapy --> S2
Gerapy --> S3
Logs[日志采集模块] --> ELK[(ELK / Loki)]
5.9 扩展方案:使用 Nginx 统一入口
为避免暴露多个端口,可通过 Nginx 路由:
server {
listen 80;
location /scrapyd1/ {
proxy_pass http://localhost:6801/;
}
location /scrapyd2/ {
proxy_pass http://localhost:6802/;
}
}
在 Gerapy 中填入统一的 Nginx 地址即可。
5.10 多节点调度策略建议
策略 | 说明 |
---|---|
轮询 | 按顺序分配给每个节点 |
随机 | 随机选择可用节点 |
权重 | 给不同节点设置执行优先级 |
压力感知调度 | 根据节点负载自动选择 |
Gerapy 默认是手动选择节点,也可二次开发支持智能调度。
第6章:Gerapy 自动调度任务系统原理与二次开发实践
6.1 Gerapy 的调度系统概览
Gerapy 使用 Django + APScheduler 构建定时任务系统:
- 任务创建:前端设置任务 → 写入数据库;
- 调度启动:后台定时器读取任务 → 调用 Scrapyd;
- 任务状态:通过 job\_id 追踪 → 获取日志、标记完成;
- 任务失败:默认不自动重试,需要扩展;
系统组件图:
graph TD
User[用户设置任务] --> Gerapy[Web UI]
Gerapy --> DB[任务数据库]
Gerapy --> APS[APScheduler 后台调度器]
APS --> Scrapyd[任务调度 Scrapyd]
Scrapyd --> JobLog[日志 & 状态返回]
6.2 数据库结构分析(SQLite)
Gerapy 使用 SQLite 存储任务信息,相关核心模型位于:
tasks.models.Task
tasks.models.Schedule
表结构核心字段:
字段 | 说明 |
---|---|
name | 任务名称 |
project | 项目名称(上传时指定) |
spider | 爬虫名称 |
node | Scrapyd 节点地址 |
cron | cron 表达式(调度周期) |
args | 传参 JSON 字符串 |
enabled | 是否启用该任务 |
last_run_time | 上次运行时间 |
6.3 创建定时任务的完整流程
1. 上传项目至节点
上传成功后才能被调度系统识别。
2. 在 Web UI 配置任务
填写如下字段:
- 项目名称(下拉选择)
- 爬虫名称(自动识别)
- cron 表达式(定时策略)
- 参数(如时间范围、城市名等)
3. 后台调度器启动任务
Gerapy 启动后,会开启一个 APScheduler 后台守护线程,读取任务表并解析 cron 表达式,自动调度任务:
from apscheduler.schedulers.background import BackgroundScheduler
6.4 调度源码分析
任务调度核心在:
gerapy/server/tasks/scheduler.py
def run_task(task):
url = task.node_url + "/schedule.json"
data = {
'project': task.project,
'spider': task.spider,
**task.args # 支持动态传参
}
requests.post(url, data=data)
支持动态参数扩展,建议在表中将 args 以 JSON 存储并转换为字典发送。
6.5 自定义重试逻辑(任务失败处理)
Scrapyd 默认不提供任务失败回调,Gerapy 原始实现也没有失败检测。我们可以手动添加失败处理逻辑。
步骤:
- 每次调用任务后记录 job\_id;
- 定时调用
/listjobs.json?project=xxx
获取状态; - 若任务超时/失败,可自动重试:
def check_and_retry(task):
job_id = task.last_job_id
status = get_job_status(job_id)
if status == 'failed':
run_task(task) # 重新调度
可以将任务状态持久化存入数据库,做失败告警通知。
6.6 实现多参数任务支持(带动态参数)
原始 Web 配置只支持静态参数:
我们可以修改前端任务配置表单,添加参数输入框,并将 JSON 转为字典:
{
"city": "shanghai",
"category": "news"
}
后端接收到后:
import json
args_dict = json.loads(task.args)
data = {
'project': task.project,
'spider': task.spider,
**args_dict
}
6.7 自定义任务运行监控界面
在 Gerapy 的管理后台添加任务状态查看:
- 展示任务执行时间、状态;
- 增加“运行日志查看按钮”;
- 增加任务失败次数统计;
- 可导出为 Excel 报表。
修改方式:
- 模板:
templates/tasks/index.html
- 后端:
tasks/views.py
6.8 与 Scrapyd 的调度通信优化建议
Scrapyd 无法主动回调任务状态,建议:
- 每隔 60 秒轮询
/listjobs.json
- 把状态写入本地数据库
也可以集成 Redis + Celery 实现任务链式调度:
@app.task
def monitor_job(job_id):
status = scrapyd_api.get_status(job_id)
if status == 'finished':
do_next_step()
elif status == 'failed':
retry_task(job_id)
6.9 图解:任务调度生命周期
sequenceDiagram
participant User
participant Gerapy
participant DB
participant APScheduler
participant Scrapyd
User->>Gerapy: 提交任务 + Cron
Gerapy->>DB: 写入任务数据
APScheduler->>DB: 周期性读取任务
APScheduler->>Scrapyd: 发起任务调度
Scrapyd-->>Gerapy: 返回 JobID
Gerapy->>DB: 记录状态
loop 每60秒
Gerapy->>Scrapyd: 查询任务状态
Scrapyd-->>Gerapy: 状态返回
Gerapy->>DB: 更新任务结果
end
6.10 Gerapy 二次开发扩展清单
扩展模块 | 功能描述 |
---|---|
任务失败自动重试 | 若任务失败,自动重调 |
参数模板支持 | 每种 Spider 有预设参数模板 |
任务依赖调度 | 支持“任务完成 → 触发下个任务” |
日志分析 | 统计抓取量、成功率、错误数 |
通知系统 | 邮件、钉钉、飞书推送失败通知 |
第7章:Gerapy + Jenkins 构建自动化爬虫发布与持续集成系统
7.1 为什么需要自动化发布?
在大型爬虫团队中,频繁的代码更新和项目部署是常态,手动上传、调度存在以下弊端:
- 易出错,流程繁琐;
- 发布不及时,影响数据时效;
- 无法保障多节点版本一致;
- 缺乏任务执行的自动反馈。
基于 Jenkins 的自动化 CI/CD 流程,结合 Gerapy 统一管理,实现“代码提交 → 自动构建 → 自动部署 → 自动调度”的闭环,极大提高效率和可靠性。
7.2 Jenkins 环境搭建与配置
1. 安装 Jenkins
官方提供多平台安装包,Docker 方式也很方便:
docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins:lts
2. 安装插件
- Git 插件(源码管理)
- Pipeline 插件(流水线)
- SSH 插件(远程命令)
- HTTP Request 插件(API 调用)
7.3 Git 代码管理规范
建议每个爬虫项目维护独立 Git 仓库,分支策略:
master/main
:稳定版dev
:开发版- Feature 分支:新功能开发
7.4 Jenkins Pipeline 脚本示例
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: 'master', url: 'git@github.com:username/mycrawler.git'
}
}
stage('Install Dependencies') {
steps {
sh 'pip install -r requirements.txt'
}
}
stage('Build Egg') {
steps {
sh 'python setup.py bdist_egg'
}
}
stage('Upload to Scrapyd') {
steps {
script {
def eggPath = "dist/mycrawler-1.0-py3.10.egg"
def response = httpRequest httpMode: 'POST',
url: 'http://scrapyd-server:6800/addversion.json',
multipartFormData: [
[name: 'project', contents: 'mycrawler'],
[name: 'version', contents: '1.0'],
[name: 'egg', file: eggPath]
]
echo "Upload Response: ${response.content}"
}
}
}
stage('Trigger Spider') {
steps {
httpRequest httpMode: 'POST', url: 'http://scrapyd-server:6800/schedule.json', body: 'project=mycrawler&spider=quote', contentType: 'APPLICATION_FORM'
}
}
}
post {
failure {
mail to: 'team@example.com',
subject: "Jenkins Build Failed: ${env.JOB_NAME}",
body: "Build failed. Please check Jenkins."
}
}
}
7.5 与 Gerapy 的结合
- Jenkins 只负责代码构建与上传;
- Gerapy 负责任务调度、状态管理与日志展示;
- 结合 Gerapy 提供的 API,可实现更加灵活的任务管理;
7.6 自动化部署流程图
graph LR
Git[Git Push] --> Jenkins
Jenkins --> Egg[构建 Egg]
Egg --> Upload[上传至 Scrapyd]
Upload --> Gerapy
Gerapy --> Schedule[调度任务]
Schedule --> Scrapyd
Scrapyd --> Logs[日志收集]
7.7 常见问题与排查
问题 | 可能原因 | 解决方案 |
---|---|---|
上传失败 | 版本号重复或权限不足 | 增加版本号,检查 Scrapyd 权限 |
任务启动失败 | 参数错误或节点未注册 | 检查参数,确认 Scrapyd 状态 |
Jenkins 执行超时 | 网络慢或命令卡住 | 调整超时,检查网络和依赖 |
邮件通知未发送 | 邮箱配置错误或 Jenkins 插件缺失 | 配置 SMTP,安装邮件插件 |
7.8 实战示例:多项目多节点自动发布
1. 在 Jenkins 中创建多项目流水线,分别对应不同爬虫;
2. 使用参数化构建,动态指定项目名称与版本号;
3. 脚本自动上传对应节点,保证多节点版本一致;
4. 调用 Gerapy API 自动创建调度任务并启用。
7.9 安全性建议
- Jenkins 访问限制 IP 白名单;
- Scrapyd 绑定内网地址,避免暴露公网;
- API 接口添加 Token 校验;
- 代码仓库权限管理。
第8章:Scrapy 项目性能调优与异步下载深度解析
8.1 Scrapy 异步架构简介
Scrapy 基于 Twisted 异步网络框架,实现高效的网络 I/O 处理。
关键特点:
- 非阻塞 I/O,避免线程切换开销;
- 单线程并发处理,降低资源消耗;
- 通过事件循环管理请求和响应。
8.2 Twisted 核心概念
- Reactor:事件循环核心,负责调度 I/O 事件;
- Deferred:异步结果占位符,回调机制实现链式操作;
- Protocol 和 Transport:网络通信协议和数据传输抽象。
8.3 Scrapy 下载流程
sequenceDiagram
participant Spider
participant Scheduler
participant Downloader
participant Reactor
Spider->>Scheduler: 发送请求Request
Scheduler->>Downloader: 获取请求
Downloader->>Reactor: 非阻塞发起请求
Reactor-->>Downloader: 请求完成,接收响应Response
Downloader->>Scheduler: 返回响应
Scheduler->>Spider: 分发Response给回调函数
8.4 关键性能影响点
影响因素 | 说明 |
---|---|
并发请求数 | CONCURRENT_REQUESTS 设置 |
下载延迟 | DOWNLOAD_DELAY 控制访问频率 |
下载超时 | DOWNLOAD_TIMEOUT 影响响应等待时长 |
DNS 解析 | DNS 缓存配置减少解析开销 |
中间件处理 | 自定义中间件效率影响整体性能 |
8.5 配置参数优化建议
# settings.py
CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 16
DOWNLOAD_DELAY = 0.25
DOWNLOAD_TIMEOUT = 15
REACTOR_THREADPOOL_MAXSIZE = 20
DNSCACHE_ENABLED = True
CONCURRENT_REQUESTS
控制全局并发数,适当调高提升吞吐;DOWNLOAD_DELAY
设置合理延迟,避免被封禁;REACTOR_THREADPOOL_MAXSIZE
控制线程池大小,影响 DNS 和文件 I/O。
8.6 异步下载中间件示例
编写下载中间件,实现异步请求拦截:
from twisted.internet.defer import Deferred
from twisted.web.client import Agent
class AsyncDownloaderMiddleware:
def process_request(self, request, spider):
d = Deferred()
agent = Agent(reactor)
agent.request(b'GET', request.url.encode('utf-8')).addCallback(self.handle_response, d)
return d
def handle_response(self, response, deferred):
# 处理响应,构建 Scrapy Response
scrapy_response = ...
deferred.callback(scrapy_response)
8.7 高性能爬虫案例分析
案例:大规模商品信息抓取
- 使用
CONCURRENT_REQUESTS=64
提升爬取速度; - 实现基于 Redis 的请求去重和分布式调度;
- 自定义下载中间件过滤无效请求;
- 结合异步数据库写入,减少阻塞。
8.8 CPU 与内存监控与调优
- 监控爬虫运行时 CPU、内存占用,排查内存泄漏;
- 优化 Item Pipeline,减少阻塞操作;
- 合理使用 Scrapy Signals 做性能统计。
8.9 避免常见性能陷阱
陷阱 | 说明 | 解决方案 |
---|---|---|
同步阻塞调用 | 阻塞数据库、文件写入 | 使用异步写入或线程池 |
过多下载延迟 | 误用高延迟导致吞吐降低 | 调整合理下载间隔 |
大量小任务导致调度开销 | 任务拆分不合理,调度压力大 | 合并任务,批量处理 |
DNS 解析瓶颈 | 每次请求都进行 DNS 解析 | 开启 DNS 缓存 |
8.10 图解:Scrapy 异步事件流
flowchart TD
Start[爬虫启动]
Start --> RequestQueue[请求队列]
RequestQueue --> Reactor[Twisted Reactor事件循环]
Reactor --> Downloader[异步下载器]
Downloader --> ResponseQueue[响应队列]
ResponseQueue --> Spider[爬虫解析]
Spider --> ItemPipeline[数据处理管道]
ItemPipeline --> Store[存储数据库]
Spider --> RequestQueue
第9章:Scrapy 多源异步分布式爬虫设计与实战
9.1 多源爬取的挑战与需求
现代业务中,往往需要同时抓取多个网站或接口数据,面临:
- 多数据源结构各异,解析复杂;
- 任务数量大,调度难度提升;
- 单机资源有限,需分布式部署;
- 实时性和容错要求高。
9.2 架构设计原则
- 模块化解析:针对不同数据源设计独立 Spider,复用基础组件;
- 异步调度:利用 Scrapy + Twisted 异步提高效率;
- 分布式调度:结合 Scrapyd 和 Gerapy 多节点管理;
- 去重与存储统一:采用 Redis 等中间件实现请求去重和缓存,统一存储。
9.3 多源爬虫架构图
graph TD
User[用户请求] --> Scheduler[调度系统]
Scheduler --> ScrapydNode1[Scrapyd节点1]
Scheduler --> ScrapydNode2[Scrapyd节点2]
ScrapydNode1 --> Spider1[Spider-数据源A]
ScrapydNode2 --> Spider2[Spider-数据源B]
Spider1 --> Redis[请求去重 & 缓存]
Spider2 --> Redis
Spider1 --> DB[数据存储]
Spider2 --> DB
9.4 Redis 实现请求去重与分布式队列
- 使用 Redis
set
实现请求 URL 去重,避免重复抓取; - 采用 Redis List 或 Stream 做任务队列,支持分布式消费;
- 结合
scrapy-redis
插件实现分布式调度。
9.5 scrapy-redis 集成示例
# settings.py
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
REDIS_URL = "redis://127.0.0.1:6379"
# spider.py
from scrapy_redis.spiders import RedisSpider
class MultiSourceSpider(RedisSpider):
name = 'multi_source'
redis_key = 'multi_source:start_urls'
def parse(self, response):
# 解析逻辑
pass
9.6 异步处理与请求批量调度
- 优化请求并发数,充分利用异步 I/O;
- 实现请求批量提交,减少调度延迟;
- 结合 Redis Stream 做消费记录,保障数据完整。
9.7 分布式爬虫运行监控方案
- 利用 Gerapy 监控各节点任务状态;
- 通过 ELK/Prometheus+Grafana 收集性能指标;
- 实时告警系统保证故障快速响应。
9.8 多源爬虫实战案例
业务需求:
采集电商平台 A、新闻网站 B、社交平台 C 的数据。
实现步骤:
- 分别为 A、B、C 创建独立 Spider;
- 在 Redis 中维护不同队列和去重集合;
- 通过 Scrapyd 多节点分布部署,利用 Gerapy 统一调度;
- 监控日志并实时反馈任务运行情况。
9.9 容错设计与自动重试
- 对失败请求做自动重试机制;
- 利用 Redis 记录失败 URL 和次数,超过阈值报警;
- 支持任务断点续爬。
9.10 图解:多源分布式异步爬虫数据流
flowchart LR
Subgraph Redis
A(RequestQueue)
B(DupeFilterSet)
C(FailQueue)
end
Spider1 -->|请求| A
Spider2 -->|请求| A
Spider1 -->|去重| B
Spider2 -->|去重| B
Spider1 -->|失败记录| C
Spider2 -->|失败记录| C
A --> ScrapydNodes
ScrapydNodes --> DB
第10章:Scrapy 爬虫安全防护与反爬策略破解实战
10.1 反爬机制概述
网站常见反爬措施包括:
- IP 封禁与限频;
- User-Agent 及请求头检测;
- Cookie 验证与登录校验;
- JavaScript 渲染与动态内容加载;
- CAPTCHA 验证码;
- Honeypot 诱饵链接与数据陷阱。
10.2 IP 代理池构建与使用
10.2.1 代理池的重要性
- 防止单 IP 访问被封;
- 分散请求压力;
- 模拟多地域访问。
10.2.2 免费与付费代理对比
类型 | 优点 | 缺点 |
---|---|---|
免费代理 | 易获取,成本低 | 不稳定,速度慢 |
付费代理 | 稳定高效,安全 | 成本较高 |
10.2.3 代理池实现示例
import requests
import random
class ProxyPool:
def __init__(self, proxies):
self.proxies = proxies
def get_random_proxy(self):
return random.choice(self.proxies)
proxy_pool = ProxyPool([
"http://111.111.111.111:8080",
"http://222.222.222.222:8080",
# 更多代理
])
def fetch(url):
proxy = proxy_pool.get_random_proxy()
response = requests.get(url, proxies={"http": proxy, "https": proxy})
return response.text
10.3 User-Agent 及请求头伪装
- 动态随机更换 User-Agent;
- 模拟浏览器常用请求头;
- 配合 Referer、防盗链头部。
示例:
import random
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
# 更多 User-Agent
]
def get_random_headers():
return {
"User-Agent": random.choice(USER_AGENTS),
"Accept-Language": "en-US,en;q=0.9",
"Referer": "https://example.com"
}
10.4 Cookie 管理与登录模拟
- 自动维护 CookieJar,实现会话保持;
- 使用 Scrapy 的
CookiesMiddleware
; - 模拟登录表单提交、Token 获取。
10.5 JavaScript 渲染处理
- 使用 Selenium、Playwright 等浏览器自动化工具;
- 结合 Splash 实现轻量级渲染;
- Scrapy-Splash 集成示例。
10.6 CAPTCHA 验证码识别与绕过
- 使用第三方打码平台(如超级鹰);
- OCR 技术自动识别;
- 结合滑动验证码、图片验证码破解技巧。
10.7 Honeypot 与数据陷阱识别
- 分析页面结构,避免访问隐藏链接;
- 验证数据合理性,过滤异常数据;
- 增加数据校验逻辑。
10.8 反爬策略动态适应
- 动态调整请求频率;
- 智能代理切换;
- 实时检测封禁并自动更换 IP。
10.9 实战案例:绕过某电商反爬
- 分析封禁策略,发现基于 IP 限制;
- 搭建稳定代理池,结合动态 User-Agent;
- 使用 Selenium 处理登录与 JS 渲染;
- 实现验证码自动识别与重试;
- 持续监控并调整请求参数。
10.10 图解:反爬防护与破解流程
flowchart TD
Request[请求网站]
subgraph 反爬防护
IPCheck[IP限制]
UACheck[User-Agent检测]
JSRender[JS动态渲染]
CAPTCHA[验证码验证]
Honeypot[隐藏陷阱]
end
Request -->|绕过| ProxyPool[代理池]
Request -->|伪装| Header[请求头伪装]
Request -->|渲染| Browser[浏览器自动化]
Request -->|验证码| OCR[验证码识别]
第11章:Scrapy+Redis+Kafka 实时分布式数据管道架构设计
11.1 现代数据采集的挑战
随着数据量和业务复杂度增长,传统单机爬虫难以满足:
- 大规模数据实时采集;
- 多源异步任务调度;
- 高吞吐、低延迟数据处理;
- 系统弹性和容错能力。
11.2 架构总体设计
本架构采用 Scrapy 作为采集引擎,Redis 负责调度和请求去重,Kafka 用于实时数据传输和处理。
graph LR
Spider[Scrapy Spider] --> RedisQueue[Redis 请求队列]
RedisQueue --> ScrapyScheduler[Scrapy Scheduler]
ScrapyScheduler --> Downloader[Scrapy Downloader]
Downloader --> Parser[Scrapy Parser]
Parser --> KafkaProducer[Kafka 生产者]
KafkaProducer --> KafkaCluster[Kafka 集群]
KafkaCluster --> DataProcessor[实时数据处理]
DataProcessor --> DataStorage[数据库/数据仓库]
11.3 Scrapy 与 Redis 集成
11.3.1 scrapy-redis 插件
- 实现请求去重与分布式调度;
- 支持请求缓存和持久化队列。
11.3.2 配置示例
# settings.py
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
REDIS_URL = "redis://127.0.0.1:6379"
11.4 Kafka 在实时数据流中的角色
Kafka 是一个高吞吐、分布式消息系统,支持:
- 多生产者、多消费者模型;
- 持久化消息,支持回溯;
- 实时流处理。
11.5 Scrapy 发送数据到 Kafka
利用 kafka-python
库,将爬取的 Item 实时发送到 Kafka:
from kafka import KafkaProducer
import json
producer = KafkaProducer(bootstrap_servers='localhost:9092')
class MyPipeline:
def process_item(self, item, spider):
data = json.dumps(dict(item)).encode('utf-8')
producer.send('scrapy_topic', data)
return item
11.6 Kafka 消费者与实时处理
- 构建消费者服务读取 Kafka 数据;
- 实时清洗、分析或存入数据库;
- 支持扩展为 Flink、Spark Streaming 等流式计算平台。
11.7 架构优势
优点 | 说明 |
---|---|
高扩展性 | 各组件独立,易横向扩展 |
异步高吞吐 | Redis + Kafka 保证数据流畅 |
容错能力 | 消息持久化,失败可重试 |
灵活的数据消费模式 | 支持多消费者并行处理 |
11.8 实战部署建议
- Redis 集群配置,保证调度高可用;
- Kafka 集群部署,分区合理设计;
- Scrapy 多节点分布式部署,配合 Gerapy 调度;
- 日志监控与报警。
11.9 图解:实时分布式数据流转
flowchart LR
subgraph Scrapy集群
A1[Spider1]
A2[Spider2]
end
A1 --> RedisQueue
A2 --> RedisQueue
RedisQueue --> ScrapyScheduler
ScrapyScheduler --> Downloader
Downloader --> Parser
Parser --> KafkaProducer
KafkaProducer --> KafkaCluster
KafkaCluster --> Consumer1
KafkaCluster --> Consumer2
Consumer1 --> DB1[数据库]
Consumer2 --> DB2[数据仓库]
第12章:Scrapy 与机器学习结合实现智能化数据采集
12.1 智能爬虫的需求与优势
- 自动识别和过滤无效数据,提高数据质量;
- 动态调整爬取策略,实现精准采集;
- 结合自然语言处理提取关键信息;
- 实现异常检测与自动告警。
12.2 机器学习在爬虫中的应用场景
应用场景 | 说明 |
---|---|
数据分类与标注 | 自动对爬取内容进行分类 |
内容去重 | 基于相似度的文本去重 |
页面结构识别 | 自动识别变动页面的内容区域 |
异常数据检测 | 检测错误或异常数据 |
智能调度策略 | 根据历史数据动态调整爬取频率 |
12.3 典型机器学习技术
- 文本分类(SVM、深度学习模型);
- 聚类分析(K-Means、DBSCAN);
- 自然语言处理(NER、关键词抽取);
- 机器视觉(图像识别);
12.4 Scrapy 集成机器学习示例
4.1 数据预处理 Pipeline
import joblib
class MLClassificationPipeline:
def __init__(self):
self.model = joblib.load('model.pkl')
def process_item(self, item, spider):
features = self.extract_features(item)
pred = self.model.predict([features])
item['category'] = pred[0]
return item
def extract_features(self, item):
# 特征提取逻辑,如文本向量化
return ...
12.5 动态调度与策略优化
- 利用模型预测网页变化,自动调整调度频率;
- 结合强化学习实现自适应调度。
12.6 智能内容提取
- 利用 NLP 模型自动识别正文、标题、时间等;
- 减少人工规则配置,提高适应性。
12.7 异常检测与自动告警
- 训练模型检测异常页面或数据;
- 爬虫实时反馈异常,自动暂停或重试。
12.8 图解:机器学习驱动的智能爬虫流程
flowchart TD
Spider[Scrapy Spider]
MLModel[机器学习模型]
DataPreprocess[数据预处理]
Scheduler[调度系统]
Monitor[异常检测与告警]
Spider --> DataPreprocess --> MLModel --> Scheduler
MLModel --> Monitor
Scheduler --> Spider
评论已关闭