Python 爬虫项目 Scrapy 爬虫项目拆分多 Spider 分品类采集
前言随着爬虫业务规模扩张单一爬虫文件承载全量采集任务的模式会暴露出诸多问题代码臃肿、不同品类采集逻辑相互干扰、局部功能修改影响整体运行、无法针对不同站点 / 品类单独配置访问规则与运行策略。将大型 Scrapy 项目按照业务品类、目标站点、数据模块拆分为多个独立 Spider是工程化爬虫架构的标准优化手段。多 Spider 拆分不仅能实现业务解耦、模块独立维护还支持差异化配置下载延时、并发数、请求头、代理策略同时可灵活控制单品类启停、分批调度采集任务适配电商、资讯、分类信息等多品类并行采集场景。本文围绕 Scrapy 多 Spider 拆分架构展开实战讲解拆分原则、目录规划、代码改造、差异化配置、批量启动、任务调度、公共代码抽离、冲突规避等全流程内容结合真实业务场景完成项目重构所有代码与配置方案均可直接落地部署。本文涉及的核心依赖与官方文档链接如下可按需查阅参考资料Scrapy 官方文档 - Spider 编写规范爬虫类编写、属性定义、运行规则说明Scrapy 官方文档 - 项目结构标准项目目录、模块职责与扩展规范Scrapy 官方文档 - 命令行工具多爬虫启动、分组运行、参数传递命令详解Python 模块导入规范公共代码抽离、跨模块引用语法说明一、多 Spider 拆分核心认知与拆分原则1.1 单 Spider 项目的现存问题在业务初期使用单个 Spider 完成所有数据采集开发效率较高但当品类增多、采集规则复杂化后弊端会逐步凸显代码维护困难数千行代码集中在一个文件中不同品类解析逻辑混杂阅读、修改、BUG 排查成本大幅上升配置无法差异化框架全局配置对所有请求生效不同品类站点反爬强度、访问节奏要求不同无法单独设置下载延时、并发数运行灵活性差启动爬虫后所有品类同步采集无法单独暂停、重启某一个品类局部异常会导致全量采集中断团队协作受阻多人并行开发不同品类时频繁修改同一文件极易引发代码冲突版本管理难度增加。1.2 多 Spider 标准拆分原则结合爬虫业务特性与工程化开发规范制定统一拆分原则保证项目结构清晰、逻辑规范按业务品类拆分适用于同一目标站点下多分类数据采集例如电商网站按数码、服饰、食品拆分不同 Spider按目标站点拆分适用于同时采集多个独立站点的场景一个站点对应一个专属 Spider按数据类型拆分同一站点内区分列表页、详情页、评论、榜单等不同数据模块拆分独立爬虫职责单一原则每个 Spider 仅负责一类业务不交叉承载无关采集逻辑公共逻辑抽离原则请求头、请求封装、数据清洗、工具方法等通用代码抽离为独立模块禁止重复编写。1.3 多 Spider 运行底层原理Scrapy 项目的spiders目录为爬虫文件专属目录框架启动时会自动扫描该目录下所有继承自scrapy.Spider的类每一个合法爬虫类即为一个独立 Spider拥有专属名称、域名规则、采集入口与生命周期。多 Spider 共存时具备以下核心特性每个 Spider 相互独立拥有专属的name属性该名称为爬虫唯一标识是单独启动、调度的依据全局settings.py配置为所有 Spider 的默认配置单个 Spider 可通过类属性覆盖全局配置实现差异化管控管道、中间件为项目全局组件默认对所有 Spider 生效也可通过代码判断爬虫名称实现组件按需执行支持单爬虫启动、指定多爬虫批量启动、全爬虫启动三种运行模式适配不同调度场景。二、原始单爬虫项目演示重构前为直观展示拆分效果先搭建传统单爬虫项目模拟多品类采集业务后续基于该项目完成拆分重构。2.1 项目初始化执行终端命令创建基础项目bash运行scrapy startproject multi_spider_demo cd multi_spider_demo # 创建初始单爬虫模拟多品类采集 scrapy genspider all_spider example.com2.2 定义通用 Item统一数据结构所有品类采集数据复用同一 Item打开items.pypython运行import scrapy class CategoryItem(scrapy.Item): # 商品/资讯标题 title scrapy.Field() # 详情链接 url scrapy.Field() # 品类名称 category scrapy.Field() # 价格/发布时间 extra_info scrapy.Field() # 内容简介 desc scrapy.Field()2.3 单爬虫代码多品类混杂打开spiders/all_spider.py编写代码模拟数码、服饰、食品三大品类采集逻辑这是典型的不合理写法python运行import scrapy from multi_spider_demo.items import CategoryItem class AllSpider(scrapy.Spider): name all_spider allowed_domains [example.com] def start_requests(self): # 模拟三大品类入口链接 category_urls [ {category: 数码产品, url: https://example.com/digital}, {category: 服饰穿搭, url: https://example.com/clothes}, {category: 休闲食品, url: https://example.com/food}, ] for item in category_urls: yield scrapy.Request( urlitem[url], meta{category: item[category]}, callbackself.parse_list ) def parse_list(self, response): 列表页解析混杂三大品类解析逻辑 category response.meta.get(category) # 模拟不同品类解析规则 if category 数码产品: title_list [手机, 电脑, 耳机] extra_list [3999元, 5999元, 299元] elif category 服饰穿搭: title_list [T恤, 牛仔裤, 外套] extra_list [99元, 159元, 299元] else: title_list [饼干, 饮料, 坚果] extra_list [19.9元, 8.9元, 39.9元] for index, title in enumerate(title_list): item CategoryItem() item[title] title item[url] f{response.url}/{index} item[category] category item[extra_info] extra_list[index] item[desc] f{category}品类测试数据 yield item2.4 全局基础配置修改settings.py关闭冗余配置设置基础运行参数python运行ROBOTSTXT_OBEY False COOKIES_ENABLED False LOG_LEVEL INFO # 全局默认下载延时 DOWNLOAD_DELAY 1 RANDOMIZE_DOWNLOAD_DELAY True # 启用数据管道 ITEM_PIPELINES { multi_spider_demo.pipelines.MultiSpiderPipeline: 300, }2.5 基础管道代码编写简易管道统一接收所有品类数据打开pipelines.pypython运行class MultiSpiderPipeline: def process_item(self, item, spider): spider.logger.info(f品类{item[category]}标题{item[title]}) return item2.6 运行单爬虫执行命令启动爬虫所有品类同步采集bash运行scrapy crawl all_spider此时三大品类逻辑集中在一个文件中后续新增品类、修改解析规则都会直接改动该文件符合前文描述的弊端场景。三、项目拆分重构按品类拆分为多 Spider本节基于上述单爬虫项目按照业务品类完成拆分将数码、服饰、食品三个品类拆分为三个独立 Spider重新规划项目结构实现业务解耦。3.1 标准目录结构重构拆分完成后的完整项目目录遵循规范命名与模块划分这是多 Spider 项目的标准结构plaintextmulti_spider_demo/ ├── multi_spider_demo/ │ ├── __init__.py │ ├── items.py # 全局数据模型所有爬虫共用 │ ├── middlewares.py # 全局中间件所有爬虫共用 │ ├── pipelines.py # 全局管道所有爬虫共用 │ ├── settings.py # 全局配置文件 │ ├── utils/ # 新增公共工具模块抽离通用代码 │ │ ├── __init__.py │ │ └── common.py # 请求封装、数据清洗、工具函数 │ └── spiders/ # 爬虫目录存放所有独立Spider │ ├── __init__.py │ ├── digital_spider.py # 数码品类爬虫 │ ├── clothes_spider.py # 服饰品类爬虫 │ └── food_spider.py # 食品品类爬虫 └── scrapy.cfg新增utils目录用于存放通用代码避免多个爬虫文件出现代码冗余。3.2 抽离公共工具代码将请求构造、数据格式化等通用逻辑抽离至utils/common.py实现代码复用python运行# utils/common.py def create_request(url, category, callback): 通用请求创建工具函数 return scrapy.Request( urlurl, meta{category: category}, callbackcallback ) def format_desc(category, title): 通用内容描述格式化函数 return f{category}品类-{title} 采集数据3.3 编写分品类独立 Spider3.3.1 数码品类爬虫 digital_spider.pypython运行import scrapy from multi_spider_demo.items import CategoryItem from multi_spider_demo.utils.common import create_request, format_desc class DigitalSpider(scrapy.Spider): # 爬虫唯一名称启动依据 name digital_spider allowed_domains [example.com] # 单爬虫差异化配置数码站点反爬较强单独设置更长延时 download_delay 3 def start_requests(self): url https://example.com/digital category 数码产品 yield create_request(url, category, self.parse_list) def parse_list(self, response): category response.meta.get(category) title_list [手机, 电脑, 耳机] extra_list [3999元, 5999元, 299元] for index, title in enumerate(title_list): item CategoryItem() item[title] title item[url] f{response.url}/{index} item[category] category item[extra_info] extra_list[index] item[desc] format_desc(category, title) yield item3.3.2 服饰品类爬虫 clothes_spider.pypython运行import scrapy from multi_spider_demo.items import CategoryItem from multi_spider_demo.utils.common import create_request, format_desc class ClothesSpider(scrapy.Spider): name clothes_spider allowed_domains [example.com] # 差异化配置服饰站点延时适中 download_delay 2 def start_requests(self): url https://example.com/clothes category 服饰穿搭 yield create_request(url, category, self.parse_list) def parse_list(self, response): category response.meta.get(category) title_list [T恤, 牛仔裤, 外套] extra_list [99元, 159元, 299元] for index, title in enumerate(title_list): item CategoryItem() item[title] title item[url] f{response.url}/{index} item[category] category item[extra_info] extra_list[index] item[desc] format_desc(category, title) yield item3.3.3 食品品类爬虫 food_spider.pypython运行import scrapy from multi_spider_demo.items import CategoryItem from multi_spider_demo.utils.common import create_request, format_desc class FoodSpider(scrapy.Spider): name food_spider allowed_domains [example.com] # 差异化配置食品站点反爬较弱使用最短延时 download_delay 1 def start_requests(self): url https://example.com/food category 休闲食品 yield create_request(url, category, self.parse_list) def parse_list(self, response): category response.meta.get(category) title_list [饼干, 饮料, 坚果] extra_list [19.9元, 8.9元, 39.9元] for index, title in enumerate(title_list): item CategoryItem() item[title] title item[url] f{response.url}/{index} item[category] category item[extra_info] extra_list[index] item[desc] format_desc(category, title) yield item3.4 拆分核心逻辑说明唯一名称标识每个爬虫类的name属性必须全局唯一不能重复这是 Scrapy 区分不同 Spider 的核心标识差异化配置通过爬虫类内部的download_delay属性覆盖全局配置实现不同品类独立控制访问节奏适配不同站点反爬策略公共代码复用请求创建、数据格式化等通用逻辑抽离至utils模块三个爬虫统一调用减少代码冗余统一维护逻辑完全解耦每个爬虫仅负责自身品类的采集与解析修改某一个品类代码不会影响其他爬虫运行。四、多 Spider 启动方式与调度命令Scrapy 提供多种命令行启动方式适配单爬虫调试、多爬虫批量运行、全量启动等不同场景是多 Spider 项目日常运维的基础操作。4.1 查看项目所有爬虫执行以下命令扫描spiders目录列出当前项目内所有 Spider 名称快速确认爬虫状态bash运行scrapy list执行后会输出plaintextclothes_spider digital_spider food_spider4.2 单爬虫单独启动调试常用日常开发、BUG 排查、单品类采集时使用该命令格式为scrapy crawl 爬虫名称bash运行# 单独启动数码品类爬虫 scrapy crawl digital_spider # 单独启动服饰品类爬虫 scrapy crawl clothes_spider # 单独启动食品品类爬虫 scrapy crawl food_spider该方式仅运行指定 Spider其余爬虫不会被触发资源占用低、调试效率高。4.3 多爬虫批量启动并行采集Scrapy 原生命令默认一次仅启动一个爬虫若需要同时运行多个 Spider分为两种实现方案。4.3.1 系统多进程并行Windows/Linux 通用通过系统命令开启多个终端窗口每个窗口执行一个爬虫启动命令实现并行运行适合简单并行场景bash运行# 终端1 scrapy crawl digital_spider # 终端2 scrapy crawl clothes_spider # 终端3 scrapy crawl food_spider4.3.2 脚本批量启动工程化推荐编写 Python 启动脚本通过多进程统一调度所有 Spider无需手动开启多个终端便于定时任务、运维调度。在项目根目录创建run_all.pypython运行import subprocess from multiprocessing import Process # 定义所有爬虫名称列表 SPIDER_LIST [digital_spider, clothes_spider, food_spider] def run_spider(spider_name): 启动单个爬虫 cmd fscrapy crawl {spider_name} subprocess.run(cmd, shellTrue) if __name__ __main__: process_list [] for name in SPIDER_LIST: p Process(targetrun_spider, args(name,)) p.start() process_list.append(p) # 等待所有进程执行完毕 for p in process_list: p.join()执行脚本即可批量并行启动所有爬虫bash运行python run_all.py4.4 爬虫启停与日志区分终止爬虫无论单爬虫还是多进程爬虫按下Ctrl C即可终止当前进程日志区分多爬虫并行运行时日志会混合输出可在启动命令中为不同爬虫单独指定日志文件实现日志隔离bash运行# 单独指定日志文件数码爬虫日志输出到 digital.log scrapy crawl digital_spider --logfiledigital.log五、全局组件针对多 Spider 差异化处理项目中的管道、下载器中间件、爬虫中间件属于全局组件默认对所有 Spider 生效。实际业务中常需要让组件区分爬虫执行不同逻辑例如不同品类数据存入不同数据表、导出不同文件、使用不同代理池本节讲解差异化实现方案。5.1 管道按 Spider 分流存储 / 导出通过spider.name判断当前运行的爬虫名称执行不同业务逻辑修改pipelines.pypython运行class MultiSpiderPipeline: def process_item(self, item, spider): # 获取当前爬虫名称 spider_name spider.name category item[category] # 数码品类单独日志与存储逻辑 if spider_name digital_spider: spider.logger.info(f【数码专区】{category}{item[title]}) # 服饰品类 elif spider_name clothes_spider: spider.logger.info(f【服饰专区】{category}{item[title]}) # 食品品类 elif spider_name food_spider: spider.logger.info(f【食品专区】{category}{item[title]}) return item基于该逻辑可扩展实现不同爬虫数据写入不同 MySQL 表、导出不同 CSV/Excel 文件、设置不同数据清洗规则。5.2 中间件按 Spider 差异化配置在下载器中间件中判断爬虫名称为不同品类设置不同请求头、代理、超时时间修改middlewares.pypython运行class CustomUserAgentMiddleware: def process_request(self, request, spider): # 数码品类使用专属UA if spider.name digital_spider: request.headers[User-Agent] Mozilla/5.0 (Windows NT 10.0; Win64; x64) Digital Spider # 服饰品类使用专属UA elif spider.name clothes_spider: request.headers[User-Agent] Mozilla/5.0 (Windows NT 10.0; Win64; x64) Clothes Spider # 食品品类使用默认UA else: request.headers[User-Agent] Mozilla/5.0 (Windows NT 10.0; Win64; x64)在settings.py中启用该中间件即可实现不同爬虫请求头差异化配置。六、多 Spider 高级配置与全局参数优先级多 Spider 项目存在多层配置体系全局配置、爬虫类配置、请求级配置存在明确优先级理清优先级规则是避免配置失效的关键。6.1 配置优先级从高到低排序请求级配置request.meta中动态设置的参数优先级最高仅对当前单条请求生效爬虫类属性配置Spider 类内部定义的download_delay、allowed_domains等属性对当前爬虫所有请求生效全局 settings 配置settings.py中的全局参数作为所有爬虫的默认配置框架默认配置Scrapy 内置默认参数优先级最低。6.2 常用参数差异化配置示例表格配置参数全局配置写法单爬虫差异化写法适用场景下载延时settings.py: DOWNLOAD_DELAY 1爬虫类download_delay 3不同站点访问节奏控制域名限制settings.py 无全局配置爬虫类allowed_domains [xxx.com]限制单爬虫访问域名防跨域并发数CONCURRENT_REQUESTS_PER_DOMAIN 2结合中间件动态修改不同站点设置不同并发超时时间DOWNLOAD_TIMEOUT 10中间件按爬虫名称修改网络质量差异站点适配6.3 全局域名白名单管控allowed_domains是单爬虫专属属性用于限制爬虫仅访问指定域名防止爬虫意外爬取第三方链接。多爬虫项目中每个爬虫必须严格配置该属性避免域名混乱、IP 被连带封禁。七、多 Spider 项目常见问题与冲突解决方案7.1 爬虫名称重复现象启动爬虫时报错Duplicate spider name。原因多个爬虫文件中name属性设置为相同值。解决方案保证所有 Spider 的name全局唯一命名建议遵循业务spider规范。7.2 全局配置覆盖差异化配置现象单爬虫设置的download_delay不生效依旧使用全局延时。原因开启了AutoThrottle自适应限流自适应配置优先级高于爬虫类属性。解决方案需要固定延时则关闭自适应限流需要自适应则统一使用全局自适应参数。7.3 多爬虫并行运行资源占用过高现象同时启动多个爬虫后CPU、内存、网络带宽占用激增。解决方案1. 控制并行爬虫数量分批启动2. 降低单爬虫并发数3. 延长下载延时降低请求频率。7.4 公共模块导入失败现象爬虫调用utils工具类时报ModuleNotFoundError。原因未将项目根目录加入 Python 环境变量或__init__.py文件缺失。解决方案保证所有目录下存在__init__.pyLinux/Windows 统一在启动脚本中添加项目路径。7.5 数据混淆、管道数据错乱现象多个爬虫数据混入同一文件 / 同一张数据表。原因管道未区分爬虫名称共用存储目标。解决方案在管道中通过spider.name分流为不同爬虫分配独立存储文件或数据表。八、生产环境多 Spider 项目最佳实践8.1 目录规范最佳实践严格按照「品类 / 站点」拆分 Spider单个爬虫文件代码行数控制在 500 行以内避免再次臃肿通用工具、请求封装、数据清洗、常量配置全部抽离至独立utils目录禁止代码复制粘贴按业务模块拆分中间件、管道例如代理中间件、异常中间件、分流管道分开编写职责清晰。8.2 命名规范爬虫名称业务标识_spider如digital_spider、news_spider见名知意爬虫文件与爬虫名称保持一致便于查找对应代码日志文件、导出文件、数据表结合爬虫名称命名实现数据与日志隔离。8.3 运行调度规范开发调试使用单爬虫启动命令精准定位问题测试环境使用脚本批量启动全量爬虫验证整体流程生产环境根据服务器性能分批启动爬虫禁止无限制并行结合定时任务Crontab/Windows 计划任务实现定时启停。8.4 配置管理规范通用配置统一写入settings.py差异化配置优先使用爬虫类属性反爬严格的站点单独加大延时、降低并发优先保障爬虫存活率禁止在爬虫文件中硬编码账号、密码、接口地址统一抽离至配置文件。8.5 运维监控规范多爬虫分离日志文件单独监控每个爬虫的运行状态、异常数量统计每个品类的数据采集量及时发现单爬虫停爬、数据缺失问题针对高频异常的品类单独优化访问策略与解析规则。