Spaces:
Sleeping
Sleeping
| import sys | |
| print(">>> STARTING APP INITIALIZATION <<<", flush=True) | |
| from fastapi import FastAPI | |
| from pydantic import BaseModel | |
| print(">>> FASTAPI IMPORTED <<<", flush=True) | |
| import urllib.parse | |
| import re | |
| import json | |
| from fastapi.responses import RedirectResponse | |
| app = FastAPI() | |
| def read_root(): | |
| return RedirectResponse(url="/docs") | |
| class QueryRequest(BaseModel): | |
| query: str | |
| def scrape_geocode(req: QueryRequest): | |
| from scrapling import Fetcher | |
| fetcher = Fetcher() | |
| url = f"https://nominatim.openstreetmap.org/search?q={urllib.parse.quote(req.query)}&format=json&limit=1" | |
| try: | |
| response = fetcher.get(url) | |
| data = json.loads(response.text) | |
| if data and len(data) > 0: | |
| return { | |
| "lat": float(data[0]["lat"]), | |
| "lng": float(data[0]["lon"]), | |
| "formattedAddress": data[0]["display_name"] | |
| } | |
| except Exception as e: | |
| print("Geocode Error:", e) | |
| pass | |
| # Fallback coordinate regex (naive) | |
| search_url = f"https://html.duckduckgo.com/html/?q={urllib.parse.quote(req.query + ' coordinates lat long')}" | |
| try: | |
| resp = fetcher.get(search_url) | |
| matches = re.findall(r'(-?\d+\.\d{3,})[^\d]+(-?\d+\.\d{3,})', resp.text) | |
| if matches: | |
| return {"lat": float(matches[0][0]), "lng": float(matches[0][1]), "formattedAddress": req.query} | |
| except Exception: | |
| pass | |
| return {"lat": 0, "lng": 0, "formattedAddress": req.query} | |
| def scrape_details(req: QueryRequest): | |
| from scrapling import Fetcher | |
| fetcher = Fetcher() | |
| search_url = f"https://html.duckduckgo.com/html/?q={urllib.parse.quote(req.query + ' tourism description rating')}" | |
| try: | |
| resp = fetcher.get(search_url) | |
| snippets = [] | |
| # In duckduckgo html, snippets are in .result__snippet | |
| for el in resp.css('.result__snippet'): | |
| if el.text: | |
| snippets.append(el.text) | |
| description = " ".join(snippets[:3]) | |
| if not description: | |
| description = "A wonderful destination worth visiting." | |
| return { | |
| "rating": 4.5, # Static fallback as parsing HTML for real ratings is extremely fragile | |
| "ratingsCount": 120, | |
| "address": req.query, | |
| "description": description[:350] + "..." if len(description) > 350 else description | |
| } | |
| except Exception as e: | |
| print("Details error:", e) | |
| return { | |
| "rating": 4.5, | |
| "ratingsCount": 100, | |
| "address": req.query, | |
| "description": f"Explore the beauty and culture of {req.query}. A highly recommended spot for travelers." | |
| } | |
| def scrape_images(req: QueryRequest): | |
| from scrapling import Fetcher | |
| fetcher = Fetcher() | |
| # Pexels allows scraping more easily than google images | |
| search_url = f"https://www.pexels.com/search/{urllib.parse.quote(req.query)}/" | |
| try: | |
| resp = fetcher.get(search_url) | |
| images = [] | |
| for img in resp.css('img'): | |
| src = img.attrib.get('src') | |
| if src and 'images.pexels.com/photos' in src: | |
| # Remove query params to get high res | |
| images.append(src.split('?')[0] + "?auto=compress&cs=tinysrgb&w=800") | |
| if images: | |
| # Deduplicate and return top 5 | |
| return {"images": list(dict.fromkeys(images))[:5]} | |
| except Exception as e: | |
| print("Images error:", e) | |
| pass | |
| return {"images": ["https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?auto=format&fit=crop&q=80&w=1200"]} | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="127.0.0.1", port=8000) | |