diff --git a/CHANGELOG.md b/CHANGELOG.md index 4630073..6bfc189 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.11 (2026-07-23) +### Fixed +- Fix HH pegel page scraping + ## 1.0.10 (2026-02-17) ### Fixed - Fix HE without flow (add FetchError class) diff --git a/docs/pegel.json b/docs/pegel.json index 9c5d99f..ccf7c96 100644 --- a/docs/pegel.json +++ b/docs/pegel.json @@ -1816,32 +1816,46 @@ "HE_44850108": "Hümme / Esse", "HE_44950055": "Helmarshausen / Diemel", "HE_45100100": "Bad_Karlshafen / Weser", + "HH_5950081": "Este Inneres Sperrwerk BP / Este", + "HH_5950082": "Este Inneres Sperrwerk AP / Este", "HH_80000001": "Hintzenort / Alte Süderelbe", "HH_99003": "Brügkamp / Ammersbek", "HH_99004": "Lottbektal / Lottbek", "HH_99006": "Herrenhausallee / Ammersbek", "HH_99007": "Vogt-Kölln-Straße / Kollau", "HH_99014": "Wohldorfer Damm / Bredenbek", + "HH_99015": "RHB Wohldorfer Damm / RHB Bredenbek/Hörndiek", "HH_99019": "Poppenbüttler Hauptstraße / Mellingbek", "HH_99020": "Bergstedter Chaussee / Saselbek", "HH_99021": "Schleuse Poppenbüttel / Alster", "HH_99023": "Teetzparkweg / Susebek", "HH_99024": "Hafen Bergedorf / Schleusengraben", "HH_99045": "Klövensteenweg / Wedeler Au", + "HH_99070": "Grotmannskroog / Wandse", + "HH_99072": "Delingsdorfer Weg / Wandse", "HH_99077": "Rahlstedter Straße / Stellau", "HH_99079": "Wilhelm-Grimm-Straße / Wandse", + "HH_99080": "Dove-Elbe Schleuse / Dove-Elbe", + "HH_99081": "Bei der blauen Brücke / Dove-Elbe", + "HH_99082": "Wulffsbrücke / Gose-Elbe", "HH_99083": "Krugkoppelbrücke / Alster", + "HH_99084": "Heinrich-Stubbe-Brücke / Gose-Elbe", "HH_99091": "Mantiusstraße / Bille", + "HH_99102": "Neuländer Elbdeich / Neuländer Wettern, Brack", "HH_99109": "Nordschleswiger Straße / Osterbek", + "HH_99116": "Gotthelfweg / Außenmühlenteich", "HH_99121": "Fünfhausener Landweg / Fünfhausener Landwegwettern", "HH_99136": "Olloweg / Mühlenau", "HH_99137": "Billstedter Hauptsraße / Schleemer Bach", "HH_99154": "Niendorfer Weg / Tarpenbek", + "HH_99176": "Finkenwerder Landscheideweg / Finkenwerder Landscheide", "HH_99182": "Pollhof / Alte Brookwetterung", "HH_99210": "Bramfelder Straße / Seebek", "HH_99256": "Rahmoor / Tarpenbek", + "HH_99294": "Nordmeerstraße / Nordmeergraben", "HH_99304": "Krohnstieg (RHB) / Tarpenbek", "HH_99306": "An der Steinbek / Glinder Au", + "HH_99313": "Neuenfelder Fährdeich / Liedenkummer Wettern", "HH_99328": "Torfhuder Stieg / Ammersbek", "HH_99329": "Berner Heerweg / Berner Au", "HH_99341": "Wandsbeker Allee / Wandse", @@ -1855,13 +1869,19 @@ "HH_99353": "Bäckerbrücke / Alster", "HH_99387": "Neuenfelder Hinterdeich / Moorwettern", "HH_99390": "An der Falkenbek / Falkengraben", + "HH_99391": "Dierksstegel / Scheidebach", "HH_99410": "Am Osdorfer Born / Düpenau", + "HH_99421": "Jakobsberg (Einlagedeich) / Rethwettern", + "HH_99422": "Siedenfelder Weg / Bewässerungsgraben", "HH_99428": "Grellkamp / Bornbach", "HH_99486": "Lohe / Diekbek", "HH_99487": "Horster Damm / Brookwetterung", + "HH_99559": "Jakobsberg (Jakobsweg) / Stillhorner Wettern", + "HH_99561": "Moldauweg / Neue Höder Wettern", "HH_99562": "Wagenwerkweg / Seeve-Kanal", "HH_99605": "Außenmühlenweg / Engelbach", "HH_99636": "RHB Brügkamp / Ammersbek", + "HH_99642": "Karl-Arnold-Ring / Stübenhofer Wettern", "HH_99643": "Allerm. Deich, Sportzentrum / Dove-Elbe", "HH_99652": "Möllers Kamp / Schulenbrooksbek", "MV_03127.0": "Bodstedt / Bodstedter Bodden", @@ -2862,6 +2882,7 @@ "RP_4250640": "Epinal / Mosel", "RP_5730610": "Toul / Mosel", "RP_6051020": "Saint-Die / Meurthe", + "RP_6330": "Ouren / Our", "RP_676101001": "Damelevières / Meurthe", "RP_7": "Mersch / Alzette", "RP_7010610": "Custines / Mosel", @@ -2873,6 +2894,8 @@ "RP_9372050": "Bliesbruck / Blies", "RP_9372053": "Frauenberg / Blies", "RP_9402110": "Bousseviller / Hornbach", + "RP_9914": "Reuland / Our", + "RP_9926": "Schönberg / Our", "SH_110005": "Wyk Foehr / Nordsee", "SH_110006": "Hooge-Anleger / Nordsee", "SH_110007": "Schlüttsiel AP / Nordsee", @@ -3098,6 +3121,7 @@ "SN_10001580": "Kleinvoigtsberg 1 / Freiberger Mulde", "SN_10001720": "Raschau 1 / Weiße Elster", "SN_10001860": "Burgneudorf 1 / Kleine Spree", + "SN_10002000": "Flößberg / Eula", "SN_1100001": "Rosenthal 1 / Lausitzer Neiße", "SN_1100002": "Jänkendorf 1 / Schwarzer Schöps", "SN_1100003": "Hartau 1 / Lausitzer Neiße", diff --git a/docs/pegel.md b/docs/pegel.md index cdd6c8c..1267578 100644 --- a/docs/pegel.md +++ b/docs/pegel.md @@ -1817,32 +1817,46 @@ | HE_44850108 | Hümme / Esse | | HE_44950055 | Helmarshausen / Diemel | | HE_45100100 | Bad_Karlshafen / Weser | +| HH_5950081 | Este Inneres Sperrwerk BP / Este | +| HH_5950082 | Este Inneres Sperrwerk AP / Este | | HH_80000001 | Hintzenort / Alte Süderelbe | | HH_99003 | Brügkamp / Ammersbek | | HH_99004 | Lottbektal / Lottbek | | HH_99006 | Herrenhausallee / Ammersbek | | HH_99007 | Vogt-Kölln-Straße / Kollau | | HH_99014 | Wohldorfer Damm / Bredenbek | +| HH_99015 | RHB Wohldorfer Damm / RHB Bredenbek/Hörndiek | | HH_99019 | Poppenbüttler Hauptstraße / Mellingbek | | HH_99020 | Bergstedter Chaussee / Saselbek | | HH_99021 | Schleuse Poppenbüttel / Alster | | HH_99023 | Teetzparkweg / Susebek | | HH_99024 | Hafen Bergedorf / Schleusengraben | | HH_99045 | Klövensteenweg / Wedeler Au | +| HH_99070 | Grotmannskroog / Wandse | +| HH_99072 | Delingsdorfer Weg / Wandse | | HH_99077 | Rahlstedter Straße / Stellau | | HH_99079 | Wilhelm-Grimm-Straße / Wandse | +| HH_99080 | Dove-Elbe Schleuse / Dove-Elbe | +| HH_99081 | Bei der blauen Brücke / Dove-Elbe | +| HH_99082 | Wulffsbrücke / Gose-Elbe | | HH_99083 | Krugkoppelbrücke / Alster | +| HH_99084 | Heinrich-Stubbe-Brücke / Gose-Elbe | | HH_99091 | Mantiusstraße / Bille | +| HH_99102 | Neuländer Elbdeich / Neuländer Wettern, Brack | | HH_99109 | Nordschleswiger Straße / Osterbek | +| HH_99116 | Gotthelfweg / Außenmühlenteich | | HH_99121 | Fünfhausener Landweg / Fünfhausener Landwegwettern | | HH_99136 | Olloweg / Mühlenau | | HH_99137 | Billstedter Hauptsraße / Schleemer Bach | | HH_99154 | Niendorfer Weg / Tarpenbek | +| HH_99176 | Finkenwerder Landscheideweg / Finkenwerder Landscheide | | HH_99182 | Pollhof / Alte Brookwetterung | | HH_99210 | Bramfelder Straße / Seebek | | HH_99256 | Rahmoor / Tarpenbek | +| HH_99294 | Nordmeerstraße / Nordmeergraben | | HH_99304 | Krohnstieg (RHB) / Tarpenbek | | HH_99306 | An der Steinbek / Glinder Au | +| HH_99313 | Neuenfelder Fährdeich / Liedenkummer Wettern | | HH_99328 | Torfhuder Stieg / Ammersbek | | HH_99329 | Berner Heerweg / Berner Au | | HH_99341 | Wandsbeker Allee / Wandse | @@ -1856,13 +1870,19 @@ | HH_99353 | Bäckerbrücke / Alster | | HH_99387 | Neuenfelder Hinterdeich / Moorwettern | | HH_99390 | An der Falkenbek / Falkengraben | +| HH_99391 | Dierksstegel / Scheidebach | | HH_99410 | Am Osdorfer Born / Düpenau | +| HH_99421 | Jakobsberg (Einlagedeich) / Rethwettern | +| HH_99422 | Siedenfelder Weg / Bewässerungsgraben | | HH_99428 | Grellkamp / Bornbach | | HH_99486 | Lohe / Diekbek | | HH_99487 | Horster Damm / Brookwetterung | +| HH_99559 | Jakobsberg (Jakobsweg) / Stillhorner Wettern | +| HH_99561 | Moldauweg / Neue Höder Wettern | | HH_99562 | Wagenwerkweg / Seeve-Kanal | | HH_99605 | Außenmühlenweg / Engelbach | | HH_99636 | RHB Brügkamp / Ammersbek | +| HH_99642 | Karl-Arnold-Ring / Stübenhofer Wettern | | HH_99643 | Allerm. Deich, Sportzentrum / Dove-Elbe | | HH_99652 | Möllers Kamp / Schulenbrooksbek | | MV_03127.0 | Bodstedt / Bodstedter Bodden | @@ -2863,6 +2883,7 @@ | RP_4250640 | Epinal / Mosel | | RP_5730610 | Toul / Mosel | | RP_6051020 | Saint-Die / Meurthe | +| RP_6330 | Ouren / Our | | RP_676101001 | Damelevières / Meurthe | | RP_7 | Mersch / Alzette | | RP_7010610 | Custines / Mosel | @@ -2874,6 +2895,8 @@ | RP_9372050 | Bliesbruck / Blies | | RP_9372053 | Frauenberg / Blies | | RP_9402110 | Bousseviller / Hornbach | +| RP_9914 | Reuland / Our | +| RP_9926 | Schönberg / Our | | SH_110005 | Wyk Foehr / Nordsee | | SH_110006 | Hooge-Anleger / Nordsee | | SH_110007 | Schlüttsiel AP / Nordsee | @@ -3099,6 +3122,7 @@ | SN_10001580 | Kleinvoigtsberg 1 / Freiberger Mulde | | SN_10001720 | Raschau 1 / Weiße Elster | | SN_10001860 | Burgneudorf 1 / Kleine Spree | +| SN_10002000 | Flößberg / Eula | | SN_1100001 | Rosenthal 1 / Lausitzer Neiße | | SN_1100002 | Jänkendorf 1 / Schwarzer Schöps | | SN_1100003 | Hartau 1 / Lausitzer Neiße | diff --git a/docs/update_pegel_list.py b/docs/update_pegel_list.py index d54466b..546e408 100644 --- a/docs/update_pegel_list.py +++ b/docs/update_pegel_list.py @@ -226,22 +226,18 @@ def get_he_stations() -> tuple[str, str]: def get_hh_stations() -> tuple[str, str]: """Get all available stations for Hamburg.""" stations = [] - data = fetch_soup("https://www.wabiha.de/karte.html") - tooltipwrapper = data.find("div", id="tooltipwrapper") - divs = tooltipwrapper.find_all("div", class_="tooltip-content") - for div in divs: - spans = div.find_all("span") - if len(spans) == 8: - ident = "HH_" + div["id"].split("-")[-1] - text = div.getText() - name = text[: text.find("Gewässer:")].strip() - name = ( - name - + " / " - + text[ - text.find("Gewässer:") + 9 : text.find("Niederschlagsvorhersage") - ].strip() - ) + data = fetch_soup("https://www.wabiha.de/pegel.html") + table = data.find_all("table", id="pegeltabelle")[0] + tbody = table.find_all("tbody")[0] + trs = tbody.find_all("tr") + for row in trs: + tds = row.find_all("td") + if len(tds) > 4: + river = tds[0].find("span").text.strip() + pegel_span = tds[1].find("span") + pegel_name = pegel_span.text.strip() + ident = "HH_" + pegel_span.get("title").split()[1] + name = pegel_name + " / " + river stations.append((ident, name)) return stations diff --git a/pyproject.toml b/pyproject.toml index 403f25f..1fe58aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "lhpapi" -version = "1.0.10" +version = "1.0.11" authors = [ { name="stephan192", email="stephan192@outlook.com" }, ] diff --git a/src/lhpapi/hh_api.py b/src/lhpapi/hh_api.py index 3dc3dcb..279c451 100644 --- a/src/lhpapi/hh_api.py +++ b/src/lhpapi/hh_api.py @@ -17,23 +17,19 @@ def init_HH(ident: str) -> StaticData: # pylint: disable=invalid-name """Init data for Hamburg.""" try: # Get data - soup = fetch_soup("https://www.wabiha.de/karte.html") - tooltipwrapper = soup.find("div", id="tooltipwrapper") - div = tooltipwrapper.find_next("div", id="tooltip-content-" + ident[3:]) - spans = div.find_all("span") - # Parse data - if len(spans) == 8: - text = div.getText() - name = text[: text.find("Gewässer:")].strip() - name = ( - name - + " / " - + text[ - text.find("Gewässer:") + 9 : text.find("Niederschlagsvorhersage") - ].strip() - ) - url = "https://www.wabiha.de/grafik-" + ident[3:] + ".html" - return StaticData(ident=ident, name=name, url=url) + soup = fetch_soup("https://www.wabiha.de/pegel.html") + table = soup.find_all("table", id="pegeltabelle")[0] + tbody = table.find_all("tbody")[0] + trs = tbody.find_all("tr") + for row in trs: + span = row.find(id="tooltip-target-" + ident[3:]) + if span: + td_prev = span.find_parent("td").find_previous_sibling("td") + river = td_prev.find("span").text.strip() + pegel_name = span.text.strip() + name = pegel_name + " / " + river + url = "https://www.wabiha.de/grafik-" + ident[3:] + ".html" + return StaticData(ident=ident, name=name, url=url) return StaticData(ident=ident) except Exception as err: raise LHPError(err, "hh_api.py: init_HH()") from err @@ -43,33 +39,32 @@ def update_HH(static_data: StaticData) -> DynamicData: # pylint: disable=invali """Update data for Hamburg.""" try: # Get data - soup = fetch_soup("https://www.wabiha.de/karte.html") - tooltipwrapper = soup.find("div", id="tooltipwrapper") - div = tooltipwrapper.find_next( - "div", id="tooltip-content-" + static_data.ident[3:] - ) - spans = div.find_all("span") + soup = fetch_soup("https://www.wabiha.de/pegel.html") + table = soup.find_all("table", id="pegeltabelle")[0] + tbody = table.find_all("tbody")[0] + trs = tbody.find_all("tr") # Parse data - if len(spans) == 8: - text = div.getText() - level = convert_to_float( - text[ - text.find("Wasserstand") + 11 : text.find("[NHN\u00a0+/-\u00a0cm]") - ] - .replace(".", "") - .strip() - ) - last_update = convert_to_datetime( - text[ - text.find("\u00a0\u00a0\u00a0 um") + 6 : text.find("Trend") - ].strip(), - "%d.%m.%Y %H:%M", - ) - stage = convert_to_int(spans[7].attrs["class"][-1].split("_")[-1]) - if stage == 2: - # Special case for Hamburg, see https://www.hochwasserzentralen.de/info - stage = 3 - return DynamicData(level=level, stage=stage, last_update=last_update) + for row in trs: + span = row.find(id="tooltip-target-" + static_data.ident[3:]) + if span: + td_next = span.find_parent("td").find_next_sibling("td") + level = convert_to_float( + td_next.find("span").text.replace(".", "").strip() + ) + td_next2 = td_next.find_next_sibling("td") + last_update = convert_to_datetime( + td_next2.find(string=True, recursive=False).text.strip(), + "%d.%m.%y %H:%M", + ) + td_next4 = td_next2.find_next_sibling("td").find_next_sibling("td") + if "--" in td_next4.attrs["class"][-1]: + stage = None + else: + stage = convert_to_int(td_next4.attrs["class"][-1].split("-")[-1]) + if stage == 2: + # Special case for Hamburg, see https://www.hochwasserzentralen.de/info + stage = 3 + return DynamicData(level=level, stage=stage, last_update=last_update) return DynamicData() except Exception as err: raise LHPError(err, "hh_api.py: update_HH()") from err