| | 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_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 = await page.screenshot(full_page=True) |
| | screenshot_base64 = base64.b64encode(screenshot).decode("utf-8") |
| |
|
| | |
| | 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 = [] |
| | if not title: |
| | seo_flags.append("Missing <title>") |
| | 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 = [] |
| | 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) |
| |
|