Scrapy 高级主题 Scrapy 高级主题详解 1. Scrapy Middleware (中间件) Scrapy Middleware 是 Scrapy 框架的核心组成部分,它允许你在请求和响应处理过程中插入自定义逻辑。Middleware 分为 Spider Middleware 和 Downloader Middleware 两种类型。 Spider Middleware: 处理 Spider 的输入(请求)和输出(Item)。 Downloader Middleware: 处理 Downloader 的输入(请求)和输出(响应)。 1.
1. Scrapy Middleware (中间件)
Scrapy Middleware 是 Scrapy 框架的核心组成部分,它允许你在请求和响应处理过程中插入自定义逻辑。Middleware 分为 Spider Middleware 和 Downloader Middleware 两种类型。
Spider Middleware: 处理 Spider 的输入(请求)和输出(Item)。
Downloader Middleware: 处理 Downloader 的输入(请求)和输出(响应)。
1.1 Downloader Middleware
Downloader Middleware 位于 Scrapy 引擎和 Downloader 之间,可以用于修改请求 (Request) 和处理响应 (Response)。常见的应用场景包括:
User-Agent 轮换: 防止被网站识别为爬虫。
代理 IP 管理: 通过代理服务器访问目标网站。
重试失败请求: 自动重试由于网络或其他原因失败的请求。
处理压缩响应: 自动解压 gzip 或 deflate 压缩的响应。
代码实践:User-Agent 轮换
首先,创建一个名为 random_user_agent.py 的 middleware 文件:
import random from scrapy.exceptions import NotConfigured class RandomUserAgentMiddleware: def __init__(self, user_agent_list): self.user_agent_list = user_agent_list @classmethod def from_crawler(cls, crawler): user_agent_list = crawler.settings.getlist('USER_AGENT_LIST') if not user_agent_list: raise NotConfigured("USER_AGENT_LIST setting is missing or empty") return cls(user_agent_list) def process_request(self, request, spider): user_agent = random.choice(self.user_agent_list) request.headers['User-Agent'] = user_agent
然后,在 settings.py 中配置 middleware 和 User-Agent 列表:
DOWNLOADER_MIDDLEWARES = { 'your_project.middlewares.RandomUserAgentMiddleware': 543, } USER_AGENT_LIST = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36', # Add more User-Agents here ]
代码详解:
RandomUserAgentMiddleware 类实现了 User-Agent 轮换的逻辑。
from_crawler 方法从 Scrapy settings 中读取 USER_AGENT_LIST。
process_request 方法在每个请求发送前,随机选择一个 User-Agent 并设置到请求头中。
DOWNLOADER_MIDDLEWARES 设置启用该 middleware,数字 543 代表优先级,数字越小优先级越高。
1.2 Spider Middleware
Spider Middleware 位于 Scrapy 引擎和 Spider 之间,可以用于处理 Spider 发出的请求和 Spider 返回的 Item。常见的应用场景包括:
过滤重复请求: 防止重复爬取相同的页面。
修改 Item 数据: 在 Item 被传递到 Item Pipeline 之前对其进行修改。
处理 Spider 的异常: 捕获并处理 Spider 运行过程中发生的异常。
代码实践:过滤重复请求
创建一个名为 duplicate_filter.py 的 middleware 文件:
from scrapy.exceptions import DropItem class DuplicateFilterMiddleware: def __init__(self): self.seen_urls = set() def process_spider_output(self, response, result, spider): for item in result: if isinstance(item, scrapy.Item): url = item.get('url') #假设你的Item中有url字段 if url in self.seen_urls: raise DropItem(f"Duplicate item found: {url}") else: self.seen_urls.add(url) yield item else: yield item
在 settings.py 中配置 middleware:
SPIDER_MIDDLEWARES = { 'your_project.middlewares.DuplicateFilterMiddleware': 543, }
代码详解:
DuplicateFilterMiddleware 类实现了过滤重复请求的逻辑。
process_spider_output 方法在 Spider 返回 Item 时被调用。
如果 Item 的 URL 已经存在于 seen_urls 集合中,则抛出 DropItem 异常,阻止 Item 被传递到 Item Pipeline。
如果 Item 的 URL 不存在于 seen_urls 集合中,则将其添加到集合中,并允许 Item 继续传递。
2. Item Pipeline
Item Pipeline 负责处理 Spider 提取的 Item 数据。它是一个由多个组件组成的管道,每个组件执行特定的数据处理任务。常见的应用场景包括:
数据清洗: 去除 Item 中的无效数据。
数据验证: 验证 Item 中的数据是否符合规范。
数据存储: 将 Item 存储到数据库或文件中。
图片下载: 下载 Item 中包含的图片。
代码实践:存储数据到 MongoDB
首先,安装 pymongo 库:
pip install pymongo
然后,创建一个名为 mongodb_pipeline.py 的 pipeline 文件:
import pymongo class MongoDBPipeline: def __init__(self, mongo_uri, mongo_db): self.mongo_uri = mongo_uri self.mongo_db = mongo_db @classmethod def from_crawler(cls, crawler): return cls( mongo_uri=crawler.settings.get('MONGO_URI'), mongo_db=crawler.settings.get('MONGO_DATABASE') ) def open_spider(self, spider): self.client = pymongo.MongoClient(self.mongo_uri) self.db = self.client[self.mongo_db] def close_spider(self, spider): self.client.close() def process_item(self, item, spider): self.db['your_collection'].insert_one(dict(item)) return item
在 settings.py 中配置 pipeline 和 MongoDB 连接信息:
ITEM_PIPELINES = { 'your_project.pipelines.MongoDBPipeline': 300, } MONGO_URI = 'mongodb://localhost:27017' MONGO_DATABASE = 'your_database'
代码详解:
MongoDBPipeline 类实现了将 Item 存储到 MongoDB 的逻辑。
from_crawler 方法从 Scrapy settings 中读取 MongoDB 连接信息。
open_spider 方法在 Spider 启动时连接到 MongoDB。
close_spider 方法在 Spider 关闭时关闭 MongoDB 连接。
process_item 方法将 Item 转换为字典,并将其插入到指定的 MongoDB 集合中。
ITEM_PIPELINES 设置启用该 pipeline,数字 300 代表优先级,数字越小优先级越高。
3. Scrapy Extension (扩展)
Scrapy Extension 允许你扩展 Scrapy 的功能,例如添加自定义命令、信号处理程序和监控工具。
代码实践:自定义日志扩展
创建一个名为 custom_log.py 的 extension 文件:
from scrapy import signals from scrapy.exceptions import NotConfigured import logging class CustomLogExtension: def __init__(self, stats): self.stats = stats self.logger = logging.getLogger(__name__) @classmethod def from_crawler(cls, crawler): if not crawler.settings.getbool('CUSTOM_LOG_ENABLED'): raise NotConfigured ext = cls(crawler.stats) crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened) crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed) return ext def spider_opened(self, spider): self.logger.info(f"Spider {spider.name} opened") def spider_closed(self, spider): self.logger.info(f"Spider {spider.name} closed, scraped {self.stats.get_value('item_scraped_count', 0)} items")
在 settings.py 中配置 extension:
EXTENSIONS = { 'your_project.extensions.custom_log.CustomLogExtension': 500, } CUSTOM_LOG_ENABLED = True
代码详解:
CustomLogExtension 类实现了自定义日志的逻辑。
from_crawler 方法从 Scrapy settings 中读取 CUSTOM_LOG_ENABLED 配置,并连接到 spider_opened 和 spider_closed 信号。
spider_opened 方法在 Spider 启动时记录日志。
spider_closed 方法在 Spider 关闭时记录日志,并包含爬取的 Item 数量。
EXTENSIONS 设置启用该 extension,数字 500 代表优先级。
CUSTOM_LOG_ENABLED 设置是否启用该 extension。
4. Scrapy Signals (信号)
Scrapy Signals 提供了一种在 Scrapy 引擎内部发生的事件时执行自定义操作的机制。你可以通过信号处理程序来监听这些事件,并在事件发生时执行相应的代码。
常见的 Scrapy 信号:
spider_opened: Spider 启动时触发。
spider_closed: Spider 关闭时触发。
item_scraped: Item 被成功提取时触发。
item_dropped: Item 被丢弃时触发。
request_scheduled: 请求被调度时触发。
response_received: 响应被接收时触发。
代码实践:使用信号统计 Item 数量
在 Spider 中连接信号:
import scrapy from scrapy import signals class MySpider(scrapy.Spider): name = "myspider" start_urls = ["http://example.com"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.item_count = 0 self.crawler.signals.connect(self.item_scraped, signal=signals.item_scraped) def parse(self, response): # 提取 Item item = {'title': response.css('title::text').get()} yield item def item_scraped(self, item, response, spider): self.item_count += 1 self.logger.info(f"Scraped item count: {self.item_count}")
代码详解:
在 Spider 的 __init__ 方法中,使用 self.crawler.signals.connect() 连接到 item_scraped 信号。
item_scraped 方法在每次 Item 被成功提取时被调用,并更新 item_count 变量。
self.logger.info() 用于记录 Item 数量。
5. Scrapy AutoThrottle (自动限速)
Scrapy AutoThrottle 扩展可以根据目标网站的响应速度自动调整爬取速度,以避免对网站造成过大的压力。
启用 AutoThrottle:
在 settings.py 中配置 AutoThrottle:
AUTOTHROTTLE_ENABLED = True AUTOTHROTTLE_START_DELAY = 5.0 # 初始下载延迟 (秒) AUTOTHROTTLE_MAX_DELAY = 60.0 # 最大下载延迟 (秒) AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0 # 目标并发请求数 AUTOTHROTTLE_DEBUG = False # 启用调试模式
配置详解:
AUTOTHROTTLE_ENABLED: 启用 AutoThrottle 扩展。
AUTOTHROTTLE_START_DELAY: 初始下载延迟,单位为秒。
AUTOTHROTTLE_MAX_DELAY: 最大下载延迟,单位为秒。
AUTOTHROTTLE_TARGET_CONCURRENCY: 目标并发请求数。
AUTOTHROTTLE_DEBUG: 启用调试模式,可以在日志中查看 AutoThrottle 的调整过程。
6. 使用 CrawlSpider 和 Rule 进行复杂页面抓取
对于结构化的网站,CrawlSpider 和 Rule 可以帮助你更方便地定义爬取规则。CrawlSpider 继承自 Spider,并提供了一个 rules 属性,用于定义爬取规则。Rule 定义了如何从页面中提取链接并继续爬取。
代码实践:爬取博客列表和文章内容
import scrapy from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class BlogSpider(CrawlSpider): name = "blogspider" allowed_domains = ["example.com"] # 替换为你的域名 start_urls = ["http://example.com"] # 替换为你的起始URL rules = ( Rule(LinkExtractor(allow=r'/blog/\d+/'), callback='parse_item', follow=True), # 匹配博客文章的链接 Rule(LinkExtractor(allow=r'/page/\d+/'), follow=True), # 匹配分页链接,只follow不parse ) def parse_item(self, response): item = { 'title': response.css('h1::text').get(), 'content': response.css('.entry-content p::text').getall(), 'url': response.url, } yield item
代码详解:
BlogSpider 继承自 CrawlSpider。
allowed_domains 定义了允许爬取的域名。
start_urls 定义了起始 URL。
rules 定义了两个爬取规则:
第一个规则使用 LinkExtractor 提取符合 /blog/\d+/ 模式的链接,并使用 parse_item 方法解析页面。follow=True 表示提取到的链接会被继续爬取。
第二个规则使用 LinkExtractor 提取符合 /page/\d+/ 模式的链接,只进行 follow,不进行解析。
parse_item 方法解析博客文章的标题、内容和 URL,并返回 Item。
7. Scrapy 的 Graph TD 图示 (mermaid 语法)
图示解释:
Scrapy Engine: Scrapy 引擎是整个爬虫框架的核心,负责控制各个组件的流程。
Scheduler: 调度器负责管理待爬取的请求队列。
Downloader Middleware (Request): 处理请求的 Downloader 中间件。
Downloader: 下载器负责下载网页内容。
Downloader Middleware (Response): 处理响应的 Downloader 中间件。
Spider Middleware (Request): 处理请求的 Spider 中间件。
Spider: Spider 负责解析网页内容,提取数据,并生成新的请求。
Spider Middleware (Item): 处理 Item 的 Spider 中间件。
Item Pipeline: Item Pipeline 负责处理 Spider 提取的 Item 数据。
总结
本文详细介绍了 Scrapy 的一些高级主题,包括 Middleware、Item Pipeline、Extension、Signals、AutoThrottle 以及 CrawlSpider 和 Rule 的使用。掌握这些高级特性可以帮助你构建更强大、更灵活的爬虫,应对更复杂的爬取需求。通过代码实践和图示,希望你能更好地理解和应用这些高级主题。