Links automatisch in changedetection.io importieren: So klappt es trotz Sonderzeichen

Links automatisch in changedetection.io importieren: So klappt es trotz Sonderzeichen

Changedetection.io ist ein hervorragendes OSS-Projekt, welches ich seit längerem einsetze, um verschiedene Webseiten auf Änderungen zu überwachen. Insbesondere jene, die keinen eigenen RSS-Feed besitzen. Nun hatte ich einen etwas komplexeren Anwendungsfall: Ich möchte alle Produkte der Kategorie eines Shops auf Veränderungen sowie neue Einträge überwachen. Die Software soll also zunächst die Kategorie-Seite abfragen, alle Produktlinks extrahieren und diese mit einem entsprechenden Tag hinzufügen, falls diese noch nicht existieren.

Dazu existiert eine recht aktuelle Anleitung von Anfang 2024 auf der Hersteller-Seite.1 Über einen XPath-Selektor holt sich der Job lediglich die Links, welche als Benachrichtigung an die eigene API gesendet werden. Im Body muss man lediglich die Variable {{current_snapshot}} für die Rohdaten eintragen. Im Beitrag werden sogar aus relativen links absolute erzeugt.

Womit keiner gerechnet hat, sind Sonderzeichen in URLs. Die setzt der Shop fleißig lokalisiert ein und hat einen Anker für die Größe eingebaut:

https://shop.com/de/kategorie/12345-produkt-xyz#/2-Größe-m

Über die Web-Oberfläche konnte ich mehrere zufällig ausgewählte problemlos per Hand hinzufügen. Nachdem ich in der Anwendung und stdout keine Hinweise gefunden habe (außer einer irreführenden Meldung, die Daten nicht ins JSON-Format umwandeln zu können), versuchte ich die API zum Importieren händisch aufzurufen. Praktischerweise steht in der Doku ein curl Beispielaufruf.2 Der schlug fehl, weil ihm die URL nicht passt:

# curl http://changedetection.internal/api/v1/import?tag=demo --data-binary @/tmp/urls.txt -H"x-api-key:12345"
Invalid or unsupported URL

Bei quelloffener Software kann man im Programmcode nachschauen, was konkret passiert.3 Es wird eine Bibliothek zur Validierung aufgerufen. Schnell wurde klar, dass es an den Sonderzeichen liegt. Mit folgender Adresse funktioniert der Aufruf und liefert die Id des angelegten Eintrags zurück:

https://shop.com/de/kategorie/12345-produkt-xyz#/2-Gr

Theoretisch sind Sonderzeichen in URLs gültig, wenn sie entsprechend maskiert sind. Das hat der Shop versäumt. Die Browser sehen das offensichtlich großzügiger, dort war das manuelle Aufrufen von Produkten aus der Kategorie möglich. Nachdem der Changedetection.io Beitrag eine Foreach-Schleife in XPath verwendete, schaute ich nach, ob es Funktionen zur Maskierung von URLs gibt. Und tatsächlich existiert fn:escape-html-uri.4 Dies wird zwar von Onlinewerkzeugen zum Testen von Xpath (z.B. xpather.com) nicht unterstützt, doch von Changedetection.io ausgeführt. Ich passte meinen einzelnen XPath-Selektor an, dass dieser stattdessen per Schleife die Funktion für jeden Link aufruft:

xpath:for-each(//a[@class="product-thumbnail-link"]/@href, function($a) { escape-html-uri($a) })

Damit werden sämtliche potenziell problematische Sonderzeichen mit „%“ gefolgt von zwei hexadezimalen Zahlen dargestellt. Dies ist auch als percent encoding bekannt.5

https%3A%2F%2Fshop.com%2Fde%2Fkategorie%2F12345-produkt-xyz%23%2F2-Gr%C3%B6%C3%9Fe-m

Nun funktioniert der Import. Und ich sehe mich darin bestätigt, in meinen Anwendungen niemals URLs mit Sonderzeichen zu generieren. Es mag zwar mittlerweile an vielen Stellen keinen Ärger mehr bereiten. Aus Sicht eines durchschnittlichen Nutzers, der lediglich im Browser auf die Links klickt, wäre das sogar hier unproblematisch. Nicht überall ist man jedoch so tolerant. Ich sehe zudem nicht den Mehrwert, warum in der Adresse zwingend alles mit sämtlichen Sonderzeichen dargestellt werden muss. Eine ungefähre Vorstellung, was man erwarten kann, liefert [a-z0-9] bereits völlig ausreichend. Das funktioniert dafür überall.

Quellen

  1. https://changedetection.io/tutorial/automatically-adding-new-pages-watch-existing-results ↩︎
  2. https://changedetection.io/docs/api_v1/index.html#api-Watch-Import ↩︎
  3. https://www.gitlockr.com/Self-Hosted/changedetection.io/src/branch/master/changedetectionio/api/api_v1.py#L230 ↩︎
  4. http://www.datypic.com/xq/fn_escape-html-uri.html ↩︎
  5. https://www.w3schools.com/tags/ref_urlencode.ASP ↩︎

Leave a Reply