连接池已满,通过Selenium和Python放弃与ThreadPoolExecutor和多个无头浏览器的连接

连接池已满,通过Selenium和Python放弃与ThreadPoolExecutor和多个无头浏览器的连接,python,selenium,threadpool,threadpoolexecutor,urllib3,Python,Selenium,Threadpool,Threadpoolexecutor,Urllib3,我正在使用selenium==3.141.0,python3.6.7,chromedriver 2.44编写一些自动化软件 大多数逻辑都可以由单个浏览器实例执行,但对于某些部分,我必须启动10-20个实例才能获得适当的执行速度 一旦涉及到由ThreadPoolExecutor执行的部分,浏览器交互将开始抛出此错误: WARNING|05/Dec/2018 17:33:11|connectionpool|_put_conn|274|Connection pool is full, discardi

我正在使用
selenium==3.141.0
python3.6.7
chromedriver 2.44
编写一些自动化软件

大多数逻辑都可以由单个浏览器实例执行,但对于某些部分,我必须启动10-20个实例才能获得适当的执行速度

一旦涉及到由
ThreadPoolExecutor
执行的部分,浏览器交互将开始抛出此错误:

WARNING|05/Dec/2018 17:33:11|connectionpool|_put_conn|274|Connection pool is full, discarding connection: 127.0.0.1
WARNING|05/Dec/2018 17:33:11|connectionpool|urlopen|662|Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProtocolError('Connection aborted.', RemoteDisconnected('Remote end closed connection without response',))': /session/119df5b95710793a0421c13ec3a83847/url
WARNING|05/Dec/2018 17:33:11|connectionpool|urlopen|662|Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fcee7ada048>: Failed to establish a new connection: [Errno 111] Connection refused',)': /session/119df5b95710793a0421c13ec3a83847/url
相关代码:

def init_chromedriver(cls):
    try:
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_argument('--headless')
        chrome_options.add_argument(f"user-agent={Utils.get_random_browser_agent()}")
        prefs = {"profile.managed_default_content_settings.images": 2}
        chrome_options.add_experimental_option("prefs", prefs)

        driver = webdriver.Chrome(driver_paths['chrome'],
                                       chrome_options=chrome_options,
                                       service_args=['--verbose', f'--log-path={bundle_dir}/selenium/chromedriver.log'])
        driver.implicitly_wait(10)

        return driver
    except Exception as e:
        logger.error(e)
with ProfileParser(acc) as pparser:
        pparser.collect_user_info(posts[0])
ProfileParser
实例化webdriver并执行一些页面交互。我认为交互本身并不相关,因为没有
ThreadPoolExecutor
,一切都可以运行。 然而,简言之:

class ProfileParser(object):
    def __init__(self, acc):
        self.driver = Utils.init_chromedriver()
    def __exit__(self, exc_type, exc_val, exc_tb):
        Utils.shutdown_chromedriver(self.driver)
        self.driver = None

    collect_user_info(post_url)
           self.driver.get(post_url)
           profile_url = self.driver.find_element_by_xpath('xpath_here')]').get_attribute('href')
ThreadPoolExecutor
中运行时,上面的错误出现在此时
self.driver.find\u element\u by_xpath
self.driver.get

这起作用了:

def init_chromedriver(cls):
    try:
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_argument('--headless')
        chrome_options.add_argument(f"user-agent={Utils.get_random_browser_agent()}")
        prefs = {"profile.managed_default_content_settings.images": 2}
        chrome_options.add_experimental_option("prefs", prefs)

        driver = webdriver.Chrome(driver_paths['chrome'],
                                       chrome_options=chrome_options,
                                       service_args=['--verbose', f'--log-path={bundle_dir}/selenium/chromedriver.log'])
        driver.implicitly_wait(10)

        return driver
    except Exception as e:
        logger.error(e)
with ProfileParser(acc) as pparser:
        pparser.collect_user_info(posts[0])
这些选项不起作用:
连接池错误

更新:

def init_chromedriver(cls):
    try:
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_argument('--headless')
        chrome_options.add_argument(f"user-agent={Utils.get_random_browser_agent()}")
        prefs = {"profile.managed_default_content_settings.images": 2}
        chrome_options.add_experimental_option("prefs", prefs)

        driver = webdriver.Chrome(driver_paths['chrome'],
                                       chrome_options=chrome_options,
                                       service_args=['--verbose', f'--log-path={bundle_dir}/selenium/chromedriver.log'])
        driver.implicitly_wait(10)

        return driver
    except Exception as e:
        logger.error(e)
with ProfileParser(acc) as pparser:
        pparser.collect_user_info(posts[0])
我找到了一个临时解决方案(它不会使这个初始问题无效)——在
ProfileParser
类之外实例化
webdriver
。我不知道它为什么有效,但首字母无效。我想原因在某些语言细节中? 感谢您的回答,但是问题似乎并不在于
ThreadPoolExecutor
max\u workers
限制-正如您在我尝试提交单个实例的其中一个选项中看到的,它仍然不起作用

当前解决方法:

futures = []
with ThreadPoolExecutor(max_workers=10) as executor:
    for p in posts:
        driver = Utils.init_chromedriver()
        futures.append({
            'future': executor.submit(collect_user_info, driver, acc, p),
            'driver': driver
        })

for f in futures:
    f['future'].done()
    Utils.shutdown_chromedriver(f['driver'])
请查看您的错误

ProtocolError('Connection aborted.', 
  RemoteDisconnected('Remote end closed connection without response',))

'NewConnectionError('<urllib3.connection.HTTPConnection object at >: 
   Failed to establish a new connection: [Errno 111] Connection refused',)':
ProtocolError('Connection aborted'),
RemoteDisconnected('远程端关闭连接,无响应',))
“NewConnectionError(”:
无法建立新连接:[Errno 111]连接被拒绝',)':
该错误是因为您的多重连接速度太快,可能是服务器关闭或服务器阻止了您的请求

此错误消息

WARNING|05/Dec/2018 17:33:11|connectionpool|_put_conn|274|Connection pool is full, discarding connection: 127.0.0.1
WARNING|05/Dec/2018 17:33:11|connectionpool|urlopen|662|Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProtocolError('Connection aborted.', RemoteDisconnected('Remote end closed connection without response',))': /session/119df5b95710793a0421c13ec3a83847/url
WARNING|05/Dec/2018 17:33:11|connectionpool|urlopen|662|Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fcee7ada048>: Failed to establish a new connection: [Errno 111] Connection refused',)': /session/119df5b95710793a0421c13ec3a83847/url

是使用线程池异步执行调用的子类。当与未来关联的可调用对象等待另一个未来的结果时,可能会发生死锁

class concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=())
  • 一个Executor子类,它使用最多包含max_工作线程的池异步执行调用
  • 初始值设定项是一个可选的可调用项,在每个工作线程开始时调用;initargs是传递给初始值设定项的参数元组。如果初始值设定项引发异常,则所有当前挂起的作业都将引发BrokenThreadPool,以及任何向该池提交更多作业的尝试
  • 从3.5版开始:如果没有或没有指定max_workers,则默认为计算机上的处理器数量乘以5,假设ThreadPoolExecutor经常用于重叠I/O而不是CPU工作,并且Worker数量应高于ProcessPoolExecutor的Worker数量
  • 从3.6版开始:添加了thread_name_prefix参数,允许用户控制线程。为便于调试,池创建的工作线程的线程名称
  • 从版本3.7开始:添加了初始值设定项和initargs参数
根据您的问题,当您尝试启动10-20个实例时,默认的连接池大小10在您的案例中似乎不够,这是硬编码的

此外,@edleave在讨论中提到:

在请求代码中,似乎没有任何对象是正常的。如果
\u get\u conn()
从池中获取None,它只会创建一个新连接。但奇怪的是,它应该从所有这些None对象开始,而且_put_conn()不够聪明,无法用连接替换None

然而,合并已经解决了这个问题

解决方案 增加默认连接池大小10,这是以前硬编码的,现在是可配置的,将解决您的问题


更新 根据您的评论更新…提交单个实例,结果相同。。。。根据讨论中的@meferguson84:

我将代码插入到它挂载适配器的地方,只是为了处理池大小,看看它是否有所不同。我发现队列中充满了非类型对象,实际上载连接是列表中的最后一项。这个列表有10个条目(这很有意义)。没有意义的是,池的unfinished_tasks参数是11。当队列本身只有11个项目时,这怎么可能呢?另外,队列中充满了非类型对象,并且我们使用的连接是列表中的最后一项,这正常吗

在您的用例中,这听起来也是一个可能的原因。这听起来可能有些多余,但您仍可以执行以下几个特殊步骤:

  • 通过IDE清理项目工作区,并仅使用所需的依赖项重建项目
  • (仅限Windows OS)在执行测试套件之前和之后,使用该工具清除所有操作系统杂务
  • (仅限LinuxOS)在执行测试套件之前和之后

嗨!谢谢你的详细回复。然而,如果再看看我的帖子,我试图提交一个实例,结果是一样的。此外-即使我提交的实例数超过
max\u workers
参数限制,
ThreadPoolExecutor
假设将其作为一个队列处理,同时执行的实例数为
max\u workers
。更新了question@user1935987我添加了一个答案更新。让我知道情况。我真的认为问题不在于遗嘱执行人本身。原因-我刚刚尝试了另一种方法,在与
ThreadPoolExecutor
和我相同的类中实例化
webdriver