Selenium多线程异常:两个WebDriver实例仅单实例执行操作
多线程下Selenium WebDriver实例冲突问题排查与解决
我编写了一个用于创建Selenium WebDriver实例并爬取数据的类,单线程运行一切正常,但切换到多线程后,所有操作仅在一个浏览器中执行,另一个浏览器虽启动但无任何动作。
多线程代码
def run_downloader(account, date_range, company_name, invoice_type): downloader = InvoiceDownloaderEnchanted(account=account, date_range=date_range, company_name=company_name, invoice_type=invoice_type) downloader.start_webdriver() downloader.process() if __name__ == '__main__': with ThreadPoolExecutor(max_workers=2) as executor: executor.map(run_downloader, repeat((cfg.USERNAME, cfg.PASSWORD)), repeat(('01/08/2022', '31/08/2022')), repeat('Company Name'), [InvoiceType.INWARD, InvoiceType.OUTWARD])
WebDriver类初始化代码
class InvoiceDownloaderEnchanted: def __init__(self, account, company_name: str, invoice_type: InvoiceType, date_range: tuple | None = None, target_url: str | None = cfg.WEBSITE_URL ): self.account = account self.company_name = company_name self.invoice_type = invoice_type self.date_range = date_range self.target_url = target_url self.driver = None def start_webdriver(self): chrome_options = webdriver.ChromeOptions() chrome_options.add_argument("--disable-gpu") chrome_prefs = cfg.CHROME_PREFS chrome_prefs['download.default_directory'] = self.download_folder print(chrome_prefs['download.default_directory']) chrome_options.add_experimental_option('prefs', chrome_prefs) chrome_options.add_experimental_option("excludeSwitches", ['enable-automation']) chrome_options.add_argument('--disable-blink-features=AutomationControlled') chrome_options.add_argument("--remote-debugging-port=9222") user_agent = UserAgent().random chrome_options.add_argument(f'user-agent={user_agent}') self.driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=chrome_options) print(self.driver) self.driver.maximize_window() def process(self): # Do scrapping stuffs
调试发现的问题
- 打印
chrome_prefs['download.default_directory']时两个实例的下载目录不同(符合预期),但chrome_options.experimental_options中的该目录却相同。 - 两个WebDriver实例的
driver.capabilities['chrome']['userDataDir']不同:
<selenium.webdriver.chrome.webdriver.WebDriver (session="93cef277d1796c241cb75f729dec222f")> <selenium.webdriver.chrome.webdriver.WebDriver (session="de08b1c6baad30587717082e41c294b5")>
- 有时运行代码时甚至只打开一个浏览器。怀疑是两个WebDriver实例引用了同一个变量,但不知道如何解决。
问题根源
- 共享配置对象:
chrome_prefs = cfg.CHROME_PREFS直接引用全局配置字典,多线程下修改该字典会导致所有线程共享同一实例,造成配置污染。 - 固定远程调试端口:
--remote-debugging-port=9222指定固定端口,第二个Chrome实例无法绑定该端口,导致WebDriver会话初始化失败,最终所有操作落到第一个实例上。
解决方法
- 复制配置字典:创建全局配置的副本,避免线程间干扰:
# 替换原chrome_prefs = cfg.CHROME_PREFS chrome_prefs = cfg.CHROME_PREFS.copy() chrome_prefs['download.default_directory'] = self.download_folder - 移除或动态分配远程调试端口:
- 若不需要远程调试功能,直接删除该行:
# chrome_options.add_argument("--remote-debugging-port=9222") - 若需要远程调试,动态生成可用端口:
import socket def get_free_port(): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(('', 0)) return s.getsockname()[1] # 在start_webdriver中使用 chrome_options.add_argument(f"--remote-debugging-port={get_free_port()}")
- 若不需要远程调试功能,直接删除该行:
- 确保线程完全隔离:每个线程的
InvoiceDownloaderEnchanted实例不共享任何全局或类级变量。
修改后的start_webdriver方法示例
def start_webdriver(self): chrome_options = webdriver.ChromeOptions() chrome_options.add_argument("--disable-gpu") # 复制全局配置,避免线程污染 chrome_prefs = cfg.CHROME_PREFS.copy() chrome_prefs['download.default_directory'] = self.download_folder print(chrome_prefs['download.default_directory']) chrome_options.add_experimental_option('prefs', chrome_prefs) chrome_options.add_experimental_option("excludeSwitches", ['enable-automation']) chrome_options.add_argument('--disable-blink-features=AutomationControlled') # 移除固定端口或使用动态端口 # chrome_options.add_argument(f"--remote-debugging-port={get_free_port()}") user_agent = UserAgent().random chrome_options.add_argument(f'user-agent={user_agent}') self.driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=chrome_options) print(self.driver) self.driver.maximize_window()
内容的提问来源于stack exchange,提问作者dinhit
相关产品推荐
相关产品推荐

