Python多进程+sync_playwright报错:Target page, context or browser has been closed
问题排查:Playwright TargetClosedError异常分析
问题现象
- DispatcherService类从Mongo获取Cookie后启动Playwright Chromium,调用
verify_status方法时self.browser.new_page()可正常运行,但调用init_scraper方法时执行同一代码抛出Target page, context or browser has been closed异常 - 复用
verify_status的页面可正常工作,但业务需求必须创建新页面 - 将
prepare_browser放入类__init__仍报错 - main方法启动两个进程,其中
execute_all_dispatcher进程会导致浏览器上下文失效
相关代码
main.py
if __name__ == "__main__": node = WorkerNodeService() node.init() # Works fine. Just random class q1 = Queue() q2 = Queue() p1 = Process(target=execute_all_redis, args=(q1, q2,)) # Works fine. A redis consumer class p1.start() p2 = Process(target=execute_all_dispatcher, args=(q1, q2,)) # Browser context dies p2.start()
DispatcherService.py
from Playwright.packages.utils.Logger import Logger from Playwright.packages.multiprocess.FacebookProcess import FacebookProcess from Playwright.packages.service.CookiesFacebookService import CookiesFacebookService class DispatcherService: def __init__(self): self.logger = Logger() self.log = self.logger.getLogger(self.__class__.__name__) self.instances_service = CookiesFacebookService() self.list_of_processes = [] def init(self): node = os.environ.get("NODE", "node") for i in self.instances_service.find_by_node(node): self.build_worker_process(i) def build_worker_process(self, instance): fb_process_object = FacebookProcess(instance) self.list_of_processes.append(fb_process_object) fb_process_object.run() def execute_all_dispatcher(q1, q2): ds = DispatcherService() ds.init()
FacebookProcess.py
from Playwright.packages.utils.Logger import Logger from Playwright.packages.service.FacebookService import FacebookService class FacebookProcess: def __init__(self, instance): self.facebook_service = FacebookService(instance) self.instance = instance # Dict self.to_run = False self.logger = Logger() self.log = self.logger.getLogger(self.__class__.__name__) def run(self): if not self.to_run: self.to_run = True self.facebook_service.prepare_browser() while self.to_run: if not self.facebook_service.verify_status(): self.log.error("Error.") time.sleep(3) self.facebook_service.init_scraper() time.sleep(3600)
FacebookService.py
import os import sys import time import base64 import traceback from playwright.sync_api import sync_playwright from Playwright.packages.utils.Logger import Logger from Playwright.packages.utils.BrowserParams import options from Playwright.playwrightbrowser.PlaywrightScraper import PlaywrightScraper class FacebookService: def __init__(self, instance): self.playwright = None self.browser = None self.instance = instance # Dict self.cookie = instance["reqCookies"] self.logger = Logger() self.options = options self.log = self.logger.getLogger(self.__class__.__name__) def prepare_browser(self): try: self.playwright = sync_playwright().start() self.browser = self.playwright.chromium.launch(**self.options) except Exception as ex0: try: self.log.error(f"Error launching Playwright: {repr(ex0)}. Retrying...", traceback.format_exc()) time.sleep(8) self.playwright = sync_playwright().start() self.browser = self.playwright.chromium.launch(**self.options) except Exception as ex1: self.log.error(f"Error launching Playwright: {repr(ex1)}. Finishing execution.", traceback.format_exc()) sys.exit() def init_scraper(self): # page = self.browser.contexts[0].pages[0] # Works fine, but need new page, not the same page = self.browser.new_page() # CONTEXT OR BROWSER DESTROYED BY SOME REASON page.context.add_cookies(self.cookie) PlaywrightScraper(page).launch_playwright() self.stop_browser() def verify_status(self): page = self.browser.new_page() # Works fine page.context.add_cookies(self.cookie) page.goto("https://www.facebook.com") if ("checkpoint" not in page.url) and not ( page.is_visible("//div/h1[contains(text(), \"Connect with people who share your interests.\")]")): self.log.info(f"Instance: OK") page.close() return True else: self.log.info(f"Instance: FAIL. Check instance.") page.close() return False def make_screenshots(self): page = self.browser.new_page() page.context.add_cookies(self.cookie) page.screenshot(path="ss.jpg") with open("ss.jpg", "rb") as image_file: converted = base64.b64encode(image_file.read()) page.close() def stop_browser(self): self.log.info(f"Shutting down instance: {node}") try: self.browser.close() self.playwright.stop() except Exception: self.log.error("Error trying to close and quit Playwright. Retrying") self.browser.close() self.playwright.stop()
PlaywrightScraper.py
class PlaywrightScraper: def __init__(self, page): self.page = page self.logger = Logger() self.log = self.logger.getLogger(self.__class__.__name__) def launch_playwright(self): page = self.page page.goto("https://www.facebook.com", wait_until='load') ...
错误日志
--- Logging error --- Traceback (most recent call last): File "/mnt/c/Users/Personal/PycharmProjects/playwright/Playwright/packages/service/FacebookService.py", line 46, in init_scraper page = self.browser.new_page() File "/home/user/.virtualenvs/playwright/lib/python3.10/site-packages/playwright/sync_api/_generated.py", line 14584, in new_page self._sync( File "/home/user/.virtualenvs/playwright/lib/python3.10/site-packages/playwright/_impl/_sync_base.py", line 115, in _sync return task.result() File "/home/user/.virtualenvs/playwright/lib/python3.10/site-packages/playwright/_impl/_browser.py", line 178, in new_page return await self._connection.wrap_api_call(inner) File "/home/user/.virtualenvs/playwright/lib/python3.10/site-packages/playwright/_impl/_connection.py", line 495, in wrap_api_call return await cb() File "/home/user/.virtualenvs/playwright/lib/python3.10/site-packages/playwright/_impl/_browser.py", line 172, in inner context = await self.new_context(**params) File "/home/user/.virtualenvs/playwright/lib/python3.10/site-packages/playwright/_impl/_browser.py", line 127, in new_context channel = await self._channel.send("newContext", params) File "/home/user/.virtualenvs/playwright/lib/python3.10/site-packages/playwright/_impl/_connection.py", line 63, in send return await self._connection.wrap_api_call( File "/home/user/.virtualenvs/playwright/lib/python3.10/site-packages/playwright/_impl/_connection.py", line 490, in wrap_api_call return await cb() File "/home/user/.virtualenvs/playwright/lib/python3.10/site-packages/playwright/_impl/_connection.py", line 101, in inner_send result = next(iter(done)).result() playwright._impl._errors.TargetClosedError: Target page, context or browser has been closed
环境版本
Python 3.10.12 Ubuntu 22.04.3 LTS on WSL2 playwright 1.41.1 chromium latest version
问题原因分析
- 进程间资源冲突:
execute_all_dispatcher作为独立进程启动,Playwright的浏览器实例与进程强绑定,无法跨进程共享。当前代码在子进程中创建浏览器,但存在资源被其他进程意外释放的风险。 - 浏览器生命周期与循环不匹配:
init_scraper方法最后调用self.stop_browser()关闭了浏览器和Playwright实例,但run方法的循环仍会继续执行,后续循环调用方法时self.browser已处于关闭状态,触发异常。 - 上下文管理混乱:每次调用
new_page都会创建新上下文,未统一管理上下文生命周期,容易引发资源泄漏或意外销毁。
解决方案
- 为每个浏览器实例分配独立进程:修改DispatcherService的
build_worker_process方法,让每个FacebookProcess在单独子进程中运行,避免进程间资源冲突:
def build_worker_process(self, instance): from multiprocessing import Process p = Process(target=FacebookProcess(instance).run) p.start() self.list_of_processes.append(p)
- 修复循环中的浏览器初始化逻辑:在
run方法的循环中,每次执行init_scraper后浏览器会被关闭,因此需要在下次循环前重新初始化浏览器:
def run(self): self.to_run = True while self.to_run: self.facebook_service.prepare_browser() if not self.facebook_service.verify_status(): self.log.error("Error.") time.sleep(3) self.facebook_service.init_scraper() time.sleep(3600)
- 统一上下文管理:在
prepare_browser中创建持久化上下文,一次性添加Cookie,后续页面都基于该上下文创建,减少资源开销:
def prepare_browser(self): try: self.playwright = sync_playwright().start() self.browser = self.playwright.chromium.launch(**self.options) self.context = self.browser.new_context() self.context.add_cookies(self.cookie) except Exception as ex0: try: self.log.error(f"Error launching Playwright: {repr(ex0)}. Retrying...", traceback.format_exc()) time.sleep(8) self.playwright = sync_playwright().start() self.browser = self.playwright.chromium.launch(**self.options) self.context = self.browser.new_context() self.context.add_cookies(self.cookie) except Exception as ex1: self.log.error(f"Error launching Playwright: {repr(ex1)}. Finishing execution.", traceback.format_exc()) sys.exit()
修改verify_status和init_scraper方法:
def verify_status(self): page = self.context.new_page() page.goto("https://www.facebook.com") if ("checkpoint" not in page.url) and not ( page.is_visible("//div/h1[contains(text(), \"Connect with people who share your interests.\")]")): self.log.info(f"Instance: OK") page.close() return True else: self.log.info(f"Instance: FAIL. Check instance.") page.close() return False def init_scraper(self): page = self.context.new_page() PlaywrightScraper(page).launch_playwright() page.close() self.stop_browser()
内容的提问来源于stack exchange,提问作者sigma5563
相关产品推荐
相关产品推荐

