You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 09:57:19