import argparse import json import re import xml.etree.ElementTree as ET import requests from kml_utils import add_placemark, add_polygon, new_document, write_kml LIST_URL = "https://reserves-naturelles.org/reserves-naturelles/" def get_coordinates_from_url(url: str) -> list[tuple[float, float]] | None: """Fetch a reserve page and extract its outer polygon ring. Args: url (str): URL of the reserve page. Returns: list[tuple[float, float]] | None: (longitude, latitude) points, or None if no coordinates could be found on the page. """ response = requests.get(url) match = re.search( r"\[\[-?\d+\.\d+,-?\d+\.\d+\](?:,\[-?\d+\.\d+,-?\d+\.\d+\])+\]", response.text ) if not match: return None return [tuple(point) for point in json.loads(match.group(0))] def get_reserves(list_url: str = LIST_URL) -> list[dict]: """Fetch the reserves list page and extract each reserve's name and URL. Args: list_url (str, optional): URL of the reserves list page. Defaults to LIST_URL. Returns: list[dict]: One {"full_name", "name", "url"} entry per reserve found on the page. """ response = requests.get(list_url) items = re.findall(r"
  • ", response.text) reserves = [] for item in items: full_name_match = re.search(r"title=\"([^\"]+)\"", item) name_match = re.search(r"class=\"rsv_nom\">([^<]+)<", item) url_match = re.search(r"href=\"([^\"]+)\"", item) if not full_name_match or not name_match or not url_match: print(f"Skipping incomplete list entry: {item}") continue reserves.append( { "full_name": full_name_match.group(1), "name": name_match.group(1), "url": url_match.group(1), } ) return reserves def scrape_reserves(list_url: str = LIST_URL) -> ET.Element: """Scrape reserves-naturelles.org and build a KML tree with one Placemark per reserve. Args: list_url (str, optional): URL of the reserves list page. Defaults to LIST_URL. Returns: ET.Element: The root element. """ root, document = new_document("RN") reserves = get_reserves(list_url) for i, reserve in enumerate(reserves): coordinates = get_coordinates_from_url(reserve["url"]) if not coordinates: print(f"No coordinates found for URL: {reserve['url']}") continue placemark = add_placemark( document, name=reserve["name"], description=f"{reserve['full_name']}
    {reserve['url']}", ) add_polygon(placemark, coordinates) print(f"Processed: {reserve['name']} ({i + 1:3d}/{len(reserves)})") return root def main(): parser = argparse.ArgumentParser(description="Scrape reserves-naturelles.org and export reserves to KML") parser.add_argument("-o", "--output", default="reserves_naturelles.kml", help="Path to the output KML file") args = parser.parse_args() root = scrape_reserves() write_kml(root, args.output) if __name__ == "__main__": main()