Scrapy 实战教程:从框架原理到微博分布式爬虫

这份教程以当前 weibocrawler 仓库为主线,先讲 Scrapy 的核心组件,再把每个概念映射到项目里的 settings.pyitems.pyspiders/pipelines.pymiddlewares.py。读完后,你应该能看懂现有爬虫链路,并能按同样模式扩展一个新的微博 API 爬虫。

框架:Scrapy + scrapy-redis 存储:MongoDB 队列:Redis Set 场景:微博 JSON API 采集

1. 教程目标

Scrapy 本身不只是“发请求再解析 HTML”的工具,它更像一个可扩展的数据流引擎。当前项目的目标也不是采集单个页面, 而是用 Redis 驱动多个 Spider 协同工作:先抓大 V 微博,再根据微博派生评论、转发、点赞,再从互动用户继续派生用户详情和关注列表。

本项目的关键特征: 所有主要 Spider 都继承 scrapy_redis.spiders.RedisSpider;起始 URL 来自 Redis; 解析结果以 Item 形式进入 Pipeline;Pipeline 一边写 MongoDB,一边继续向 Redis 写入下一阶段 URL。

建议先按本文顺序读一遍,再对照代码做二次阅读:

  • README.md:原始架构说明和启动顺序。
  • weibo/settings.py:Scrapy、Redis、MongoDB、Middleware、Pipeline 的集中配置。
  • weibo/items.py:所有数据类型、Redis 队列名和 API URL 模板。
  • weibo/spiders/:每类数据的解析逻辑。
  • weibo/pipelines.py:数据清洗、写库、派生 URL、布隆过滤。

2. Scrapy 架构速览

Scrapy 架构图:Engine、Scheduler、Downloader、Spider、Middleware、Item Pipeline 的数据流
Scrapy 经典架构图:Engine 位于中心,调度 Scheduler、Downloader、Spider、Middleware 和 Item Pipeline 之间的数据流。

Scrapy 的核心链路可以理解成一个闭环:

Spider 产生初始请求,或从响应中继续产生新的 RequestItem
Engine 控制数据流,把请求交给 Scheduler,把响应交给 Spider,把 Item 交给 Pipeline。
Scheduler 负责排队和去重,决定下一个要下载的请求。
Downloader 真实发起 HTTP 请求并返回 Response。
Middleware 在请求和响应流经 Downloader 或 Spider 时插入自定义逻辑,例如 Cookie、代理、重试。
Pipeline 处理 Spider 抽取出的 Item,例如补字段、清洗、写库、派生下游任务。

Scrapy 内部数据流

1. Spider产生初始 Request,或从 Response 中继续 yield Request / Item。
2. Engine接收 Spider 输出,把 Request 交给 Scheduler。
3. Scheduler请求排队、去重,并返回下一个待下载 Request。
4. Downloader Middleware为请求加 Cookie、代理、Header,处理异常响应。
5. Downloader发起 HTTP 请求,从微博接口下载 JSON 响应。
6. Spider解析 JSON,yield Item 或下一页 Request。
7. Item Pipeline补字段、写 MongoDB、派生 Redis 下游任务。
8. 新一轮调度新 Request 回到 Scheduler,新 Item 进入存储和编排。

普通 Scrapy 与本项目的区别

维度 普通 Scrapy 本项目
起始 URL 通常写在 Spider 的 start_urls 列表中。 写入 Redis,例如 m_weibo_redis:start_urls,Spider 从 Redis 拉取。
调度队列 内存或本地磁盘队列。 scrapy-redis 使用 Redis 队列,可多进程、多机器共享。
去重 默认 RFPDupeFilter Scrapy 请求去重 + 项目自定义布隆过滤器,避免重复灌入起始 URL。
结果存储 常见是 JSON、CSV、数据库。 MongoDB,按 Item 类型写入 weiboscommentsrepostsusers 等集合。

3. 项目地图

weibocrawler/
├── scrapy.cfg                         # Scrapy 项目入口,默认 settings = weibo.settings
├── requirements.txt                   # Scrapy、scrapy_redis、pymongo、redis 等依赖
├── README.md                          # 原始架构、部署、启动顺序说明
├── weibo/
│   ├── settings.py                    # 全局配置:中间件、管道、Redis、MongoDB、限速
│   ├── items.py                       # Item 类型、集合名、Redis key、URL 模板
│   ├── pipelines.py                   # 时间字段、MongoDB 入库、派生 URL、布隆过滤
│   ├── middlewares.py                 # Cookie、跳转检测、代理、重试
│   ├── redis_init.py                  # 初始化 Redis 起始 URL
│   ├── run_spider.py                  # 以 mode 参数启动指定 Spider
│   └── spiders/
│       ├── m_weibo_redis.py           # 大 V 微博列表
│       ├── m_comment_redis.py         # 评论
│       ├── m_repost_redis.py          # 转发
│       ├── m_attitude_redis.py        # 点赞
│       ├── m_user_detail_redis.py     # 用户详情
│       ├── m_user_attention_tag_redis.py
│       └── m_user_attention_members_redis.py

配置层

settings.py 决定运行行为,包括 DOWNLOAD_DELAYDOWNLOADER_MIDDLEWARESITEM_PIPELINESREDIS_URLMONGO_URI

业务层

items.pyspiders/ 定义“采什么”和“怎么解析”。本项目采集的是微博接口返回的 JSON,不是传统 HTML 页面。

任务编排层

redis_init.py 负责把第一批 URL 放入 Redis;RedisStartUrlContinuePipeline 负责根据已采 Item 继续派生后续 URL。

存储层

MongoPipeline 使用 update_one(..., upsert=True) 写 MongoDB,并为常用查询字段建唯一索引或时间索引。

4. 从 settings.py 理解项目运行方式

weibo/settings.py 是 Scrapy 项目的控制台。理解这个文件,就能知道一次请求会经过哪些组件。

基础配置

BOT_NAME = 'weibo'
SPIDER_MODULES = ['weibo.spiders']
NEWSPIDER_MODULE = 'weibo.spiders'
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 0.3
RANDOMIZE_DOWNLOAD_DELAY = True

ROBOTSTXT_OBEY = False 表示不按 robots.txt 自动过滤 URL。DOWNLOAD_DELAYRANDOMIZE_DOWNLOAD_DELAY 控制请求节奏,README 里也记录了不同场景下 M 站、PC 站的建议间隔。

下载器中间件

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 101,
    'weibo.middlewares.CookieMiddleware': 100,
    'weibo.middlewares.RedirectMiddleware': 200,
}

Scrapy 会按优先级处理请求和响应。这里的重点是: CookieMiddleware 从 MongoDB 账号池随机选可用 Cookie; RedirectMiddleware 检查 302、403、414、418 或接口 ok=-100,判断 Cookie 或 IP 是否异常。

Item Pipeline

ITEM_PIPELINES = {
    'weibo.pipelines.RecentTimePipeline': 100,
    'weibo.pipelines.TimePipeline': 300,
    'weibo.pipelines.RedisStartUrlContinuePipeline': 398,
    'weibo.pipelines.RedisStartUrlFilterPipeline': 399,
    'weibo.pipelines.MongoPipeline': 400,
}

数字越小越先执行。一个 Item 从 Spider 产出后,会依次经过这些 Pipeline:先处理时间和通用字段,再派生下游 Redis URL,再记录过滤器,最后写 MongoDB。

scrapy-redis 配置

SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
SCHEDULER_PERSIST = True
SCHEDULER_FLUSH_ON_START = False
REDIS_START_URLS_AS_SET = True

这几行是分布式能力的核心。SCHEDULER_PERSIST = True 表示爬虫停止时 Redis 队列不清空; SCHEDULER_FLUSH_ON_START = False 表示重启后继续消费旧队列; REDIS_START_URLS_AS_SET = True 表示起始 URL 用 Set 存放,天然避免重复成员。

注意: 当前 settings.py 中包含 Redis、MongoDB、DingTalk 等明文连接信息。教程层面可以理解其作用,但生产代码建议改为环境变量或密钥管理系统,避免仓库泄露造成风险。

5. Item:定义数据类型、Redis Key 和 URL 模板

在这个项目里,Item 不只是字段声明,还承担了三个职责:MongoDB 集合名、Redis 起始队列名、接口 URL 模板。 例如 WeiboItem

class WeiboItem(Item):
    collection = 'weibos'
    start_url = 'm_weibo_redis:start_urls'
    start_url_filter = 'm_weibo_redis:filter'
    url = 'https://m.weibo.cn/api/container/getIndex?uid={uid}&type=uid&page={page}&containerid=107603{uid}'
    data = Field()
    first_row = Field()

常见 Item 对照表

Item MongoDB 集合 Redis 起始队列 用途
WeiboItem weibos m_weibo_redis:start_urls 采集大 V 微博列表,是主链路的起点。
CommentNewItem comments m_comment_redis:start_urls 采集微博评论,并抽取评论用户。
RepostItem reposts m_repost_redis:start_urls 采集微博转发,并抽取转发用户。
AttitudeItem attitudes m_attitude_redis:start_urls 采集微博点赞用户。
UserDetailItem user_detail m_user_detail_redis:start_urls 采集用户详情接口。
UserAttentionTagItem user_attention_tag m_user_attention_tag_redis:start_urls 采集用户关注分组。
UserAttentionMemberItem users m_user_attention_member_redis:start_urls 采集某个关注分组下的成员。
实践建议: 新增采集对象时,先在 items.py 中把 collectionstart_urlstart_url_filterurl 定清楚。 这样 Spider 和 Pipeline 都可以复用这些常量,避免 Redis key 和 URL 模板散落在多个文件。

6. Spider:请求从 Redis 来,结果以 Item 返回

本项目的主要 Spider 都继承 RedisSpider。它与普通 scrapy.Spider 的最大区别是: 不依赖类属性 start_urls,而是监听 Redis key。

以微博列表 Spider 为例

class MWeiboRedisSpider(RedisSpider):
    name = 'm_weibo_redis'
    allowed_domains = ['m.weibo.cn']
    start_url = WeiboItem.start_url
    next_url = WeiboItem.url

    def parse(self, response):
        result = json.loads(response.text)
        if result.get('ok') == 1 and result.get('data').get('cards'):
            cards = result.get('data', {}).get('cards', [])
            uid = re.findall('uid=(\\d*)', response.url)[0]
            page = re.findall('page=(\\d*)', response.url)[0]
            for card in cards:
                weibo = card.get('mblog')
                if card.get('card_type') == 9 and weibo:
                    item = WeiboItem()
                    item['data'] = weibo
                    yield item
            yield Request(self.next_url.format(uid=uid, page=int(page) + 1), self.parse)

真实代码还做了两个重要处理:

  • 通过 since_data = '2019-07-22' 限定只继续翻页采集指定日期之后的微博。
  • 每页成功后一次派生后面 3 页,注释说明这是为了应对可能的空数据页。

分页模式

Spider 分页参数 终止方式
m_weibo_redis.py page 当微博创建时间早于 since_data 或接口无有效 cards 时停止。
m_comment_redis.py max_id 接口返回新的 max_id 才继续请求下一页。
m_repost_redis.py page reposts 就继续加页;无数据记录错误并停止。
m_attitude_redis.py page 有点赞数据就继续加页;接口无数据停止。

为什么要重写 make_requests_from_url

def make_requests_from_url(self, url):
    return scrapy.Request(url, dont_filter=True)

代码注释写到“新版本没有这个方法所以需要重写下”。在 scrapy-redis 老版本适配中, 这个方法用于把 Redis 里的 URL 字符串转换成 Scrapy Requestdont_filter=True 表示 Redis 起始 URL 取出后不走默认请求去重,这对重跑、断点续爬、外部灌入任务有帮助。

7. scrapy-redis:把单机爬虫改造成分布式爬虫

scrapy-redis 替换了 Scrapy 的 Scheduler 和 DupeFilter,让多个爬虫进程可以共享 Redis 中的请求队列。 本项目又额外使用 Redis Set 和布隆过滤器管理“业务起始 URL”。

scrapy-redis 队列示意图:多个调度器共享一个爬取队列
scrapy-redis 的核心思路:把请求队列放到 Redis,多台机器或多个进程共享同一批待爬任务。

初始化入口

weibo/redis_init.py 会把配置中的大 V UID 转成微博列表 URL,然后写入 WeiboItem.start_url

def init_m_weibo_redis_spider():
    redis_utils.bak_weibo_filter()
    urls = []
    for init_weibo in INIT_USERS:
        urls.append(WeiboItem.url.format(uid=init_weibo, page=1))
    redis_init(WeiboItem.start_url, urls)

启动方式

# 初始化大 V 微博起始 URL
python weibo/redis_init.py weibo

# 启动微博列表 Spider
scrapy crawl m_weibo_redis

# 或使用封装脚本
python weibo/run_spider.py weibo

Redis Key 的两类用途

类型 示例 作用
起始 URL Set m_comment_redis:start_urls 等待某个 RedisSpider 消费的任务入口。
业务布隆过滤器 m_user_detail_redis:filter 避免 Pipeline 反复把同一个用户详情 URL 灌入 Redis。
scrapy-redis 调度队列 scrapy_redis.scheduler.Scheduler 生成 Spider 运行过程中待下载 Request 的队列。
容易混淆的点: start_url 不是 Scrapy 原生的 start_urls。这里它是项目自定义命名,用来保存 Redis key; 真正起作用的是 RedisSpider 按 key 从 Redis 拉 URL。

8. Pipeline:本项目最重要的业务编排层

如果只看 Spider,会误以为每个 Spider 互相独立。实际联动关系主要写在 weibo/pipelines.py。 每一个 Item 经过 Pipeline 后,可能写库,也可能生成下一批 Redis URL。

TimePipeline:补充 crawled_at

class TimePipeline():
    def process_item(self, item, spider):
        data = item.get('data')
        if data:
            user = data.get('user')
            if user:
                user['crawled_at'] = datetime.now(tz=pytz.utc)
            data['crawled_at'] = datetime.now(tz=pytz.utc)
        return item

所有带 data 的 Item 都会补充抓取时间。如果数据里有嵌套 user,用户对象也会带上抓取时间。

RedisStartUrlContinuePipeline:派生下游任务

这是当前项目的“流程编排器”。典型规则包括:

  • WeiboItem 且微博作者属于 INIT_USERS:派生点赞、评论、转发 URL。
  • CommentNewItemRepostItemAttitudeItem:从互动数据抽取用户 ID,派生用户详情和关注分组 URL。
  • UserAttentionTagItem:从关注分组中派生关注成员 URL。
  • UserSearchItemUserAttentionMemberItem:派生用户详情 URL。
if isinstance(item, WeiboItem):
    weibo_id = item.get("data").get("id")
    self.conn.sadd(AttitudeItem.start_url, AttitudeItem.url.format(weibo_id=weibo_id, page=1))
    self.conn.sadd(CommentNewItem.start_url, CommentNewItem.url.format(id=weibo_id, max_id=0))
    self.conn.sadd(RepostItem.start_url, RepostItem.url.format(id=weibo_id, page=1))

RedisStartUrlFilterPipeline:记录已处理入口

该 Pipeline 用布隆过滤器记录已经产生过的业务入口。例如用户详情抓完后,把 UserDetailItem.url.format(user_id=user_id) 写入 UserDetailItem.start_url_filter。 后续 Pipeline 想再次添加相同用户详情 URL 时,会先查过滤器。

MongoPipeline:清洗并写库

MongoPipeline 做了三类事:

  1. open_spider 中连接 MongoDB,并为各集合创建索引。
  2. 按 Item 类型清洗数据,例如把嵌套 user 拆到 users 集合。
  3. update_one(..., upsert=True) 做幂等写入,避免重复采集导致重复记录。
if isinstance(item, CommentNewItem) or isinstance(item, RepostItem) or isinstance(item, AttitudeItem):
    data = item.get('data')
    user = data.get('user')
    if user:
        self.db['users'].update_one({'id': user.get("id")}, {'$set': user}, upsert=True)
        del data["user"]
    self.db[item.collection].update_one({'id': data.get('id')}, {'$set': data}, upsert=True)
设计重点: Spider 负责“解析当前响应”,Pipeline 负责“处理解析结果”。这个边界让 Spider 不必直接知道 MongoDB 怎么存,也不必把所有下游任务逻辑写进解析函数。

9. Middleware:Cookie、异常响应和代理

微博采集很依赖登录态和请求稳定性,所以本项目的下载器中间件很关键。

CookieMiddleware

CookieMiddleware 在每次请求前,根据 URL 判断是 PC 站还是 M 站,再从 MongoDB 的 weibo.account 集合中随机取一个状态为 success 的账号 Cookie。

def process_request(self, request, spider):
    is_pc = request.url.find("weibo.com") != -1
    if is_pc:
        self.pc_cookie(request)
    else:
        self.m_cookie(request)

RedirectMiddleware

该中间件在响应阶段判断账号或 IP 是否异常:

  • 302403:通常表示 Cookie 或账号状态异常。
  • 418414:通常表示 IP 风控异常。
  • JSON 中 ok == -100:触发钉钉通知,并把账号状态更新为 error。
  • JSON 解析失败:记录错误并返回原请求重试。

代理相关中间件

ProxypoolMiddleware 可以从代理池获取代理并写入 request.meta['proxy']ProxyDecreaseMiddleware 根据 HTTP 状态码或连接异常降低代理评分,或者在成功状态下提高代理评分。 当前全局配置中代理中间件是注释状态,说明项目可按部署环境启用。

10. 用一次完整微博采集流程串起来

结合 README 和代码,主流程可以这样理解:

本项目业务流程图

INIT_USERS配置大 V UID,生成微博列表第一页 URL。
m_weibo_redis采集大 V 微博,写入 weibos。
互动 URLPipeline 派生 comment / repost / attitude 起始 URL。
互动 Spider采评论、转发、点赞,并抽取互动用户。
用户 URL派生 user_detail 和 user_attention_tag。
关注分组采集 relations_tags,继续派生成员 URL。
关注成员采集 groupsMembersByTag,写入 users。
MongoDB最终沉淀 weibos、comments、reposts、attitudes、users、user_detail。
第 0 步:准备账号池、Redis、MongoDB。 MongoDB 中需要有 weibo.account 可用 Cookie;Redis 用于队列;MongoDB 用于结果表。
第 1 步:初始化大 V 微博 URL。 python weibo/redis_init.py weibo 读取 INIT_USERS,生成 WeiboItem.url,写入 m_weibo_redis:start_urls
第 2 步:运行微博列表 Spider。 m_weibo_redis 从 Redis 拉 URL,解析微博列表,产出 WeiboItem,写入 weibos
第 3 步:Pipeline 派生互动 URL。 对大 V 的微博,RedisStartUrlContinuePipeline 写入评论、转发、点赞起始 URL。
第 4 步:运行评论、转发、点赞 Spider。 m_comment_redism_repost_redism_attitude_redis 抽取互动用户,并写入对应集合。
第 5 步:Pipeline 派生用户 URL。 从互动用户派生 user_detailuser_attention_tag;再从关注分组派生 user_attention_member
第 6 步:运行用户相关 Spider。 采集用户详情、关注分组、关注成员,最终沉淀到 user_detailuser_attention_tagusers 等集合。

推荐启动顺序

# 1. 初始化大 V 微博入口
python weibo/redis_init.py weibo

# 2. 采集大 V 微博
python weibo/run_spider.py weibo

# 3. 采集互动数据
python weibo/run_spider.py comment
python weibo/run_spider.py repost
python weibo/run_spider.py attitude

# 4. 采集用户关注关系和详情
python weibo/run_spider.py user_attention_tag
python weibo/run_spider.py user_attention_member
python weibo/run_spider.py user_detail
运行前检查: 当前代码依赖外部 Redis、MongoDB、账号 Cookie 和微博接口可用性。不要在不了解线上连接信息和采集策略的情况下直接运行生产配置。

11. 如何按本项目模式新增一个 Spider

假设要新增一个“采集某类用户标签”的微博 API,建议按下面步骤做。

步骤 1:在 items.py 定义 Item

class UserSomeTagItem(Item):
    collection = 'user_some_tag'
    start_url = 'm_user_some_tag_redis:start_urls'
    start_url_filter = 'm_user_some_tag_redis:filter'
    url = 'https://m.weibo.cn/api/example?uid={user_id}&page={page}'
    data = Field()
    first_row = Field()

步骤 2:新增 RedisSpider

class MUserSomeTagRedisSpider(RedisSpider):
    name = 'm_user_some_tag_redis'
    allowed_domains = ['m.weibo.cn']
    start_url = UserSomeTagItem.start_url
    next_url = UserSomeTagItem.url

    def parse(self, response):
        result = json.loads(response.text)
        if result.get('ok') == 1:
            for row in result.get('data', {}).get('list', []):
                item = UserSomeTagItem()
                item['data'] = row
                yield item

    def make_requests_from_url(self, url):
        return scrapy.Request(url, dont_filter=True)

步骤 3:在 Pipeline 中写存储和派生逻辑

如果新 Item 只需入库,可以在 MongoPipeline._process_item 中增加一个 isinstance 分支。 如果新 Item 还会派生下游 URL,就在 RedisStartUrlContinuePipeline._process_item 中增加对应规则。

步骤 4:注册启动入口

如果用 scrapy crawl,只要 Spider 类在 weibo/spiders/ 下并有唯一 name 即可。 如果用 run_spider.py,还需要把 mode 映射到 Spider 类。

mode_to_spider = {
    'user_some_tag': MUserSomeTagRedisSpider,
}

步骤 5:准备 Redis 起始 URL

redis-cli SADD m_user_some_tag_redis:start_urls \
  'https://m.weibo.cn/api/example?uid=123456&page=1'

12. 调试与排障

先确认 Scrapy 是否能找到项目配置

scrapy settings --get BOT_NAME
scrapy list

如果找不到 Spider,优先检查 scrapy.cfgSPIDER_MODULES 和当前工作目录。

检查 Redis 队列

redis-cli SCARD m_weibo_redis:start_urls
redis-cli SMEMBERS m_weibo_redis:start_urls
redis-cli SCARD m_comment_redis:start_urls

如果 Spider 启动后一直 idle,多半是对应 start_urls key 为空,或者 Redis 连接配置不对。

检查账号池

// Mongo shell 示例
db.account.countDocuments({ m_status: 'success' })
db.account.countDocuments({ pc_status: 'success' })

如果账号池为空,CookieMiddleware 会抛异常并通过钉钉通知。M 站和 PC 站使用不同字段: m_status/m_cookiepc_status/pc_cookie

看日志

tail -fn 1000 logs/scrapy_$(date +%F).log

成功下载会出现 成功下载 {url};接口异常会出现 Result not okNo Comments DataJson decode error 等信息。根据日志里的 URL 回到对应 Spider 的解析逻辑定位。

常见问题

现象 可能原因 处理方向
Spider 启动后无请求 Redis 起始 URL 为空;mode 对错;连接到错误 Redis DB。 检查 REDIS_URLSCARD start_urlsrun_spider.py mode。
大量 403、302、ok=-100 Cookie 失效或账号被限制。 更新 weibo.account 中的 Cookie 和状态字段。
大量 418、414 IP 被风控或请求过快。 增大 DOWNLOAD_DELAY,启用代理池,降低并发。
MongoDB 重复键错误 唯一索引字段与写入过滤条件不一致。 检查 MongoPipeline.open_spider 建索引字段和 update_one 查询条件。
下游任务没有生成 Item 类型未进入对应 Pipeline 分支;布隆过滤器判断已存在;字段缺失。 检查 RedisStartUrlContinuePipelineRedisStartUrlFilterPipeline

13. 学习与开发检查清单

理解现有爬虫

  • 先看 items.py 确认数据类型、队列和 URL。
  • 再看 对应 spiders/*.pyparse
  • 最后看 pipelines.py 判断数据写到哪里、派生什么任务。

新增爬虫前

  • 确认接口返回是 HTML 还是 JSON。
  • 确认分页参数和停止条件。
  • 确认唯一键字段,避免 MongoDB 写入重复。
  • 确认是否会派生下游 URL。

上线运行前

  • 检查 Redis、MongoDB、账号池配置是否指向预期环境。
  • 检查 DOWNLOAD_DELAY、代理和 Cookie 策略。
  • 先用少量 URL 验证日志和 MongoDB 数据。
  • 确认异常通知不会刷屏。

代码质量

  • Spider 只负责解析当前响应。
  • 存储、清洗、派生 URL 放在 Pipeline。
  • Redis key 和 URL 模板优先放在 Item 类常量。
  • 敏感连接信息使用环境变量管理。
一句话总结: Scrapy 负责“请求-响应-解析-管道”的稳定数据流,scrapy-redis 把请求队列外置到 Redis, 而本项目的 Pipeline 把多个微博采集任务串成一条可断点续跑的分布式采集链路。