mirror of
https://github.com/mendableai/firecrawl.git
synced 2024-11-16 03:32:22 +08:00
be9e7f9edf
Added new ConfigMap for playwright-service and adjusted existing references. Applied imagePullPolicy: Always to ensure all images are updated promptly. Updated README to include --no-cache for Docker build instructions.
109 lines
3.4 KiB
Python
109 lines
3.4 KiB
Python
"""
|
|
This module provides a FastAPI application that uses Playwright to fetch and return
|
|
the HTML content of a specified URL. It supports optional proxy settings and media blocking.
|
|
"""
|
|
|
|
from os import environ
|
|
|
|
from fastapi import FastAPI, Response
|
|
from fastapi.responses import JSONResponse
|
|
from playwright.async_api import Browser, async_playwright
|
|
from pydantic import BaseModel
|
|
from get_error import get_error
|
|
|
|
PROXY_SERVER = environ.get("PROXY_SERVER", None)
|
|
PROXY_USERNAME = environ.get("PROXY_USERNAME", None)
|
|
PROXY_PASSWORD = environ.get("PROXY_PASSWORD", None)
|
|
BLOCK_MEDIA = environ.get("BLOCK_MEDIA", "False").upper() == "TRUE"
|
|
|
|
app = FastAPI()
|
|
|
|
class UrlModel(BaseModel):
|
|
"""Model representing the URL and associated parameters for the request."""
|
|
url: str
|
|
wait_after_load: int = 0
|
|
timeout: int = 15000
|
|
headers: dict = None
|
|
|
|
browser: Browser = None
|
|
|
|
@app.on_event("startup")
|
|
async def startup_event():
|
|
"""Event handler for application startup to initialize the browser."""
|
|
global browser
|
|
playwright = await async_playwright().start()
|
|
browser = await playwright.chromium.launch()
|
|
|
|
@app.on_event("shutdown")
|
|
async def shutdown_event():
|
|
"""Event handler for application shutdown to close the browser."""
|
|
await browser.close()
|
|
|
|
@app.get("/health/liveness")
|
|
def liveness_probe():
|
|
"""Endpoint for liveness probe."""
|
|
return JSONResponse(content={"status": "ok"}, status_code=200)
|
|
|
|
|
|
@app.get("/health/readiness")
|
|
async def readiness_probe():
|
|
"""Endpoint for readiness probe. Checks if the browser instance is ready."""
|
|
if browser:
|
|
return JSONResponse(content={"status": "ok"}, status_code=200)
|
|
return JSONResponse(content={"status": "Service Unavailable"}, status_code=503)
|
|
|
|
|
|
@app.post("/html")
|
|
async def root(body: UrlModel):
|
|
"""
|
|
Endpoint to fetch and return HTML content of a given URL.
|
|
|
|
Args:
|
|
body (UrlModel): The URL model containing the target URL, wait time, and timeout.
|
|
|
|
Returns:
|
|
JSONResponse: The HTML content of the page.
|
|
"""
|
|
context = None
|
|
if PROXY_SERVER and PROXY_USERNAME and PROXY_PASSWORD:
|
|
context = await browser.new_context(
|
|
proxy={
|
|
"server": PROXY_SERVER,
|
|
"username": PROXY_USERNAME,
|
|
"password": PROXY_PASSWORD,
|
|
}
|
|
)
|
|
else:
|
|
context = await browser.new_context()
|
|
|
|
if BLOCK_MEDIA:
|
|
await context.route(
|
|
"**/*.{png,jpg,jpeg,gif,svg,mp3,mp4,avi,flac,ogg,wav,webm}",
|
|
handler=lambda route, request: route.abort(),
|
|
)
|
|
|
|
page = await context.new_page()
|
|
|
|
# Set headers if provided
|
|
if body.headers:
|
|
await page.set_extra_http_headers(body.headers)
|
|
|
|
response = await page.goto(
|
|
body.url,
|
|
wait_until="load",
|
|
timeout=body.timeout,
|
|
)
|
|
page_status_code = response.status
|
|
page_error = get_error(page_status_code)
|
|
# Wait != timeout. Wait is the time to wait after the page is loaded - useful in some cases were "load" / "networkidle" is not enough
|
|
if body.wait_after_load > 0:
|
|
await page.wait_for_timeout(body.wait_after_load)
|
|
|
|
page_content = await page.content()
|
|
await context.close()
|
|
json_compatible_item_data = {
|
|
"content": page_content,
|
|
"pageStatusCode": page_status_code,
|
|
"pageError": page_error
|
|
}
|
|
return JSONResponse(content=json_compatible_item_data) |