Python分布式爬虫完整教程

文章目录

一、分布式爬虫基础概念

单机爬虫局限:单机器带宽、IP、CPU有限,URL队列在内存,多开爬虫会重复抓取。

分布式架构(Master-Worker)

  1. Master(Redis服务):存放待爬URL队列、已爬URL去重集合、抓取结果。只做存储,不爬页面。
  2. Worker爬虫节点:多台服务器/多个进程,连接同一个Redis,从Redis取URL,请求网页、解析,新链接放回Redis队列。

所有Worker代码完全一样,直接复制到多台机器运行即可。

备选方案

  1. Scrapy-Redis:首选,上手快,成熟稳定,最主流
  2. 原生Python+Redis(requests/aiohttp):轻量,适合自定义逻辑(可以集成Selenium)
  3. Celery+Redis:任务分发,适合离散任务

二、环境准备

1. 安装Redis

  • Windows:Redis Windows版本;Linux:apt install redis-server
  • 启动redis:redis-server,默认端口6379
  • 开放远程访问(多机分布式需要):修改redis配置bind 0.0.0.0,设置密码

2. Python依赖安装

bash 复制代码
pip install scrapy scrapy-redis

三、实战:Scrapy-Redis分布式爬虫

步骤1:创建scrapy项目

bash 复制代码
scrapy startproject dist_crawl
cd dist_crawl
scrapy genspider demo example.com

步骤2:修改爬虫 spiders/demo.py

python 复制代码
from scrapy_redis.spiders import RedisSpider

class DemoSpider(RedisSpider):
    name = "demo"
    # Redis的key:从这个key读取起始url
    redis_key = "demo:start_urls"
    allowed_domains = ["example.com"]

    def parse(self, response):
        # 解析当前页面数据
        yield {
            "title": response.css("h1::text").get(),
            "url": response.url
        }
        # 提取新链接,自动push到redis共享队列
        for link in response.css("a::attr(href)").getall():
            yield response.follow(link, callback=self.parse)

重点:继承RedisSpider,不再写start_urls,起始URL由Redis推送。

步骤3:修改settings.py(核心配置)

python 复制代码
# 开启scrapy-redis调度器:使用redis共享队列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 全局去重过滤器(所有机器共用去重集合)
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 断点续爬:爬虫停止不清空redis队列,重启继续跑
SCHEDULER_PERSIST = True

# Redis连接地址(Master节点Redis)
REDIS_URL = "redis://127.0.0.1:6379/0"
# 如果redis有密码:redis://:mypassword@127.0.0.1:6379/0

# 管道:数据存入redis
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300,
}

# 常规爬虫配置
ROBOTSTXT_OBEY = False
CONCURRENT_REQUESTS = 16
DOWNLOAD_DELAY = 0.5
DEFAULT_REQUEST_HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}

四、运行分布式集群

  1. 启动Redis服务(Master机器)
  2. 所有Worker节点进入项目目录执行(多台机器都执行这条命令)
bash 复制代码
scrapy crawl demo

此时爬虫会阻塞,等待Redis队列推送起始URL。

  1. 往Redis写入起始url(redis-cli执行)
redis 复制代码
lpush demo:start_urls https://example.com

一旦推入URL,所有Worker会争抢消费任务,自动分布式抓取,URL不会重复。

Redis里存储的数据说明

  • demo:start_urls:待爬URL队列(list)
  • demo:dupefilter:已爬URL指纹集合(set),全局去重
  • demo:items:抓取完成的数据(list)

五、轻量方案:原生Python + Redis分布式爬虫(可集成Selenium)

如果你不想用Scrapy,想要requests/aiohttp,甚至把Selenium页面白屏巡检做成分布式任务,用这个模板。

bash 复制代码
pip install redis requests
python 复制代码
# distributed_worker.py
import redis
import requests

# Master Redis
r = redis.Redis(host="127.0.0.1", port=6379, db=0, decode_responses=True)

def get_task():
    # 从队列左侧pop,阻塞式获取任务
    return r.brpop("task_queue", timeout=5)

def save_result(data):
    r.lpush("result_queue", str(data))

def is_url_crawled(url):
    # 去重
    return r.sismember("crawled_set", url)

def mark_crawled(url):
    r.sadd("crawled_set", url)

def crawl(url):
    resp = requests.get(url, timeout=10)
    return {"url":url, "status":resp.status_code, "text":resp.text[:100]}

if __name__ == "__main__":
    print("Worker节点启动,等待任务...")
    while True:
        task = get_task()
        if not task:
            continue
        _, url = task
        if is_url_crawled(url):
            continue
        try:
            res = crawl(url)
            save_result(res)
            print(f"抓取成功:{url}")
        except Exception as e:
            print(f"抓取失败 {url}, err:{e}")
        mark_crawled(url)

Master端推送任务(redis-cli)

redis 复制代码
lpush task_queue https://example.com

多台机器同时运行这个脚本,就是分布式集群。你可以把crawl()函数替换为之前Selenium页面白屏检测代码,实现分布式页面可用性巡检集群。

相关推荐
benchmark_cc1 小时前
量化系统架构设计:为什么应该将数据获取与策略逻辑解耦?
python·数据分析·pandas·量化交易·股票数据·quantdash
狗凯之家源码网1 小时前
全新 PHP 轻量客服聊天室源码深度评测
开发语言·php·聊天室系统
凤山老林1 小时前
Spring Boot 启动冲刺:耗时剖析、懒加载策略与事件驱动初始化优化
spring boot·后端·python
小白快快跑哦2 小时前
python-字符串全解(六):正则表达式-逻辑运算或
python·正则表达式·字符串·逻辑运算
远离UE42 小时前
UE5 Substrate Thin Film 源码拆析
开发语言·javascript·ue5
keyipatience2 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
Thinker QAQ2 小时前
并发编程(九):BlockingQueue 与 Channel——语言层的原子性、可见性与有序性
java·python·go·并发编程·channel·blockingqueue·message passing
道满科技2 小时前
Function Calling 参数总出错?只修参数就行
linux·windows·python
栈帧之外2 小时前
【C++】STL之set
开发语言·c++