使用 Playwright 和沙箱模式实现安全的浏览器自动化

发布于:2025-02-18 ⋅ 阅读:(54) ⋅ 点赞:(0)

我的需求,做一个流式测试环境,一个结点测试用例跑完,进入下一个结点运行。
在这里插入图片描述
需要具备下面特性:
1、使用沙箱模式隔离执行环境
2、利用 Playwright 进行浏览器自动化
3. 实现了安全的变量共享机制
4、包含了错误处理和资源清理
5、支持动态代码执行

实现代码片段

const { chromium } = require('playwright')

class Sandbox {
  constructor() {
    this.sharedVariables = {
      browser: null,
      page: null,
      pageScreenshot: null,
    }

    // 创建代理对象来自动处理变量存取
    this.context = new Proxy(this.sharedVariables, {
      get: (target, prop) => target[prop],
      set: (target, prop, value) => {
        target[prop] = value
        return true
      },
    })
  }

  // 获取代理对象
  getContext() {
    return this.context
  }

  // 保留cleanup方法
  async cleanup() {
    if (this.context.browser) {
      await this.context.browser.close()
      this.context.browser = null
      this.context.page = null
    }
  }
}

// 修改 createSandboxExecutor 使用新的 context
const createSandboxExecutor = (code, sandbox) => {
  return new Function(
    'sandbox',
    'chromium',
    `
    return (async () => {
      try {
        const context = sandbox.getContext();
        
        // 添加全局对象到上下文
        Object.assign(context, {
          console,
          setTimeout,
          setInterval,
          clearTimeout,
          clearInterval,
          chromium,
        });

        with (context) {
          ${code}
        }

        return context;
      } catch (error) {
        throw new Error('Sandbox execution error: ' + error.message);
      }
    })()
  `
  )
}

// 执行代码片段
async function executeCodeSnippet(sandbox, code) {
  try {
    const executor = createSandboxExecutor(code, sandbox)
    return await executor(sandbox, chromium)
  } catch (error) {
    await sandbox.cleanup()
    throw error
  }
}

// 优化后的代码片段 1
const code1 = `
  browser = await chromium.launch({ 
    headless: false,
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
    timeout: 10000
  });
  page = await browser.newPage();
  await page.setViewportSize({ width: 1280, height: 800 });
  console.log('Browser and page launched');
`

// 优化后的代码片段 2
const code2 = `
  try {
    await page.goto('https://music.163.com/', { waitUntil: 'networkidle' });
    pageScreenshot = Buffer.from(await page.screenshot({
      fullPage: true,
      quality: 80,
      type: 'jpeg'
    })).toString('base64');
    console.log('Page screenshot captured');
  } catch (error) {
    console.error('Failed to capture screenshot:', error);
    throw error;
  }
`

async function run() {
  const sandbox = new Sandbox()
  try {
    await executeCodeSnippet(sandbox, code1)
    await executeCodeSnippet(sandbox, code2)

    const context = sandbox.getContext()
    console.log('截图成功:', !!context.pageScreenshot)
  } catch (error) {
    console.error('执行错误:', error)
  } finally {
    await sandbox.cleanup()
  }
}

run()