import asyncio import asyncpg import httpx from bs4 import BeautifulSoup import re import json DB_URL = "postgresql://teeoff_admin:teeoff_secret_password@db:5432/teeoff" def clean(text): if not text: return "" # Fjerner alt som ikke er bokstaver for å matche navn på tvers av systemer return re.sub(r'[^a-z0-9]', '', text.lower().replace("golfklubb", "").replace("gk", "").replace(" og ", "").replace("&", "")) async def scrape_golfamore(): print("\n******************************************") print("🚀 GOLFAMORE ULTIMATE SYNC v1.2") print("******************************************\n") conn = await asyncpg.connect(DB_URL) facilities = await conn.fetch("SELECT id, name FROM facilities") headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36', } async with httpx.AsyncClient(timeout=30.0, headers=headers, follow_redirects=True) as client: print("🕵️ Henter baneliste fra Golfamore (Norge)...") url = "https://www.golfamore.com/no/golfbaner/?country=NO" resp = await client.get(url) # 1. Finn alle klubb-lenker i kildekoden (omgår lazyload) # Vi leter etter mønsteret "/no/golfklubb/navn-pa-klubb/" all_slugs = re.findall(r'/no/golfklubb/([^/\"\s]+)/', resp.text) found_links = list(set([f"https://www.golfamore.com/no/golfklubb/{s}/" for s in all_slugs])) print(f"📍 Fant {len(found_links)} potensielle norske klubber i kildekoden.") if not found_links: print("❌ Klarte ikke å finne noen baner. Sjekk om Golfamore har endret URL-struktur.") await conn.close() return # Lag et map for rask matching {vasket_navn: url} link_map = {clean(l.split('/')[-2].replace('-', ' ')): l for l in found_links} matches_found = 0 for fac in facilities: fac_id = fac['id'] fac_name = fac['name'] fac_clean = clean(fac_name) match_url = link_map.get(fac_clean) # Prøv delvis match (f.eks "Arendal" i "Arendal og Omegn") if not match_url: for key, url in link_map.items(): if len(fac_clean) > 4 and (fac_clean in key or key in fac_clean): match_url = url break if match_url: try: print(f"✅ Match: {fac_name}") f_resp = await client.get(match_url) soup = BeautifulSoup(f_resp.text, 'html.parser') # Finn teksten om når kortet gjelder # Vi leter etter div-er som inneholder "Gjelder" validity = "Gjelder én gang pr. sesong." # Golfamore bruker ofte en liste (ul/li) eller en spesifikk div for regler rules_container = soup.find('div', class_=re.compile(r'rules|conditions|terms', re.I)) if not rules_container: # Fallback: Let etter tekstblokken manuelt for div in soup.find_all('div'): if div.text and "Gjelder" in div.text and len(div.text) < 200: validity = div.text.strip() break else: validity = rules_container.get_text(separator=' ').strip() # Vask teksten for linjeskift validity = re.sub(r'\s+', ' ', validity).replace('"', '').strip() ga_data = { "url": match_url, "validity": validity } await conn.execute(""" UPDATE facilities SET golfamore = true, golfamore_data = $1 WHERE id = $2 """, json.dumps(ga_data), fac_id) matches_found += 1 await asyncio.sleep(0.2) # Vær snill except Exception as e: print(f"⚠️ Feil ved parsing av {fac_name}: {e}") await conn.execute("UPDATE facilities SET golfamore = true WHERE id = $1", fac_id) else: # Hvis ikke funnet, sett til false await conn.execute("UPDATE facilities SET golfamore = false, golfamore_data = '{}' WHERE id = $1", fac_id) await conn.close() print(f"\n🎉 Ferdig! {matches_found} baner er nå synkronisert med Golfamore.") if __name__ == "__main__": asyncio.run(scrape_golfamore())