Scrapy:如何使用LinkExtractor跳过已抓取的链接
问题解答
你的需求完全可行,下面给你几种高效的实现方式:
一、优先用Scrapy内置去重(最省心)
Scrapy自带请求去重功能,默认通过RFPDupeFilter自动过滤已经请求过的URL,根本不用自己写哈希存储和校验逻辑。只要确保:
- 项目
settings.py里DUPEFILTER_CLASS用默认的scrapy.dupefilters.RFPDupeFilter(默认就是这个,不用改) - 请求的
dont_filter参数别设为True(默认是False,会自动走过滤)
要是只是不想重复抓取,这个方案是最优解,省得自己维护链接列表。
二、自定义LinkExtractor过滤已抓取链接(适合需要保留自定义链接列表的场景)
如果必须基于你自己维护的links.txt来过滤,用LinkExtractor的process_value参数就能实现,提取链接时直接跳过已抓取的:
1. 初始化时缓存已抓取哈希到内存
把links.txt里的哈希在爬虫启动时读一次存到内存,避免每次处理请求都读文件(你代码里TODO的部分):
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.crawled_hashes = set(self.read_hash_list(self.filename)) # 用set存,查询比列表快多了
2. 写链接过滤函数
定义一个函数,检查提取到的URL的哈希是否在已抓取列表里,不在就返回原URL,否则返回None(返回None会被LinkExtractor跳过):
def filter_crawled_links(self, url): url_hash = hashlib.md5(url.encode('utf-8')).hexdigest() return url if url_hash not in self.crawled_hashes else None
3. 修改Rule里的LinkExtractor
把process_value参数指向上面的过滤函数:
rules = [ Rule( LinkExtractor( deny_domains='www.youtube.com', process_value=self.filter_crawled_links ), callback='parse_item', follow=True ) ]
4. 更新内存缓存
在check_dupes里,新增哈希后要同步更新内存里的crawled_hashes,避免后续判断出错:
def check_dupes(self, response): new = False new_hash = hashlib.md5(response.url.encode('utf-8')).hexdigest() if new_hash not in self.crawled_hashes: self.write_hash_list(response.url, self.filename) self.crawled_hashes.add(new_hash) # 同步内存缓存 new = True return new
三、优化你原有代码的核心问题
你原来的代码每次check_dupes都读一次文件,性能拉胯,改成初始化时读一次内存缓存,并用set存储哈希(查询速度远快于列表),能大幅提升爬虫效率。
如果不需要自定义存储已抓取链接,强烈建议用第一种内置去重的方式,代码更简洁,性能也更稳定。
内容的提问来源于stack exchange,提问作者old_hands
相关产品推荐
相关产品推荐

