I built a browser automation class using Playwright for my FastAPI application. When I test the class by itself, everything works perfectly. However, when I try to use the same class inside a FastAPI route, it breaks.
Here’s my browser wrapper class:
from playwright.async_api import async_playwright
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class WebScraper:
browser_instance = None
playwright_process = None
@classmethod
async def start_browser(cls):
try:
if cls.browser_instance is None:
if cls.playwright_process is None:
cls.playwright_process = await async_playwright().__aenter__()
cls.browser_instance = await cls.playwright_process.chromium.launch(headless=True)
logger.info('Browser started successfully.')
except Exception as e:
logger.error(f'Browser startup failed: {e}')
async def fetch_html(self, target_url):
if self.browser_instance is None:
raise RuntimeError("Browser not started. Call start_browser first.")
ctx = await self.browser_instance.new_context()
tab = await ctx.new_page()
await tab.goto(target_url)
html_content = await tab.content()
await ctx.close()
return html_content
@classmethod
async def scrape_url(cls, target_url):
await cls.start_browser()
scraper = cls()
return await scraper.fetch_html(target_url)
This works fine when called directly:
async def test_function():
result = await WebScraper.scrape_url("https://httpbin.org")
print(result)
asyncio.run(test_function())
But fails inside FastAPI:
from fastapi import FastAPI
api = FastAPI()
@api.get("/scrape/")
async def scrape_endpoint():
result = await WebScraper.scrape_url('https://httpbin.org')
return result
Error message:
raise RuntimeError("Browser not started. Call start_browser first.")
RuntimeError: Browser not started. Call start_browser first.