Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
Fix brandables API NameError, switch Trend Surfer to a working Google Trends RSS endpoint, and harden the HUNT UI against failed requests. Also add sharp for Next.js standalone image optimization and remove PostHog script crossOrigin to reduce CORS breakage.
61 lines
2.1 KiB
Python
61 lines
2.1 KiB
Python
"""
|
|
Trend Surfer: fetch trending search queries via public RSS (no API key).
|
|
|
|
Note: This still performs an external HTTP request to Google Trends RSS.
|
|
It's not a paid API and uses public endpoints.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
from typing import Optional
|
|
from xml.etree import ElementTree as ET
|
|
|
|
import httpx
|
|
|
|
|
|
async def fetch_google_trends_daily_rss(geo: str = "US", *, timeout_seconds: float = 10.0) -> list[dict]:
|
|
geo = (geo or "US").upper().strip()
|
|
# Google has changed/retired older RSS paths (e.g. /trends/trendingsearches/daily/rss).
|
|
# This endpoint is currently the stable public feed for daily search trends.
|
|
url = f"https://trends.google.com/trending/rss?geo={geo}"
|
|
headers = {
|
|
# Use a browser-like UA; Google returns 404 for some automated clients otherwise.
|
|
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
|
|
"Accept": "application/rss+xml, application/xml;q=0.9, text/xml;q=0.8, */*;q=0.5",
|
|
}
|
|
async with httpx.AsyncClient(timeout=timeout_seconds, follow_redirects=True, headers=headers) as client:
|
|
res = await client.get(url)
|
|
res.raise_for_status()
|
|
xml = res.text
|
|
|
|
root = ET.fromstring(xml)
|
|
|
|
items: list[dict] = []
|
|
for item in root.findall(".//item"):
|
|
title = item.findtext("title") or ""
|
|
link = item.findtext("link")
|
|
pub = item.findtext("pubDate")
|
|
approx = item.findtext("{*}approx_traffic") # namespaced
|
|
|
|
published_at: Optional[datetime] = None
|
|
if pub:
|
|
try:
|
|
# Example: "Sat, 14 Dec 2025 00:00:00 +0000"
|
|
published_at = datetime.strptime(pub, "%a, %d %b %Y %H:%M:%S %z").astimezone(timezone.utc)
|
|
except Exception:
|
|
published_at = None
|
|
|
|
if title.strip():
|
|
items.append(
|
|
{
|
|
"title": title.strip(),
|
|
"approx_traffic": approx.strip() if approx else None,
|
|
"published_at": published_at,
|
|
"link": link,
|
|
}
|
|
)
|
|
|
|
return items
|
|
|