浏览器多实例项目隔离方案详解

在 Python 实现多浏览器实例的 JavaScript 注入时,要确保 数据隔离、会话隔离、存储隔离,否则多个实例之间可能会共享 Cookie、LocalStorage 或者其他持久化数据,导致数据污染或冲突。以下是一些关键的隔离策略和代码实现。


1. 数据隔离

问题

  • 默认情况下,不同浏览器实例可能会共享同一个 Profile(如 Chrome 账户、缓存等)。
  • 共享 LocalStorage 或 SessionStorage 可能导致数据冲突。
  • 解决方案

  • 启动 Chrome 时,使用 --user-data-dir 让每个实例使用独立的数据目录。
  • 禁用缓存 --disable-cache,避免数据复用。
  • 运行时清理 LocalStorage 和 SessionStorage。
  • 代码示例(Pyppeteer)

    import asyncio
    import os
    from pyppeteer import launch
    
    async def inject_js(browser_id, url, script):
        user_data_dir = f"./chrome_profiles/profile_{browser_id}"  # 每个实例独立的用户数据目录
        os.makedirs(user_data_dir, exist_ok=True)
        
        browser = await launch(
            headless=False,
            args=[
                '--no-sandbox',
                '--disable-cache',
                f'--user-data-dir={user_data_dir}',  # 使用独立的用户数据目录
            ]
        )
        
        page = await browser.newPage()
        await page.goto(url)
        
        # 执行 JavaScript 代码并清理本地存储
        await page.evaluate(f"""
            {script}
            localStorage.clear();
            sessionStorage.clear();
        """)
    
        print(f"[{browser_id}] 执行完毕")
        await browser.close()
    
    async def main():
        url = "https://www.example.com"
        script = "document.body.style.backgroundColor = 'green';"
        
        tasks = [inject_js(i, url, script) for i in range(5)]  # 5 个浏览器实例
        await asyncio.gather(*tasks)
    
    asyncio.run(main())
    

    关键点

  • 独立 user-data-dir:确保不同实例的数据目录不同,防止缓存、Cookie、LocalStorage 共享。
  • 清除本地存储:每次执行前,清除 localStoragesessionStorage,避免数据污染。

  • 2. 会话隔离

    问题

  • 浏览器实例可能会共享 Cookie、SessionStorage,导致数据不安全或干扰测试。
  • 解决方案

  • 通过 无痕模式(Incognito Mode) 启动浏览器,每个实例都会有独立的会话。
  • 显式清除 Cookies,避免会话共享。
  • 代码示例(Selenium)

    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.chrome.options import Options
    import multiprocessing
    
    def inject_js_selenium(browser_id, url, script):
        print(f"[{browser_id}] 启动浏览器...")
    
        chrome_options = Options()
        chrome_options.add_argument("--incognito")  # 无痕模式
        chrome_options.add_argument("--disable-cache")
        chrome_options.add_argument("--disable-application-cache")
        
        driver = webdriver.Chrome(service=Service("/path/to/chromedriver"), options=chrome_options)
        driver.get(url)
        
        print(f"[{browser_id}] 注入 JavaScript 代码...")
        driver.execute_script(script)
    
        # 清除 Cookie,确保会话隔离
        driver.delete_all_cookies()
        
        print(f"[{browser_id}] 注入完成")
        driver.quit()
    
    if __name__ == "__main__":
        url = "https://www.example.com"
        script = "document.body.style.backgroundColor = 'blue';"
        
        processes = []
        for i in range(5):  # 5个浏览器实例
            p = multiprocessing.Process(target=inject_js_selenium, args=(i, url, script))
            p.start()
            processes.append(p)
        
        for p in processes:
            p.join()
    

    关键点

  • 使用 --incognito 选项:避免不同实例共享 SessionStorage。
  • 清除 Cookiedriver.delete_all_cookies() 确保不会话复用。

  • 3. 存储隔离

    问题

  • 数据存储可能会发生共享(如 IndexedDB、LocalStorage)。
  • 需要在每个实例执行前,清除本地存储和数据库。
  • 解决方案

  • 通过 JavaScript 代码 清除 IndexedDBLocalStorageSessionStorage
  • 代码示例(Pyppeteer)

    async def clear_storage(page):
        await page.evaluate("""
            indexedDB.databases().then(dbs => {
                for (let db of dbs) {
                    indexedDB.deleteDatabase(db.name);
                }
            });
            localStorage.clear();
            sessionStorage.clear();
        """)
        print("存储清理完毕")
    
    async def inject_js(browser_id, url, script):
        browser = await launch(headless=False, args=['--no-sandbox'])
        page = await browser.newPage()
        await page.goto(url)
        
        # 执行 JavaScript 代码并清理存储
        await clear_storage(page)
        await page.evaluate(script)
    
        print(f"[{browser_id}] 执行完毕")
        await browser.close()
    
    async def main():
        url = "https://www.example.com"
        script = "document.body.style.backgroundColor = 'yellow';"
        
        tasks = [inject_js(i, url, script) for i in range(5)]
        await asyncio.gather(*tasks)
    
    asyncio.run(main())
    

    关键点

  • indexedDB.deleteDatabase():删除 IndexedDB 以防止数据泄漏。
  • 清理 localStoragesessionStorage,确保不会话存储干扰。

  • 4. 进程隔离

    问题

  • 共享浏览器进程可能导致线程安全问题或数据共享。
  • 需要确保每个实例运行在独立进程中。
  • 解决方案

  • 通过 多进程 multiprocessingasyncio 任务并发,确保每个浏览器实例是独立的进程。
  • 代码示例(多进程 + Selenium)

    import multiprocessing
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    
    def start_browser(instance_id):
        options = Options()
        options.add_argument("--headless")  
        options.add_argument("--incognito")  # 开启无痕模式
        options.add_argument(f"--user-data-dir=./chrome_profile_{instance_id}")  # 独立数据目录
    
        driver = webdriver.Chrome(options=options)
        driver.get("https://www.example.com")
        
        # 注入 JavaScript 代码
        driver.execute_script("document.body.style.backgroundColor = 'pink';")
        
        driver.quit()
    
    if __name__ == "__main__":
        num_instances = 5  # 启动5个实例
        processes = []
    
        for i in range(num_instances):
            p = multiprocessing.Process(target=start_browser, args=(i,))
            p.start()
            processes.append(p)
    
        for p in processes:
            p.join()
    

    关键点

  • 独立 user-data-dir 目录,避免多个 Selenium 实例共享数据。
  • multiprocessing 多进程,确保浏览器实例的完全隔离。

  • 总结

    隔离类型 方案 实现方式
    数据隔离 独立 user-data-dir、清除 LocalStorage --user-data-dirlocalStorage.clear()
    会话隔离 开启 --incognito 无痕模式 delete_all_cookies()
    存储隔离 清除 IndexedDB、LocalStorage、SessionStorage indexedDB.deleteDatabase()
    进程隔离 multiprocessing 启动多个浏览器实例 multiprocessing.Process()

    通过 独立会话、无痕模式、存储清理、进程隔离,可以确保每个浏览器实例完全隔离,不会影响其他实例的数据。

    作者:百锦再@新空间代码工作室

    物联沃分享整理
    物联沃-IOTWORD物联网 » 浏览器多实例项目隔离方案详解

    发表回复