3. Scrapy 高级主题


文档摘要

Scrapy 高级主题 Scrapy 高级主题详解 1. Scrapy Middleware (中间件) Scrapy Middleware 是 Scrapy 框架的核心组成部分,它允许你在请求和响应处理过程中插入自定义逻辑。Middleware 分为 Spider Middleware 和 Downloader Middleware 两种类型。 Spider Middleware: 处理 Spider 的输入(请求)和输出(Item)。 Downloader Middleware: 处理 Downloader 的输入(请求)和输出(响应)。 1.

3. Scrapy 高级主题

Scrapy 高级主题详解

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_openedspider_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 进行复杂页面抓取

对于结构化的网站,CrawlSpiderRule 可以帮助你更方便地定义爬取规则。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 的使用。掌握这些高级特性可以帮助你构建更强大、更灵活的爬虫,应对更复杂的爬取需求。通过代码实践和图示,希望你能更好地理解和应用这些高级主题。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U