Mittwoch, 27. März 2024
Headless Chrome eignet sich wunderbar, wenn man Web-Seiten mittels über Cron Jobs aufgerufenen bash-Scripts automatisiert abrufen möchte.
In meinem Anwendungsfall sende ich mir an Werktagen um 9:00 Uhr jeweils das Mittagsmenu des örtlichen Metzgers per Email zu.
Dies tat ich bis vor einigen Wochen mittels wget, doch serverseitig hat irgendwas geändert, und das CMS und/oder der Serverbetreiber haben nun eine Landeseite vorgeschaltet, die überprüft, ob ein „realer“ Benutzer oder ein Bot auf die Seite zugreift.
Anstelle des gewünschten Seiteninhalts bekam ich so seither nur noch eine HTML-Seite mit viel komprimiertem JavaScript-Code zu sehen, welcher höchstwahrscheinlich für die Erkennung und Weiterleitung verantwortlich ist.
Headless Chrome half hier:
chromium --proxy-auto-detect --temp-profile --disable-gpu --headless --virtual-time-budget=5000 --dump-dom "https://www.mittagsmenu.com/" > "/tmp/mittagsmenu.html"
Die Option --virtual-time-budget=5000 ist super, denn sie gaukelt vor, dass nach dem Laden 5 Sekunden vergangen sind. Genug, um die Landeseite aufzurufen, die Checks durchlaufen zu lassen, und dann den tatsächlichen gewünschten Inhalt anzuzeigen.
Das funktionierte wunderschön bis vor einigen Tagen, als kein Menu mehr via Email eintraf. Irgendwie kam auch Headless Chrome nicht mehr über die Landeseite hinaus. Doch wieso?
Bald einmal kam ich darauf: Wird Headless Chrome wie oben aufgerufen, identifiziert sich der Browser als Headless laufender Chrome (!):
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/96.0.4664.110 Safari/537.36
Kein Wunder entdeckte und blockierte die Bot-Abwehr den Zugriff.
Zum Glück war die Lösung des Problems ganz einfach:
chromium --proxy-auto-detect --temp-profile --disable-gpu --headless --virtual-time-budget=5000 --dump-dom --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36" "https://www.mittagsmenu.com/" > "/tmp/mittagsmenu.html"
… und seither funktioniert das Script wieder wie gewünscht. Das Argument --user-agent="" war alles, was es dazu brauchte.
Selenium headless: How to bypass Cloudflare detection using Selenium half mir dabei auf die Sprünge.