如何让Scrapy按顺序抓取指定列表中的链接?
解决Scrapy爬虫链接抓取顺序问题
Scrapy的异步并发特性会导致请求完成顺序和发送顺序不一致,要保证rankings["Men's Pound-for-Pound"]中的链接按原顺序抓取并输出结果,有两种可行方案:
方案一:限制并发数为1(简单但效率低)
通过强制Scrapy一次只处理一个请求,确保链接按顺序被抓取。可以在爬虫类中添加自定义配置,或者修改项目的settings.py:
修改爬虫类代码
class FighterSpiderSpider(scrapy.Spider): name = 'fighter_spider' allowed_domains = ['www.ufc.com.br'] start_urls = ['https://www.ufc.com.br/rankings'] # 添加自定义设置,限制并发 custom_settings = { 'CONCURRENT_REQUESTS': 1, 'CONCURRENT_REQUESTS_PER_DOMAIN': 1 } def parse(self, response): all_rankings = response.css('div.view-grouping').getall() champions = {Selector(text=x).css('div.view-grouping div.info h4::text').get().strip() : Selector(text=x).css('a::attr(href)').get() for x in all_rankings} rankings = {Selector(text=x).css('div.info h4::text').get().strip() : Selector(text=x).css('a::attr(href)').getall() for x in all_rankings} if self.ranking == "p4p male": for link in rankings["Men's Pound-for-Pound"]: yield response.follow(link, callback=self.parse_date)
这种方法适合数据量较小的场景,爬取速度会变慢,但能直接保证顺序。
方案二:携带顺序索引,最终排序输出(高效推荐)
不改变并发效率,给每个请求带上原列表中的索引,在数据处理阶段记录索引,最后通过Pipeline按索引排序输出。
步骤1:修改parse方法,传递索引
def parse(self, response): all_rankings = response.css('div.view-grouping').getall() champions = {Selector(text=x).css('div.view-grouping div.info h4::text').get().strip() : Selector(text=x).css('a::attr(href)').get() for x in all_rankings} rankings = {Selector(text=x).css('div.info h4::text').get().strip() : Selector(text=x).css('a::attr(href)').getall() for x in all_rankings} if self.ranking == "p4p male": # 用enumerate获取每个链接的索引,存入meta for idx, link in enumerate(rankings["Men's Pound-for-Pound"]): yield response.follow(link, callback=self.parse_date, meta={'order': idx})
步骤2:在parse_date中记录索引
def parse_date(self, response): # 从meta中取出顺序索引 order = response.meta['order'] # 提取页面数据(示例,根据实际需求调整) fighter_info = { 'order': order, 'name': response.css('h1.fighter-name::text').get().strip(), # 其他需要抓取的字段... } yield fighter_info
步骤3:编写Pipeline排序并保存
在项目的pipelines.py中添加排序Pipeline:
import json class SortByOrderPipeline: def __init__(self): self.item_list = [] def process_item(self, item, spider): self.item_list.append(dict(item)) return item def close_spider(self, spider): # 按order字段从小到大排序 sorted_items = sorted(self.item_list, key=lambda x: x['order']) # 保存到JSON文件(可根据需求改为其他格式) with open('sorted_fighters.json', 'w', encoding='utf-8') as f: json.dump(sorted_items, f, ensure_ascii=False, indent=2)
步骤4:启用Pipeline
在settings.py中配置:
ITEM_PIPELINES = { 'your_project_name.pipelines.SortByOrderPipeline': 300, }
这种方法保留了Scrapy的并发优势,同时能确保最终输出结果按原链接顺序排列,适合大多数场景。
内容的提问来源于stack exchange,提问作者Samuel Martins
相关产品推荐
相关产品推荐

