110 lines
4.7 KiB
Python
110 lines
4.7 KiB
Python
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())
|