/ examples / playwright / main.py
main.py
  1  # Copyright 2025 Alibaba Group Holding Ltd.
  2  #
  3  # Licensed under the Apache License, Version 2.0 (the "License");
  4  # you may not use this file except in compliance with the License.
  5  # You may obtain a copy of the License at
  6  #
  7  #     http://www.apache.org/licenses/LICENSE-2.0
  8  #
  9  # Unless required by applicable law or agreed to in writing, software
 10  # distributed under the License is distributed on an "AS IS" BASIS,
 11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  # See the License for the specific language governing permissions and
 13  # limitations under the License.
 14  
 15  import asyncio
 16  import os
 17  from datetime import timedelta
 18  from pathlib import Path
 19  
 20  from opensandbox import Sandbox
 21  from opensandbox.config import ConnectionConfig
 22  
 23  
 24  def _required_env(name: str) -> str:
 25      value = os.getenv(name)
 26      if not value:
 27          raise RuntimeError(f"{name} is required")
 28      return value
 29  
 30  
 31  async def _print_logs(label: str, execution) -> None:
 32      for msg in execution.logs.stdout:
 33          print(f"[{label} stdout] {msg.text}")
 34      for msg in execution.logs.stderr:
 35          print(f"[{label} stderr] {msg.text}")
 36      if execution.error:
 37          print(f"[{label} error] {execution.error.name}: {execution.error.value}")
 38  
 39  
 40  async def main() -> None:
 41      domain = os.getenv("SANDBOX_DOMAIN", "localhost:8080")
 42      api_key = os.getenv("SANDBOX_API_KEY")
 43      image = os.getenv(
 44          "SANDBOX_IMAGE",
 45          "opensandbox/playwright:latest",
 46      )
 47      python_version = os.getenv("PYTHON_VERSION", "3.11")
 48  
 49      config = ConnectionConfig(
 50          domain=domain,
 51          api_key=api_key,
 52          request_timeout=timedelta(seconds=60),
 53      )
 54  
 55      # Inject Python version into container environment
 56      env = {"PYTHON_VERSION": python_version}
 57      sandbox = await Sandbox.create(
 58          image,
 59          connection_config=config,
 60          env=env,
 61      )
 62  
 63      async with sandbox:
 64          # Playwright and Chromium are pre-installed in the image
 65          # Run browser script
 66          browse_exec = await sandbox.commands.run(
 67              "python - <<'PY'\n"
 68              "import asyncio\n"
 69              "import os\n"
 70              "from pathlib import Path\n"
 71              "from playwright.async_api import async_playwright\n"
 72              "\n"
 73              "URL = os.environ.get('TARGET_URL', 'https://example.com')\n"
 74              "SCREENSHOT_PATH = Path('/home/playwright/screenshot.png')\n"
 75              "SCREENSHOT_PATH.parent.mkdir(parents=True, exist_ok=True)\n"
 76              "\n"
 77              "async def run():\n"
 78              "    async with async_playwright() as p:\n"
 79              "        browser = await p.chromium.launch(headless=True)\n"
 80              "        page = await browser.new_page()\n"
 81              "        await page.goto(URL, wait_until='networkidle')\n"
 82              "        title = await page.title()\n"
 83              "        content = await page.text_content('body')\n"
 84              "        await page.screenshot(path=str(SCREENSHOT_PATH), full_page=True)\n"
 85              "        print('title:', title)\n"
 86              "        print('screenshot saved at:', SCREENSHOT_PATH)\n"
 87              "        if content:\n"
 88              "            snippet = content.strip().replace('\\n', ' ')\n"
 89              "            print('content snippet:', snippet[:300])\n"
 90              "        await browser.close()\n"
 91              "\n"
 92              "asyncio.run(run())\n"
 93              "PY"
 94          )
 95          await _print_logs("browse", browse_exec)
 96  
 97          # Download screenshot from sandbox to local disk
 98          screenshot_remote = "/home/playwright/screenshot.png"
 99          screenshot_local = Path("screenshot.png")
100          try:
101              data = await sandbox.files.read_bytes(screenshot_remote)
102              screenshot_local.write_bytes(data)
103              print(f"\nDownloaded screenshot to: {screenshot_local.resolve()}")
104          except Exception as e:
105              print(f"\nFailed to download screenshot from {screenshot_remote}: {e}")
106  
107          await sandbox.kill()
108  
109  
110  if __name__ == "__main__":
111      asyncio.run(main())