Pyppeteer启动浏览器时阻塞问题求助(Python3.9环境)
Pyppeteer 重复执行时浏览器未关闭导致启动阻塞的优化方案
问题场景
使用Python 3.9编写了基于pyppeteer的循环测试代码,正常运行无问题,但当异常导致浏览器未正常关闭时,下次执行会在browser = await launch处阻塞,无头模式(headless: True)下同样存在该问题。当前想到的临时方案是启动前杀死已存在的pyppeteer进程,现寻求更优解决办法。
原代码如下:
async def pars(): site_ad = "my_link" msg = '' new_mass_for_mes = [] try: launcher.defaultArgs().remove("--enable-automation") browser = await launch({"headless": False,"userDataDir":"./tool/pyppeteer", "args": ["--no-sandbox", '--disable-setuid-sandbox',"--start-maximized","--disable-infobars","--disable-dev-shm-usage"]}) page = await browser.newPage() await stealth(page) await page.goto(site_ad,{'timeout': 15000}) # ... 省略中间业务逻辑 await page.waitFor(3000) await page.close() await browser.close() except Exception as e: try: try: await page.close() except: pass try: await browser.disconnect() except: pass await browser.close() except: pass logging.error("Error params {0} at {1}".format(site_ad, datetime.datetime.now())) logging.error(e)
优化方案
1. 用async with上下文管理器自动管理资源
pyppeteer的Browser和Page对象支持异步上下文管理,能自动在代码块结束(无论正常还是异常)时执行关闭操作,避免手动处理的疏漏。修改后代码更简洁可靠:
async def pars(): site_ad = "my_link" msg = '' new_mass_for_mes = [] try: launcher.defaultArgs().remove("--enable-automation") # 用async with管理browser,自动关闭 async with await launch({ "headless": False, "userDataDir": "./tool/pyppeteer", "args": ["--no-sandbox", '--disable-setuid-sandbox',"--start-maximized","--disable-infobars","--disable-dev-shm-usage"] }) as browser: # 用async with管理page,自动关闭 async with await browser.newPage() as page: await stealth(page) await page.goto(site_ad,{'timeout': 15000}) # ... 省略中间业务逻辑 await page.waitFor(3000) except Exception as e: logging.error("Error params {0} at {1}".format(site_ad, datetime.datetime.now())) logging.error(e)
这样无需手动调用page.close()和browser.close(),上下文管理器会自动处理,异常时也能保证资源释放。
2. 统一资源清理逻辑,简化异常处理
如果不想用上下文管理器,可以把清理逻辑抽成单独函数,避免嵌套try的混乱,确保每个资源都被正确处理:
async def cleanup(browser, page=None): # 先关闭页面 if page: try: await page.close() except Exception: pass # 断开并关闭浏览器 if browser: try: await browser.disconnect() except Exception: pass try: await browser.close() except Exception: pass async def pars(): site_ad = "my_link" msg = '' new_mass_for_mes = [] browser = None page = None try: launcher.defaultArgs().remove("--enable-automation") browser = await launch({ "headless": False, "userDataDir": "./tool/pyppeteer", "args": ["--no-sandbox", '--disable-setuid-sandbox',"--start-maximized","--disable-infobars","--disable-dev-shm-usage"] }) page = await browser.newPage() await stealth(page) await page.goto(site_ad,{'timeout': 15000}) # ... 省略中间业务逻辑 await page.waitFor(3000) await cleanup(browser, page) except Exception as e: await cleanup(browser, page) logging.error("Error params {0} at {1}".format(site_ad, datetime.datetime.now())) logging.error(e)
提前初始化browser和page为None,异常时调用统一的清理函数,逻辑更清晰,减少遗漏。
3. 避免固定userDataDir的锁问题
固定userDataDir时,若浏览器进程异常退出,可能残留Chromium的Singleton锁文件,导致新进程启动阻塞。可以改用临时目录作为用户数据目录:
import tempfile async def pars(): site_ad = "my_link" msg = '' new_mass_for_mes = [] try: launcher.defaultArgs().remove("--enable-automation") # 创建临时目录作为userDataDir with tempfile.TemporaryDirectory() as temp_dir: async with await launch({ "headless": False, "userDataDir": temp_dir, "args": ["--no-sandbox", '--disable-setuid-sandbox',"--start-maximized","--disable-infobars","--disable-dev-shm-usage"] }) as browser: async with await browser.newPage() as page: await stealth(page) await page.goto(site_ad,{'timeout': 15000}) # ... 省略中间业务逻辑 await page.waitFor(3000) except Exception as e: logging.error("Error params {0} at {1}".format(site_ad, datetime.datetime.now())) logging.error(e)
临时目录会在使用后自动删除,彻底避免残留锁文件导致的阻塞问题。如果需要保留用户数据,可以在启动前检查userDataDir下的锁文件(比如SingletonLock)并删除,但临时目录方案更省心。
内容的提问来源于stack exchange,提问作者The person with the question
相关产品推荐
相关产品推荐

