Краткое описание
Я обрабатываю много страниц с селеном последовательно, но для повышения производительности я решил распараллелить обработку - разделить страницы на несколько потоков (Это можно сделать, поскольку страницы независимы друг от друга).
Вот упрощенный код:
def process_page(driver, page, lock):
driver.get("page.url()")
driver.find_element_by_css_selector("some selector")
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "some selector")))
.
.
.
with lock:
for i in range(result_tuple.__len__()):
logger.info(result_tuple[i])
return result_tuple
def process_all_pages():
def pages_processing(id, lock):
result = []
with MyWebDriver(webdriver_options) as driver:
for i in range(50):
result.append(process_page(driver, pages[id * 50 + i], lock))
return result
lock = threading.Lock()
with ThreadPoolExecutor(4) as executor:
futures = []
for i in range(4):
futures.append(executor.submit(pages_processing, i, lock))
result = []
for i in range(futures.__len__()):
result.append(futures[i].result())
return result
MyWebDriver - простой диспетчер контекста для драйвера Chrome, при входе в контекст он порождает новый экземпляр драйвера Chrome и когда он выходит из контекста, он закрывает данный экземпляр Chrome.
Этот код порождает 4 Chrome драйвера отдельно для каждого потока и заставляет работать селен в Chrome драйверы, а также каждый поток отдельно.
Проблема
Первые несколько секунд это работает как чудо, но через некоторое время в логгере и Selenium начинают появляться предупреждения похоже, перестает общаться с Chrome драйверами.
- То же поведение наблюдается с любым количеством потоков, кроме случаев, когда он работает в одном потоке.
- То же поведение, выполняемое либо на Windows, либо на Ubuntu
При необходимости я мог бы также предоставить журналы отладки, но не уверен, что что-то есть релевантно.
Предупреждения в логгере:
...
# With these first warnings selenium stops to communicate with some Chrome drivers - just nothing happens in some of them.
WARNING - urllib3.connectionpool - Connection pool is full, discarding connection: 127.0.0.1
WARNING - urllib3.connectionpool - Connection pool is full, discarding connection: 127.0.0.1
...
# These warnings come a bit later
WARNING - urllib3.connectionpool - Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'NewConnectionError('<urllib3.connection.HTTPConnection object at 0x0000018343AB24A8>: Failed to establish a new connection: [WinError 10061] No connection could be made because the target machine actively refused it')': /session/9c9fc148f278aaa360a26d95eac0966e/url
WARNING - urllib3.connectionpool - Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'NewConnectionError('<urllib3.connection.HTTPConnection object at 0x0000018348854E10>: Failed to establish a new connection: [WinError 10061] No connection could be made because the target machine actively refused it')': /session/9c9fc148f278aaa360a26d95eac0966e/url
WARNING - urllib3.connectionpool - Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'NewConnectionError('<urllib3.connection.HTTPConnection object at 0x0000018348869710>: Failed to establish a new connection: [WinError 10061] No connection could be made because the target machine actively refused it')': /session/9c9fc148f278aaa360a26d95eac0966e/url
...
Протестированные обходные пути
Я пробовал эти патчи для установки более высокого максимального размера ( HTTPConnectionPool , HTTPSConnectionPool ) - { ссылка } - это не решило проблему, кстати. исправления были выполнены.
Далее я попытался установить более высокие значения num_pools в классе PoolManager - я изменил это только в исходных кодах, а также в maxsize в HTTPConnectionPool и HTTPSConnectionPool . Это фактически решило одну проблему - никаких предупреждений в журнале не было, НО связь селена с драйвером все еще зависла.