Я пишу некоторое программное обеспечение для автоматизации, используя selenium==3.141.0
, python 3.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)
соответствующий код:
ProfileParser
создает экземпляр веб-драйвера и выполняет несколько взаимодействий страниц.Я полагаю, что сами взаимодействия не имеют отношения, потому что все работает без 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_element_by_xpath
или в self.driver.get
, это работает:
with ProfileParser(acc) as pparser:
pparser.collect_user_info(posts[0])
эти опции не работают: (connectionpool errors
)
futures = []
#one worker, one future
with ThreadPoolExecutor(max_workers=1) as executor:
with ProfileParser(acc) as pparser:
futures.append(executor.submit(pparser.collect_user_info, posts[0]))
#10 workers, multiple futures
with ThreadPoolExecutor(max_workers=10) as executor:
for p in posts:
with ProfileParser(acc) as pparser:
futures.append(executor.submit(pparser.collect_user_info, p))
ОБНОВЛЕНИЕ:
Я нашел временное решение (которое не лишает законной силы этот первоначальный вопрос) - создать экземпляр webdriver
вне ProfileParser
класса.Не знаю, почему это работает, но начальная - нет.Я полагаю, причина в какой-то языковой специфике?Спасибо за ответы, однако, похоже, что проблема не в пределе ThreadPoolExecutor
max_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'])