Redis 服务
List 或 Stream 结构存储待执行的任务(如 crawler:tasks)。Hash 存储任务元数据(如任务ID、URL、优先级、重试次数)。Set 或 布隆过滤器 实现 URL 去重。Pub/Sub 广播全局指令(如暂停、终止任务)。Web 服务
SQL Server
tasks: 任务ID、状态、创建时间、优先级。results: 清洗后的结构化数据(标题、作者、正文)。logs: 错误日志、重试记录。文件服务器
任务ID/时间戳 组织目录结构,例如 /data/task_001/20240401/page.html。任务执行流程
while True:
task = redis.blpop("crawler:tasks", timeout=30) # 阻塞获取任务
if task:
try:
# 使用 Chrome 渲染动态页面
html = chrome_driver.get(task.url)
# 用 Trafilatura 解析内容
data = trafilatura.extract(html)
# 存储到 SQL Server 和文件服务器
save_to_sql(data)
save_to_fileserver(html)
# 标记任务成功
redis.hset(f"task:{task.id}", "status", "success")
except Exception as e:
handle_retry(task, e) # 重试逻辑
关键技术选型
Selenium 或 Playwright 控制 Headless Chrome。Trafilatura 或 Parsel(Scrapy 的解析库)。ProxyMesh)、随机 User-Agent 轮换。资源隔离与扩展
任务去重
redisbloom 模块),拦截已爬URL。分布式锁
使用 Redlock 算法保证同一任务不会被多个 Worker 重复执行:
lock = redlock.Redlock([redis], retry_count=3)
if lock.acquire("task_lock:123", 5000): # 5秒锁超时
process_task()
错误处理
监控告警
+----------------+ +-----------------+
| User | | Web Dashboard |
| (提交任务) +-------> (监控/配置) |
+----------------+ +--------+--------+
|
| API
+------------------+ +------v--------+ +----------------+
| SQL Server <--------+ Master +-------> Redis |
| (结构化数据) | | (Web服务) | | (任务队列/状态) |
+------------------+ +------+--------+ +--------+-------+
| |
| | 任务分发
+------------------+ +------v-------------------------v-------+
| File Server <--------+ Workers |
| (原始HTML/文件) | | (Scrapy/Chrome/Trafilatura) |
+------------------+ +----------------------------------------+
Master 高可用
Worker 异构调度
queue:dynamic → 需要 Chrome 的 Worker。queue:static → 仅需 Scrapy 的轻量 Worker。安全加固
通过以上设计,你的系统将具备高可扩展性和容错性,能有效管理分布式爬虫任务,同时合理分配网络和计算资源。
User: 这是否意味着 Workers 是无状态的,也就是说 Workers 机器不能在本地存储有关文件或者数据,即便有许多网页数据、文件、图片等资源,也要存储到中心文件服务中?这样会不会对带宽要求太高了
You:
你的观察非常准确!在分布式系统中,Worker 是否需要完全无状态、是否必须将所有数据实时传输到中心存储,取决于具体的业务需求、数据规模和技术成本。以下是更细致的分析和优化方案:
示例流程:
# Worker 处理任务时:
1. 爬取数据 → 解析结构化结果 → 立即写入 SQL Server(关键数据)
2. 原始 HTML/图片 → 暂存本地磁盘 → 定时任务批量压缩上传到文件服务器
gzip 或 brotli 压缩原始 HTML,减少传输体积。MD5/SHA1 计算文件指纹,仅上传唯一文件。.tar.gz 或 .parquet 格式后上传,减少请求次数。示例:
# Worker 本地操作:
tar -czvf batch_001.tar.gz /tmp/html/*.html
s3cmd put batch_001.tar.gz s3://your-bucket/
示例:
# 关键字段实时写入
save_to_sql({
"title": "Example",
"content": "Hello World",
"url": "https://example.com"
})
示例:
# 原始 HTML 暂存本地,每小时上传一次
with open(f"/tmp/html/{task_id}.html", "w") as f:
f.write(html)
schedule_hourly_upload() # 触发批量上传
Redis 记录文件上传状态(如 file:1234 → uploaded)。 Worker 本地存储(原始HTML)
→ 每日批量上传至 NAS/SFTP
→ 最终归档到 S3/OSS(低频存储)
Worker 本地作为 HDFS DataNode
→ 实时写入 HDFS 分布式文件块
→ 定期备份到对象存储(如 S3 Glacier)
Worker(本地IDC) → 临时存储到本地 NAS
→ 夜间通过专线同步到云端 S3
通过合理设计,即使 Worker 有部分本地存储,也能在带宽、成本和可靠性之间取得平衡。例如,爬取新闻正文时立即存储结构化结果到 SQL,而原始 HTML 可以每小时批量压缩上传一次,这样对带宽的影响会显著降低。
在爬虫或分布式系统中,类似“管理者-执行者”这样的架构模式非常常见,通常会用一些更具象化的名称来描述这种分工明确的架构。以下是一些常见的类似称呼:
负责任务的分配、调度和监控。
存储任务状态、爬取规则和数据。
协调多个Worker节点的运行。
Worker(执行者):
负责具体任务的执行,如爬取网页、解析数据。
将结果返回给Master或存储到指定位置。
适用场景:
分布式爬虫系统。
需要动态扩展执行节点的场景。
以下是一些常用的库和架构:
使用任务队列(Message Queue)来分发任务。
Worker 从队列中拉取任务并执行。
常用库:
Celery:
分布式任务队列库,支持异步任务调度和 Worker 管理。
可以与 Redis、RabbitMQ 等消息队列集成。
支持定时任务、任务重试、结果存储等功能。
RQ (Redis Queue):
基于 Redis 的轻量级任务队列。
简单易用,适合小型项目。
Huey:
轻量级任务队列,支持 Redis 或 SQLite 作为后端。
适合小型到中型项目。
架构示例:
Master:负责将任务推送到队列。
Worker:从队列中拉取任务并执行。
适用场景:
需要异步任务处理的系统。
分布式任务调度。