如何通过Scrapy Middlewares阻止重复请求已爬取过的页面?
实现思路与代码示例
要实现在发送请求前阻止已爬取URL的请求,你需要使用Scrapy的Downloader Middleware,并在process_request方法中处理——这个方法会在请求发送前触发,正好匹配你的需求。
步骤1:存储已爬取URL
建议用一个文本文件(比如visited_urls.txt)记录所有已爬取的赛事URL,每行一个URL。如果你的CSV里已经存储了赛事URL,也可以从CSV中提取并初始化这个列表。
步骤2:编写Downloader Middleware
在项目的middlewares.py中添加以下代码:
from scrapy.exceptions import IgnoreRequest from urllib.parse import urlparse, parse_qs, urlencode, urlunparse def normalize_url(url): """标准化URL,避免因参数顺序不同误判为新URL""" parsed = urlparse(url) query_params = parse_qs(parsed.query) # 按参数名排序,生成统一格式的查询字符串 sorted_query = urlencode(sorted(query_params.items()), doseq=True) return urlunparse(parsed._replace(query=sorted_query)) class FilterVisitedURLsMiddleware: def __init__(self): # 初始化已爬URL集合(用set提升查找效率) self.visited_urls = set() # 从文件加载已爬URL try: with open('visited_urls.txt', 'r', encoding='utf-8') as f: for line in f: url = line.strip() if url: self.visited_urls.add(normalize_url(url)) except FileNotFoundError: # 首次运行文件不存在,跳过加载 pass def process_request(self, request, spider): # 标准化当前请求的URL normalized_req_url = normalize_url(request.url) # 检查是否已爬取,是则忽略请求 if normalized_req_url in self.visited_urls: raise IgnoreRequest(f"跳过已爬URL: {request.url}") # 未爬取则继续处理 return None def process_response(self, request, response, spider): # 请求成功后,将标准化后的URL写入文件 normalized_req_url = normalize_url(request.url) if normalized_req_url not in self.visited_urls: self.visited_urls.add(normalized_req_url) with open('visited_urls.txt', 'a', encoding='utf-8') as f: f.write(normalized_req_url + '\n') return response
步骤3:启用中间件
在项目的settings.py中配置DOWNLOADER_MIDDLEWARES,添加你编写的中间件:
DOWNLOADER_MIDDLEWARES = { # 替换成你的项目名,比如my_spider.middlewares... 'your_project_name.middlewares.FilterVisitedURLsMiddleware': 543, # 保留其他默认中间件配置(如果有) }
优先级数字越小,中间件越先执行。设置为543是为了让它在大部分默认中间件之后、下载操作之前执行。
注意事项
- 如果要从CSV提取已爬URL,可以在
__init__方法中用csv模块读取CSV文件,提取对应URL列并加入self.visited_urls。 IgnoreRequest异常会被Scrapy自动捕获,不会记录为错误日志,只会跳过该请求。- 标准化URL的步骤很重要,避免因URL参数顺序不同(比如
runSeqNumber在不同位置)导致重复爬取。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

