如何修复Scrapy用Selenium中间件爬取多URL时的连接重置错误
问题原因
报错核心是Selenium中间件的浏览器实例生命周期逻辑完全写错:第一个请求处理完成后你就主动调用self.driver.quit()关闭了浏览器进程,后续跟进的详情页请求到达中间件时,已经没有运行的ChromeDriver服务监听端口,直接触发WinError 10061连接被拒绝的错误。
你觉得和cb_kwargs有关纯属巧合,这个参数本身不会触发任何Selenium相关的错误,之前不用参数能跑,本质是你当时的代码要么没真正发出后续详情页请求,要么链接拼接错误导致请求没走到Selenium处理逻辑。
代码里的具体问题
- 错误1:在
process_request方法中执行self.driver.quit()。Scrapy的中间件是全局单例,初始化时只创建一次浏览器实例,你每处理一个请求就关一次浏览器,第二个请求进来当然连不上driver。 - 错误2:详情页链接拼接逻辑错误。你用
"https://steamdb.info".join(href)的写法不符合Python字符串join方法的逻辑,会把域名插到href字符串的每个字符中间,生成完全无效的URL。正确的相对路径拼接应该用Scrapy自带的response.urljoin()方法,不需要自己手写字符串拼接。 - 错误3:写了裸
except: pass吞掉所有异常,访问页面时不管出什么错都直接跳过,没有任何日志输出,调试时根本定位不到问题点。
修正后的代码
Middleware.py 修正版
from scrapy.http import HtmlResponse import undetected_chromedriver as uc class SeleniumMiddleWare(object): def __init__(self): path = "G:/Downloads/chromedriver.exe" options = uc.ChromeOptions() options.headless = True chrome_prefs = {} options.experimental_options["prefs"] = chrome_prefs chrome_prefs["profile.default_content_settings"] = {"images": 2} chrome_prefs["profile.managed_default_content_settings"] = {"images": 2} self.driver = uc.Chrome( options=options, use_subprocess=True, driver_executable_path=path ) # 绑定爬虫关闭信号,爬虫全部跑完再销毁浏览器 def spider_closed(self, spider): self.driver.quit() def process_request(self, request, spider): self.driver.get(request.url) content = self.driver.page_source # 删掉此处的self.driver.quit(),绝对不能每个请求关一次浏览器 return HtmlResponse( request.url, encoding='utf-8', body=content, request=request ) def process_response(self, request, response, spider): return response
Spider.py 修正版
import scrapy class SeleniumSpider(scrapy.Spider): name = 'steamdb' #allowed_domains = ['steamdb.info'] start_urls = ['https://steamdb.info/graph/'] def parse(self, response): table = response.xpath('//*[@id="table-apps"]/tbody') rows = table.css('tr.app') for element in rows: # 用Scrapy自带的urljoin处理相对路径,不要自己手拼 link = response.urljoin(element.css('a::attr(href)').get()) name = element.css('a::text').get() game_info = {"Link": link, "Name": name} yield scrapy.Request( url=link, callback=self.parse_info, cb_kwargs=dict(game_info=game_info) ) def parse_info(self, response, game_info): game_info["sales"] = response.xpath('//*[@id="graphs"]/div[5]/div[2]/ul/li[1]/strong/span/text()').getall() yield game_info
注意事项
- 浏览器实例的生命周期要和爬虫绑定:初始化放在中间件的
__init__方法,销毁放在spider_closed回调里,不要在单个请求的处理逻辑里开关浏览器,不然不仅会报连接错误,爬取效率也会极低。 - 不要写无任何处理的裸异常捕获,如果需要加容错,至少要打印对应的错误日志,不然出问题根本找不到原因。
- 爬取SteamDB这类有反爬的站点时,记得在settings.py里配置好下载延迟,不要请求太频繁,不然容易被封IP。
内容的提问来源于stack exchange,提问作者Baraa Zaid
相关产品推荐
相关产品推荐

