Scrapy的start_requests含多个start_url时仅爬取第一页如何解决
问题根因分析
- Scrapy默认采用异步并发调度模式,所有请求默认优先级相同,多个初始start_url提交后,不同auction_id的列表页、详情页、翻页请求会混合进入调度队列并行执行
- 该目标站点对单客户端会话存在访问限制:同一时间仅支持处理一个拍卖分类(auction_id)的请求,多分类并行请求时会被站点拦截,导致翻页请求无法正常返回有效内容,因此每个初始url仅能爬取第一页
- 爬虫逻辑本身无语法错误,其他站点运行正常是因为对应站点没有类似的会话/分类并行访问限制
解决方案
方案1:单分类串行爬取(完全满足处理完第一个start_url全量再处理下一个的需求)
核心逻辑是通过Spider实例变量存储待处理任务队列,仅在当前分类所有页面爬取完成后再提交下一个分类的初始请求,修改后的完整代码如下:
import scrapy import re import hashlib from lotbot.items import LotbotItem from scrapy import Request class VendunotarisSpider(scrapy.Spider): name = "vendurotterdam" allowed_domains = ['vendurotterdam.nl'] def __init__(self, *args, **kwargs): super(VendunotarisSpider, self).__init__(*args, **kwargs) # 存储所有待处理的拍卖任务 self.task_queue = [[1777, 'https://vendurotterdam.nl/catalogus/?auction_id=1229'], [1761, 'https://vendurotterdam.nl/catalogus/?auction_id=1224'], [1731, 'https://vendurotterdam.nl/catalogus/?auction_id=1225']] def start_requests(self): # 启动时仅提交第一个任务 if self.task_queue: first_task = self.task_queue.pop(0) yield Request(url=first_task[1], callback=self.parse_list, cb_kwargs=dict(sale=first_task[0])) def parse_list(self, response, sale): lot_page_links = response.css('div.items div#objectinfo div.col-lg-12.col-xs-12 a') yield from response.follow_all(lot_page_links, self.parse_lot, cb_kwargs=dict(sale=sale)) pagination_links = response.xpath("//div/a[contains(text(), 'Volgende')]") if pagination_links: # 有下一页,继续提交当前分类的翻页请求 yield from response.follow_all(pagination_links, self.parse_list, cb_kwargs=dict(sale=sale)) else: # 当前分类爬取完成,提交下一个待处理任务 if self.task_queue: next_task = self.task_queue.pop(0) yield Request(url=next_task[1], callback=self.parse_list, cb_kwargs=dict(sale=next_task[0])) def parse_lot(self, response, sale): def extract_with_css(query): return response.css(query).get() item = LotbotItem() item['source_url'] = response.request.url item['catalog_url'] = response.request.url item['productId'] = response.xpath('//table/tr[1]/td[3]/b/text()').get() item['name'] = extract_with_css('h1.item-detail-title::text') if extract_with_css('div.modal-body') is None: item['description'] = extract_with_css('span.title::text') + ' ' + response.xpath('/html/body/div/div/div[2]/section/div[1]/div/div/div[2]/div/div[2]/div[2]/text()').getall()[2] else: item['description'] = extract_with_css('div.modal-body::text') + ' ' + response.css('div.col-lg-6.col-md-6.col-sm-6.detail-right div ::text').getall()[8] relative_img_urls = response.css('div.slider_image div a::attr(href)').get() item['image_urls'] = [relative_img_urls] item['estimates'] = response.css('div.box-black.pull-right::text').get() item['price_result'] = response.xpath('//td[contains(@id,"highest_bid_td")]/text()').get() checksum_string = str(sale) + response.request.url item['checksum'] = hashlib.md5(checksum_string.encode('utf-8')).hexdigest() item['sale_id'] = sale yield item def url_join(self, urls, response): joined_urls = [] for url in urls: joined_urls.append(response.urljoin(url)) return joined_urls
方案2:并发适配方案(无需修改爬虫逻辑,仅调整配置)
如果不想用串行模式牺牲爬取效率,可以在settings.py中添加以下配置适配站点限制:
# 限制同一域名下并发请求数为1,所有请求按顺序执行 CONCURRENT_REQUESTS_PER_DOMAIN = 1 # 设置请求间隔,避免触发站点频率限制 DOWNLOAD_DELAY = 1 # 启用cookie保持同一会话 COOKIES_ENABLED = True
额外排查建议
可开启Scrapy的Debug日志模式,查看多start_url场景下翻页请求的响应状态码和返回内容,确认是否存在站点拦截类的响应。
内容的提问来源于stack exchange,提问作者djdaft3000
相关产品推荐
相关产品推荐

