from fastapi import FastAPI, HTTPException, Query from pydantic import BaseModel from playwright.async_api import async_playwright import asyncio import base64 import time from typing import Optional, List import uvicorn import logging app = FastAPI() logging.basicConfig(level=logging.INFO) logger = logging.getLogger("analyzer") class AnalysisResult(BaseModel): url: str load_time: float title: Optional[str] meta_description: Optional[str] og_image: Optional[str] seo_flags: List[str] accessibility_flags: List[str] screenshot_base64: str status_code: Optional[int] = None @app.get("/analyze", response_model=AnalysisResult) async def analyze_website(url: str): try: async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context() page = await context.new_page() # Start timing start_time = time.time() response = await page.goto(url, timeout=60000, wait_until='domcontentloaded') await page.wait_for_load_state("networkidle") load_time = round(time.time() - start_time, 2) # Screenshot screenshot = await page.screenshot(full_page=True) screenshot_base64 = base64.b64encode(screenshot).decode("utf-8") # Title and meta info title = await page.title() meta_description = await page.eval_on_selector("meta[name='description']", "el => el.content") if await page.query_selector("meta[name='description']") else None og_image = await page.eval_on_selector("meta[property='og:image']", "el => el.content") if await page.query_selector("meta[property='og:image']") else None # SEO flags seo_flags = [] if not title: seo_flags.append("Missing ") if not meta_description: seo_flags.append("Missing meta description") if not await page.query_selector("h1"): seo_flags.append("Missing <h1> tag") if not og_image: seo_flags.append("Missing Open Graph image") # Accessibility flags accessibility_flags = [] images = await page.query_selector_all("img") for img in images: has_alt = await img.get_attribute("alt") if not has_alt: accessibility_flags.append("Image without alt attribute") break status_code = response.status if response else None await browser.close() return AnalysisResult( url=url, load_time=load_time, title=title, meta_description=meta_description, og_image=og_image, seo_flags=seo_flags, accessibility_flags=accessibility_flags, screenshot_base64=screenshot_base64, status_code=status_code ) except Exception as e: logger.error(f"Analysis failed for {url}: {str(e)}") raise HTTPException(status_code=500, detail=f"Error analyzing {url}: {str(e)}") if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)