commit c347078f271517e79e76a7f0c92be882c17695b1 Author: madgerm@msn.com Date: Thu Nov 13 13:01:48 2025 +0100 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..b1d57d4 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# RAG-Crawler + +### Projekt-Zusammenfassung + +Der RAG-Crawler ist ein asynchroner, konfigurierbarer Web-Crawler, der entwickelt wurde, um Inhalte von Webseiten systematisch zu extrahieren und für die spätere Verwendung in RAG-Systemen (Retrieval-Augmented Generation) zu speichern. Er ist modular aufgebaut und bietet erweiterte Funktionen wie Zustandsverwaltung zum Pausieren und Fortsetzen, Duplikaterkennung zur Vermeidung redundanter Daten und inkrementelles Crawling, um nur geänderte Inhalte erneut zu verarbeiten. Die Konfiguration ermöglicht eine feingranulare Steuerung des Crawling-Verhaltens, des Loggings und der Datenbereinigung. + +### Feature-Liste + +* **Asynchrones Crawling:** Nutzt `aiohttp` für performante, nicht-blockierende HTTP-Anfragen. +* **Zustandsverwaltung:** Speichert den Fortschritt (URL-Warteschlange und besuchte Links) in einer Datei, um den Crawling-Prozess unterbrechen und später fortsetzen zu können. +* **Duplikaterkennung:** Verwendet SimHash, um semantisch ähnliche Inhalte zu identifizieren und das Speichern von Duplikaten zu verhindern. +* **Inkrementelles Crawling:** Führt eine SQLite-Datenbank mit den Hashes der Seiteninhalte, um nur Seiten zu verarbeiten, deren Inhalt sich seit dem letzten Crawl geändert hat. +* **Konfigurierbare Crawling-Modi:** Unterstützt verschiedene Modi pro Start-URL: + * `single_page`: Crawlt nur die angegebene Start-URL. + * `domain_wide`: Crawlt die gesamte Domain, ausgehend von der Start-URL. + * `path_limited`: Beschränkt das Crawling auf einen bestimmten URL-Pfad. +* **HTML-Bereinigung:** Entfernt vor der Inhaltsextraktion unerwünschte HTML-Tags (z.B. Header, Footer) und Textmuster (z.B. "Datenschutz"), um die Datenqualität zu verbessern. +* **`robots.txt`-Unterstützung:** Respektiert die in der `robots.txt` einer Webseite definierten Crawling-Regeln. +* **Flexibles Logging:** Unterstützt das Logging in die Konsole, rotierende Log-Dateien sowie in SQLite- und MySQL-Datenbanken. +* **Statistik-Tracking:** Erfasst detaillierte Statistiken für jeden Crawling-Vorgang (Dauer, besuchte Seiten, gespeicherte Datenmenge, Fehler). +* **URL-Normalisierung:** Bereinigt URLs, indem Fragmente und gängige Tracking-Parameter entfernt werden, um die Effizienz der Duplikaterkennung zu erhöhen. + +### Konfigurations-Tabelle (`config.json`) + +| Parameter | Beschreibung | Standardwert | +| :--- | :--- | :--- | +| `PAGE_LIMIT` | Die maximale Anzahl von Seiten, die pro Domain gecrawlt werden. | `400` | +| `CRAWL_DELAY` | Die Verzögerung in Sekunden zwischen Anfragen an dieselbe Domain. | `2` | +| `USER_AGENT` | Der User-Agent-String, der für HTTP-Anfragen verwendet wird. | `"Mozilla/5.0 ..."` | +| `PATH_STRICT` | (Legacy) Wenn `true`, werden nur URLs gecrawlt, die mit einer der `start_urls` beginnen. | `false` | +| `BLOCKED_PATTERNS` | URL-Muster, die vom Crawler ignoriert werden sollen. | `["/logout", "/auth", ...]` | +| `MAX_RETRIES` | Maximale Anzahl von Wiederholungen für fehlgeschlagene HTTP-Anfragen. | `3` | +| `RETRY_DELAY_BASE` | Basisverzögerung in Sekunden für exponentielles Backoff bei Wiederholungen. | `5` | +| `MIN_CONTENT_LENGTH` | Minimale Länge des extrahierten Textes (in Zeichen), damit eine Seite gespeichert wird. | `500` | +| `OUTPUT_DIR` | Das Verzeichnis, in dem die gecrawlten Daten als JSON-Dateien gespeichert werden. | `"/app/output"` | +| `log` | Konfiguration für das Logging-System (Handler, Dateipfade, DB-Zugangsdaten). | `{...}` | +| `html_cleaner` | Konfiguration zur Bereinigung von HTML (zu entfernende Tags und Textmuster). | `{...}` | +| `priority_patterns` | URL-Pfade, die in der Crawling-Warteschlange priorisiert werden. | `["/docs/", "/wiki/", ...]` | +| `duplicate_detection` | Aktiviert/deaktiviert die Duplikaterkennung und setzt den Ähnlichkeitsschwellenwert. | `{ "enable": true, ... }` | +| `incremental_crawling` | Aktiviert/deaktiviert das inkrementelle Crawling und gibt den Pfad zur Hash-Datenbank an. | `{ "enable": true, ... }` | +| `state_management` | Aktiviert/deaktiviert die Zustandsverwaltung und gibt den Namen der Zustandsdatei an. | `{ "enable": true, ... }` | + +### Zweck der `url_list.json` + +Die Datei `url_list.json` dient als Eingabe für den Crawler und definiert die zu verarbeitenden "Quellen". Sie ist eine Liste von Objekten, wobei jedes Objekt eine Quelle mit einer oder mehreren `start_urls` und einem spezifischen `crawl_mode` (z.B. `single_page` oder `domain_wide`) repräsentiert. Dies ermöglicht es, mehrere Webseiten mit unterschiedlichen Crawling-Strategien in einem einzigen Durchlauf zu verarbeiten. diff --git a/config.json b/config.json new file mode 100644 index 0000000..5ee1eb0 --- /dev/null +++ b/config.json @@ -0,0 +1,96 @@ +{ + "_documentation": "Allgemeine Konfigurationseinstellungen für den Web-Crawler.", + "PAGE_LIMIT": { + "value": 400, + "description": "Die maximale Anzahl von Seiten, die pro Domain gecrawlt werden." + }, + "CRAWL_DELAY": { + "value": 2, + "description": "Die Verzögerung in Sekunden zwischen aufeinanderfolgenden Anfragen an dieselbe Domain." + }, + "USER_AGENT": { + "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0", + "description": "Der User-Agent-String, der für HTTP-Anfragen verwendet wird." + }, + "PATH_STRICT": { + "value": false, + "description": "Wenn true, werden nur URLs gecrawlt, die mit einem der start_urls beginnen." + }, + "BLOCKED_PATTERNS": { + "value": [ + "/logout", + "/auth", + "?session=" + ], + "description": "URL-Muster, die vom Crawler blockiert werden sollen." + }, + "MAX_RETRIES": { + "value": 3, + "description": "Die maximale Anzahl von Wiederholungen für fehlgeschlagene HTTP-Anfragen." + }, + "RETRY_DELAY_BASE": { + "value": 5, + "description": "Die Basisverzögerung in Sekunden für exponentielles Backoff bei Wiederholungen." + }, + "MIN_CONTENT_LENGTH": { + "value": 500, + "description": "Die minimale Länge des Inhalts (in Zeichen), die eine Seite haben muss, um gespeichert zu werden." + }, + "OUTPUT_DIR": { + "value": "/app/output", + "description": "Das Verzeichnis, in dem die gecrawlten Daten gespeichert werden." + }, + "log": { + "value": { + "handlers": ["file", "console", "sqlite"], + "file": { + "log_file": "crawler.log", + "max_size_mb": 12, + "keep_last": 7 + }, + "sqlite": { + "db_file": "crawler_logs.db" + }, + "mysql": { + "host": "localhost", + "user": "user", + "password": "password", + "database": "crawler_logs", + "log_stats": true + } + }, + "description": "Konfiguration für das Logging-System." + }, + "html_cleaner": { + "value": { + "remove_tags": ["header", "nav", "aside", "footer"], + "remove_patterns": ["Datenschutz", "Kontakt", "Newsletter", "Zuletzt aktualisiert"] + }, + "description": "Konfiguration zur Bereinigung von HTML-Inhalten." + }, + "priority_patterns": { + "value": ["/docs/", "/wiki/", "/handbuch/", "/kapitel/"], + "description": "URL-Pfade, die beim Crawling priorisiert werden sollen." + }, + "duplicate_detection": { + "value": { + "enable": true, + "similarity_threshold": 95 + }, + "description": "Konfiguration für die Duplikat-Erkennung mittels SimHash." + }, + "incremental_crawling": { + "value": { + "enable": true, + "db_file": "crawled_hashes.db" + }, + "description": "Konfiguration für das inkrementelle Crawling." + }, + "state_management": { + "value": { + "enable": true, + "state_file": "crawler_state.json" + }, + "description": "Konfiguration für die Speicherung des Crawler-Zustands zum Fortsetzen." + } +} \ No newline at end of file diff --git a/crawled_hashes.db b/crawled_hashes.db new file mode 100644 index 0000000..2f65208 Binary files /dev/null and b/crawled_hashes.db differ diff --git a/crawler.log b/crawler.log new file mode 100644 index 0000000..eaf3040 --- /dev/null +++ b/crawler.log @@ -0,0 +1,683 @@ +2025-11-12 04:04:00,437 - INFO - Logging-System erfolgreich initialisiert. +2025-11-12 04:04:00,448 - INFO - Processing source: https://sqlite.org/docs.html +2025-11-12 04:04:00,454 - INFO - Starte Quelle: https://sqlite.org/docs.html (Modus: single_page) +2025-11-12 04:04:00,456 - INFO - Crawle: https://sqlite.org/docs.html +2025-11-12 04:04:00,459 - INFO - Lade robots.txt von: https://sqlite.org/robots.txt +2025-11-12 04:04:01,621 - ERROR - Allgemeiner Fehler beim Crawlen von https://sqlite.org/docs.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:04:03,627 - INFO - Quelle https://sqlite.org/docs.html erfolgreich gecrawlt. +2025-11-12 04:04:03,634 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:03,640 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:03,646 - INFO - Finished source: https://sqlite.org/docs.html +2025-11-12 04:04:03,656 - INFO - Processing source: https://www.gesetze-im-internet.de/ao_1977/BJNR006130976.html +2025-11-12 04:04:03,662 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/ao_1977/BJNR006130976.html (Modus: single_page) +2025-11-12 04:04:03,667 - INFO - Crawle: https://www.gesetze-im-internet.de/ao_1977/BJNR006130976.html +2025-11-12 04:04:03,671 - INFO - Lade robots.txt von: https://www.gesetze-im-internet.de/robots.txt +2025-11-12 04:04:04,001 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/ao_1977/BJNR006130976.html: 'utf-8' codec can't decode byte 0xfc in position 945: invalid start byte +2025-11-12 04:04:06,006 - INFO - Quelle https://www.gesetze-im-internet.de/ao_1977/BJNR006130976.html erfolgreich gecrawlt. +2025-11-12 04:04:06,013 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:06,022 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:06,027 - INFO - Finished source: https://www.gesetze-im-internet.de/ao_1977/BJNR006130976.html +2025-11-12 04:04:06,035 - INFO - Processing source: https://www.gesetze-im-internet.de/gmbhg/BJNR004770892.html +2025-11-12 04:04:06,044 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/gmbhg/BJNR004770892.html (Modus: single_page) +2025-11-12 04:04:06,052 - INFO - Crawle: https://www.gesetze-im-internet.de/gmbhg/BJNR004770892.html +2025-11-12 04:04:06,111 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/gmbhg/BJNR004770892.html: 'utf-8' codec can't decode byte 0xe4 in position 324: invalid continuation byte +2025-11-12 04:04:08,117 - INFO - Quelle https://www.gesetze-im-internet.de/gmbhg/BJNR004770892.html erfolgreich gecrawlt. +2025-11-12 04:04:08,122 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:08,131 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:08,138 - INFO - Finished source: https://www.gesetze-im-internet.de/gmbhg/BJNR004770892.html +2025-11-12 04:04:08,142 - INFO - Processing source: https://www.gesetze-im-internet.de/pangv_2022/BJNR492110021.html +2025-11-12 04:04:08,148 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/pangv_2022/BJNR492110021.html (Modus: single_page) +2025-11-12 04:04:08,153 - INFO - Crawle: https://www.gesetze-im-internet.de/pangv_2022/BJNR492110021.html +2025-11-12 04:04:08,203 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/pangv_2022/BJNR492110021.html: 'utf-8' codec can't decode byte 0xfc in position 956: invalid start byte +2025-11-12 04:04:10,207 - INFO - Quelle https://www.gesetze-im-internet.de/pangv_2022/BJNR492110021.html erfolgreich gecrawlt. +2025-11-12 04:04:10,212 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:10,218 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:10,224 - INFO - Finished source: https://www.gesetze-im-internet.de/pangv_2022/BJNR492110021.html +2025-11-12 04:04:10,233 - INFO - Processing source: https://www.gesetze-im-internet.de/ustg_1980/BJNR119530979.html +2025-11-12 04:04:10,240 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/ustg_1980/BJNR119530979.html (Modus: single_page) +2025-11-12 04:04:10,247 - INFO - Crawle: https://www.gesetze-im-internet.de/ustg_1980/BJNR119530979.html +2025-11-12 04:04:10,370 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/ustg_1980/BJNR119530979.html: 'utf-8' codec can't decode byte 0xfc in position 951: invalid start byte +2025-11-12 04:04:12,374 - INFO - Quelle https://www.gesetze-im-internet.de/ustg_1980/BJNR119530979.html erfolgreich gecrawlt. +2025-11-12 04:04:12,381 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:12,388 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:12,393 - INFO - Finished source: https://www.gesetze-im-internet.de/ustg_1980/BJNR119530979.html +2025-11-12 04:04:12,398 - INFO - Processing source: https://www.gesetze-im-internet.de/uwg_2004/BJNR141400004.html +2025-11-12 04:04:12,406 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/uwg_2004/BJNR141400004.html (Modus: single_page) +2025-11-12 04:04:12,410 - INFO - Crawle: https://www.gesetze-im-internet.de/uwg_2004/BJNR141400004.html +2025-11-12 04:04:12,460 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/uwg_2004/BJNR141400004.html: 'utf-8' codec can't decode byte 0xfc in position 970: invalid start byte +2025-11-12 04:04:14,464 - INFO - Quelle https://www.gesetze-im-internet.de/uwg_2004/BJNR141400004.html erfolgreich gecrawlt. +2025-11-12 04:04:14,473 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:14,479 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:14,487 - INFO - Finished source: https://www.gesetze-im-internet.de/uwg_2004/BJNR141400004.html +2025-11-12 04:04:14,491 - INFO - Processing source: https://www.gesetze-im-internet.de/hgb/BJNR002190897.html +2025-11-12 04:04:14,497 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/hgb/BJNR002190897.html (Modus: single_page) +2025-11-12 04:04:14,501 - INFO - Crawle: https://www.gesetze-im-internet.de/hgb/BJNR002190897.html +2025-11-12 04:04:14,709 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/hgb/BJNR002190897.html: 'utf-8' codec can't decode byte 0xfc in position 949: invalid start byte +2025-11-12 04:04:16,724 - INFO - Quelle https://www.gesetze-im-internet.de/hgb/BJNR002190897.html erfolgreich gecrawlt. +2025-11-12 04:04:16,729 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:16,739 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:16,747 - INFO - Finished source: https://www.gesetze-im-internet.de/hgb/BJNR002190897.html +2025-11-12 04:04:16,753 - INFO - Processing source: https://www.gesetze-im-internet.de/bgb/BJNR001950896.html +2025-11-12 04:04:16,762 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/bgb/BJNR001950896.html (Modus: single_page) +2025-11-12 04:04:16,768 - INFO - Crawle: https://www.gesetze-im-internet.de/bgb/BJNR001950896.html +2025-11-12 04:04:17,342 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/bgb/BJNR001950896.html: 'utf-8' codec can't decode byte 0xfc in position 276: invalid start byte +2025-11-12 04:04:19,347 - INFO - Quelle https://www.gesetze-im-internet.de/bgb/BJNR001950896.html erfolgreich gecrawlt. +2025-11-12 04:04:19,352 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:19,358 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:19,366 - INFO - Finished source: https://www.gesetze-im-internet.de/bgb/BJNR001950896.html +2025-11-12 04:04:19,379 - INFO - Processing source: https://www.gesetze-im-internet.de/fahrschausbo_2012/BJNR131800012.html +2025-11-12 04:04:19,385 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/fahrschausbo_2012/BJNR131800012.html (Modus: single_page) +2025-11-12 04:04:19,393 - INFO - Crawle: https://www.gesetze-im-internet.de/fahrschausbo_2012/BJNR131800012.html +2025-11-12 04:04:19,449 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/fahrschausbo_2012/BJNR131800012.html: 'utf-8' codec can't decode byte 0xfc in position 296: invalid start byte +2025-11-12 04:04:21,457 - INFO - Quelle https://www.gesetze-im-internet.de/fahrschausbo_2012/BJNR131800012.html erfolgreich gecrawlt. +2025-11-12 04:04:21,463 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:21,470 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:21,473 - INFO - Finished source: https://www.gesetze-im-internet.de/fahrschausbo_2012/BJNR131800012.html +2025-11-12 04:04:21,476 - INFO - Processing source: https://www.gesetze-im-internet.de/fahrlpr_fv/BJNR004200018.html +2025-11-12 04:04:21,481 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/fahrlpr_fv/BJNR004200018.html (Modus: single_page) +2025-11-12 04:04:21,484 - INFO - Crawle: https://www.gesetze-im-internet.de/fahrlpr_fv/BJNR004200018.html +2025-11-12 04:04:21,509 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/fahrlpr_fv/BJNR004200018.html: 'utf-8' codec can't decode byte 0xfc in position 276: invalid start byte +2025-11-12 04:04:23,514 - INFO - Quelle https://www.gesetze-im-internet.de/fahrlpr_fv/BJNR004200018.html erfolgreich gecrawlt. +2025-11-12 04:04:23,520 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:23,523 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:23,528 - INFO - Finished source: https://www.gesetze-im-internet.de/fahrlpr_fv/BJNR004200018.html +2025-11-12 04:04:23,535 - INFO - Processing source: https://www.gesetze-im-internet.de/fahrlg_2018/BJNR216210017.html +2025-11-12 04:04:23,540 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/fahrlg_2018/BJNR216210017.html (Modus: single_page) +2025-11-12 04:04:23,544 - INFO - Crawle: https://www.gesetze-im-internet.de/fahrlg_2018/BJNR216210017.html +2025-11-12 04:04:23,615 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/fahrlg_2018/BJNR216210017.html: 'utf-8' codec can't decode byte 0xfc in position 285: invalid start byte +2025-11-12 04:04:25,620 - INFO - Quelle https://www.gesetze-im-internet.de/fahrlg_2018/BJNR216210017.html erfolgreich gecrawlt. +2025-11-12 04:04:25,627 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:25,633 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:25,640 - INFO - Finished source: https://www.gesetze-im-internet.de/fahrlg_2018/BJNR216210017.html +2025-11-12 04:04:25,644 - INFO - Processing source: https://www.gesetze-im-internet.de/fahrlausbv/BJNR001500018.html +2025-11-12 04:04:25,651 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/fahrlausbv/BJNR001500018.html (Modus: single_page) +2025-11-12 04:04:25,656 - INFO - Crawle: https://www.gesetze-im-internet.de/fahrlausbv/BJNR001500018.html +2025-11-12 04:04:25,727 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/fahrlausbv/BJNR001500018.html: 'utf-8' codec can't decode byte 0xfc in position 971: invalid start byte +2025-11-12 04:04:27,732 - INFO - Quelle https://www.gesetze-im-internet.de/fahrlausbv/BJNR001500018.html erfolgreich gecrawlt. +2025-11-12 04:04:27,738 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:27,744 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:27,748 - INFO - Finished source: https://www.gesetze-im-internet.de/fahrlausbv/BJNR001500018.html +2025-11-12 04:04:27,754 - INFO - Processing source: https://www.gesetze-im-internet.de/pbefg/BJNR002410961.html +2025-11-12 04:04:27,757 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/pbefg/BJNR002410961.html (Modus: single_page) +2025-11-12 04:04:27,761 - INFO - Crawle: https://www.gesetze-im-internet.de/pbefg/BJNR002410961.html +2025-11-12 04:04:27,841 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/pbefg/BJNR002410961.html: 'utf-8' codec can't decode byte 0xf6 in position 288: invalid start byte +2025-11-12 04:04:29,849 - INFO - Quelle https://www.gesetze-im-internet.de/pbefg/BJNR002410961.html erfolgreich gecrawlt. +2025-11-12 04:04:29,853 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:29,857 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:29,861 - INFO - Finished source: https://www.gesetze-im-internet.de/pbefg/BJNR002410961.html +2025-11-12 04:04:29,865 - INFO - Processing source: https://www.gesetze-im-internet.de/g_kg_1998/BJNR148510998.html +2025-11-12 04:04:29,870 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/g_kg_1998/BJNR148510998.html (Modus: single_page) +2025-11-12 04:04:29,873 - INFO - Crawle: https://www.gesetze-im-internet.de/g_kg_1998/BJNR148510998.html +2025-11-12 04:04:29,920 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/g_kg_1998/BJNR148510998.html: 'utf-8' codec can't decode byte 0xfc in position 270: invalid start byte +2025-11-12 04:04:31,926 - INFO - Quelle https://www.gesetze-im-internet.de/g_kg_1998/BJNR148510998.html erfolgreich gecrawlt. +2025-11-12 04:04:31,932 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:31,937 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:31,940 - INFO - Finished source: https://www.gesetze-im-internet.de/g_kg_1998/BJNR148510998.html +2025-11-12 04:04:31,943 - INFO - Processing source: https://www.gesetze-im-internet.de/fev_2010/BJNR198000010.html +2025-11-12 04:04:31,948 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/fev_2010/BJNR198000010.html (Modus: single_page) +2025-11-12 04:04:31,952 - INFO - Crawle: https://www.gesetze-im-internet.de/fev_2010/BJNR198000010.html +2025-11-12 04:04:32,057 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/fev_2010/BJNR198000010.html: 'utf-8' codec can't decode byte 0xfc in position 286: invalid start byte +2025-11-12 04:04:34,062 - INFO - Quelle https://www.gesetze-im-internet.de/fev_2010/BJNR198000010.html erfolgreich gecrawlt. +2025-11-12 04:04:34,069 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:34,077 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:34,084 - INFO - Finished source: https://www.gesetze-im-internet.de/fev_2010/BJNR198000010.html +2025-11-12 04:04:34,091 - INFO - Processing source: https://www.gesetze-im-internet.de/stvzo_2012/BJNR067910012.html +2025-11-12 04:04:34,103 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/stvzo_2012/BJNR067910012.html (Modus: single_page) +2025-11-12 04:04:34,110 - INFO - Crawle: https://www.gesetze-im-internet.de/stvzo_2012/BJNR067910012.html +2025-11-12 04:04:34,415 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/stvzo_2012/BJNR067910012.html: 'utf-8' codec can't decode byte 0xdf in position 281: invalid continuation byte +2025-11-12 04:04:36,421 - INFO - Quelle https://www.gesetze-im-internet.de/stvzo_2012/BJNR067910012.html erfolgreich gecrawlt. +2025-11-12 04:04:36,425 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:36,428 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:36,432 - INFO - Finished source: https://www.gesetze-im-internet.de/stvzo_2012/BJNR067910012.html +2025-11-12 04:04:36,435 - INFO - Processing source: https://www.gesetze-im-internet.de/stvg/BJNR004370909.html +2025-11-12 04:04:36,439 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/stvg/BJNR004370909.html (Modus: single_page) +2025-11-12 04:04:36,441 - INFO - Crawle: https://www.gesetze-im-internet.de/stvg/BJNR004370909.html +2025-11-12 04:04:36,549 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/stvg/BJNR004370909.html: 'utf-8' codec can't decode byte 0xdf in position 280: invalid continuation byte +2025-11-12 04:04:38,553 - INFO - Quelle https://www.gesetze-im-internet.de/stvg/BJNR004370909.html erfolgreich gecrawlt. +2025-11-12 04:04:38,557 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:38,560 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:38,565 - INFO - Finished source: https://www.gesetze-im-internet.de/stvg/BJNR004370909.html +2025-11-12 04:04:38,569 - INFO - Processing source: https://www.gesetze-im-internet.de/stvoausnv_8/BJNR113000998.html +2025-11-12 04:04:38,572 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/stvoausnv_8/BJNR113000998.html (Modus: single_page) +2025-11-12 04:04:38,574 - INFO - Crawle: https://www.gesetze-im-internet.de/stvoausnv_8/BJNR113000998.html +2025-11-12 04:04:38,602 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/stvoausnv_8/BJNR113000998.html: 'utf-8' codec can't decode byte 0xfc in position 300: invalid start byte +2025-11-12 04:04:40,607 - INFO - Quelle https://www.gesetze-im-internet.de/stvoausnv_8/BJNR113000998.html erfolgreich gecrawlt. +2025-11-12 04:04:40,611 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:40,615 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:40,618 - INFO - Finished source: https://www.gesetze-im-internet.de/stvoausnv_8/BJNR113000998.html +2025-11-12 04:04:40,622 - INFO - Processing source: https://www.gesetze-im-internet.de/stvoausnv_5/BJNR062300994.html +2025-11-12 04:04:40,625 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/stvoausnv_5/BJNR062300994.html (Modus: single_page) +2025-11-12 04:04:40,628 - INFO - Crawle: https://www.gesetze-im-internet.de/stvoausnv_5/BJNR062300994.html +2025-11-12 04:04:40,655 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/stvoausnv_5/BJNR062300994.html: 'utf-8' codec can't decode byte 0xfc in position 284: invalid start byte +2025-11-12 04:04:42,659 - INFO - Quelle https://www.gesetze-im-internet.de/stvoausnv_5/BJNR062300994.html erfolgreich gecrawlt. +2025-11-12 04:04:42,664 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:42,668 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:42,671 - INFO - Finished source: https://www.gesetze-im-internet.de/stvoausnv_5/BJNR062300994.html +2025-11-12 04:04:42,673 - INFO - Processing source: https://www.gesetze-im-internet.de/stvoausnv_9/BJNR317100998.html +2025-11-12 04:04:42,676 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/stvoausnv_9/BJNR317100998.html (Modus: single_page) +2025-11-12 04:04:42,679 - INFO - Crawle: https://www.gesetze-im-internet.de/stvoausnv_9/BJNR317100998.html +2025-11-12 04:04:42,706 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/stvoausnv_9/BJNR317100998.html: 'utf-8' codec can't decode byte 0xfc in position 301: invalid start byte +2025-11-12 04:04:44,711 - INFO - Quelle https://www.gesetze-im-internet.de/stvoausnv_9/BJNR317100998.html erfolgreich gecrawlt. +2025-11-12 04:04:44,713 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:44,715 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:44,717 - INFO - Finished source: https://www.gesetze-im-internet.de/stvoausnv_9/BJNR317100998.html +2025-11-12 04:04:44,719 - INFO - Processing source: https://www.gesetze-im-internet.de/stvoausnv_12/BJNR086600005.html +2025-11-12 04:04:44,721 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/stvoausnv_12/BJNR086600005.html (Modus: single_page) +2025-11-12 04:04:44,723 - INFO - Crawle: https://www.gesetze-im-internet.de/stvoausnv_12/BJNR086600005.html +2025-11-12 04:04:44,747 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/stvoausnv_12/BJNR086600005.html: 'utf-8' codec can't decode byte 0xf6 in position 286: invalid start byte +2025-11-12 04:04:46,751 - INFO - Quelle https://www.gesetze-im-internet.de/stvoausnv_12/BJNR086600005.html erfolgreich gecrawlt. +2025-11-12 04:04:46,753 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:46,755 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:46,757 - INFO - Finished source: https://www.gesetze-im-internet.de/stvoausnv_12/BJNR086600005.html +2025-11-12 04:04:46,758 - INFO - Processing source: https://www.gesetze-im-internet.de/stvoausnv_4/BJNR011240992.html +2025-11-12 04:04:46,761 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/stvoausnv_4/BJNR011240992.html (Modus: single_page) +2025-11-12 04:04:46,763 - INFO - Crawle: https://www.gesetze-im-internet.de/stvoausnv_4/BJNR011240992.html +2025-11-12 04:04:46,789 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/stvoausnv_4/BJNR011240992.html: 'utf-8' codec can't decode byte 0xfc in position 301: invalid start byte +2025-11-12 04:04:48,796 - INFO - Quelle https://www.gesetze-im-internet.de/stvoausnv_4/BJNR011240992.html erfolgreich gecrawlt. +2025-11-12 04:04:48,817 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:48,839 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:48,850 - INFO - Finished source: https://www.gesetze-im-internet.de/stvoausnv_4/BJNR011240992.html +2025-11-12 04:04:48,868 - INFO - Processing source: https://www.gesetze-im-internet.de/stvo_2013/BJNR036710013.html +2025-11-12 04:04:48,878 - INFO - Starte Quelle: https://www.gesetze-im-internet.de/stvo_2013/BJNR036710013.html (Modus: single_page) +2025-11-12 04:04:48,891 - INFO - Crawle: https://www.gesetze-im-internet.de/stvo_2013/BJNR036710013.html +2025-11-12 04:04:49,014 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gesetze-im-internet.de/stvo_2013/BJNR036710013.html: 'utf-8' codec can't decode byte 0xdf in position 280: invalid continuation byte +2025-11-12 04:04:51,026 - INFO - Quelle https://www.gesetze-im-internet.de/stvo_2013/BJNR036710013.html erfolgreich gecrawlt. +2025-11-12 04:04:51,035 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:51,042 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:51,045 - INFO - Finished source: https://www.gesetze-im-internet.de/stvo_2013/BJNR036710013.html +2025-11-12 04:04:51,056 - INFO - Processing source: https://eur-lex.europa.eu/legal-content/DE/TXT/HTML/?uri=CELEX:32016R0679 +2025-11-12 04:04:51,065 - INFO - Starte Quelle: https://eur-lex.europa.eu/legal-content/DE/TXT/HTML/?uri=CELEX:32016R0679 (Modus: single_page) +2025-11-12 04:04:51,071 - INFO - Crawle: https://eur-lex.europa.eu/legal-content/DE/TXT/HTML/?uri=CELEX%3A32016R0679 +2025-11-12 04:04:51,075 - INFO - Lade robots.txt von: https://eur-lex.europa.eu/robots.txt +2025-11-12 04:04:51,208 - WARNING - Fehler beim Abruf von https://eur-lex.europa.eu/robots.txt: Status 202 +2025-11-12 04:04:51,210 - WARNING - robots.txt verbietet: https://eur-lex.europa.eu/legal-content/DE/TXT/HTML/?uri=CELEX%3A32016R0679 +2025-11-12 04:04:53,216 - INFO - Quelle https://eur-lex.europa.eu/legal-content/DE/TXT/HTML/?uri=CELEX%3A32016R0679 erfolgreich gecrawlt. +2025-11-12 04:04:53,222 - INFO - Lösche Zustandsdatei. +2025-11-12 04:04:53,225 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:04:53,228 - INFO - Finished source: https://eur-lex.europa.eu/legal-content/DE/TXT/HTML/?uri=CELEX:32016R0679 +2025-11-12 04:04:53,230 - INFO - Processing source: https://www.gnu.org/software/bash/manual/bash.html +2025-11-12 04:04:53,233 - INFO - Starte Quelle: https://www.gnu.org/software/bash/manual/bash.html (Modus: single_page) +2025-11-12 04:04:53,235 - INFO - Crawle: https://www.gnu.org/software/bash/manual/bash.html +2025-11-12 04:04:53,238 - INFO - Lade robots.txt von: https://www.gnu.org/robots.txt +2025-11-12 04:04:58,062 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.gnu.org/software/bash/manual/bash.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:05:00,067 - INFO - Quelle https://www.gnu.org/software/bash/manual/bash.html erfolgreich gecrawlt. +2025-11-12 04:05:00,073 - INFO - Lösche Zustandsdatei. +2025-11-12 04:05:00,076 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:05:00,079 - INFO - Finished source: https://www.gnu.org/software/bash/manual/bash.html +2025-11-12 04:05:00,083 - INFO - Processing source: https://www.json.org/json-en.html +2025-11-12 04:05:00,086 - INFO - Starte Quelle: https://www.json.org/json-en.html (Modus: single_page) +2025-11-12 04:05:00,089 - INFO - Crawle: https://www.json.org/json-en.html +2025-11-12 04:05:00,091 - INFO - Lade robots.txt von: https://www.json.org/robots.txt +2025-11-12 04:05:00,632 - WARNING - robots.txt verbietet: https://www.json.org/json-en.html +2025-11-12 04:05:02,637 - INFO - Quelle https://www.json.org/json-en.html erfolgreich gecrawlt. +2025-11-12 04:05:02,641 - INFO - Lösche Zustandsdatei. +2025-11-12 04:05:02,643 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:05:02,646 - INFO - Finished source: https://www.json.org/json-en.html +2025-11-12 04:05:02,649 - INFO - Processing source: https://www.bussgeldkatalog.org/ +2025-11-12 04:05:02,652 - INFO - Starte Quelle: https://www.bussgeldkatalog.org/ (Modus: domain_wide) +2025-11-12 04:05:02,655 - INFO - Crawle: https://www.bussgeldkatalog.org/ +2025-11-12 04:05:02,657 - INFO - Lade robots.txt von: https://www.bussgeldkatalog.org/robots.txt +2025-11-12 04:05:03,014 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.bussgeldkatalog.org/: [Errno 13] Permission denied: '/app' +2025-11-12 04:05:05,017 - INFO - Quelle https://www.bussgeldkatalog.org/ erfolgreich gecrawlt. +2025-11-12 04:05:05,022 - INFO - Lösche Zustandsdatei. +2025-11-12 04:05:05,025 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:05:05,027 - INFO - Finished source: https://www.bussgeldkatalog.org/ +2025-11-12 04:05:05,030 - INFO - Processing source: https://docs.budibase.com/ +2025-11-12 04:05:05,032 - INFO - Starte Quelle: https://docs.budibase.com/ (Modus: domain_wide) +2025-11-12 04:05:05,034 - INFO - Crawle: https://docs.budibase.com/ +2025-11-12 04:05:05,037 - INFO - Lade robots.txt von: https://docs.budibase.com/robots.txt +2025-11-12 04:05:06,423 - ERROR - Allgemeiner Fehler beim Crawlen von https://docs.budibase.com/: [Errno 13] Permission denied: '/app' +2025-11-12 04:05:08,427 - INFO - Quelle https://docs.budibase.com/ erfolgreich gecrawlt. +2025-11-12 04:05:08,431 - INFO - Lösche Zustandsdatei. +2025-11-12 04:05:08,435 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:05:08,438 - INFO - Finished source: https://docs.budibase.com/ +2025-11-12 04:05:08,441 - INFO - Processing source: https://shadowhelix.de/ +2025-11-12 04:05:08,444 - INFO - Starte Quelle: https://shadowhelix.de/ (Modus: domain_wide) +2025-11-12 04:05:08,447 - INFO - Crawle: https://shadowhelix.de/ +2025-11-12 04:05:08,449 - INFO - Lade robots.txt von: https://shadowhelix.de/robots.txt +2025-11-12 04:05:08,802 - WARNING - robots.txt verbietet: https://shadowhelix.de/ +2025-11-12 04:05:10,806 - INFO - Quelle https://shadowhelix.de/ erfolgreich gecrawlt. +2025-11-12 04:05:10,811 - INFO - Lösche Zustandsdatei. +2025-11-12 04:05:10,814 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:05:10,817 - INFO - Finished source: https://shadowhelix.de/ +2025-11-12 04:05:10,819 - INFO - Processing source: https://markdown.de/ +2025-11-12 04:05:10,823 - INFO - Starte Quelle: https://markdown.de/ (Modus: domain_wide) +2025-11-12 04:05:10,825 - INFO - Crawle: https://markdown.de/ +2025-11-12 04:05:10,827 - INFO - Lade robots.txt von: https://markdown.de/robots.txt +2025-11-12 04:05:10,973 - WARNING - robots.txt verbietet: https://markdown.de/ +2025-11-12 04:05:12,976 - INFO - Quelle https://markdown.de/ erfolgreich gecrawlt. +2025-11-12 04:05:12,981 - INFO - Lösche Zustandsdatei. +2025-11-12 04:05:12,986 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:05:12,988 - INFO - Finished source: https://markdown.de/ +2025-11-12 04:05:12,991 - INFO - Processing source: https://mariadb.com/docs/ +2025-11-12 04:05:12,994 - INFO - Starte Quelle: https://mariadb.com/docs/ (Modus: domain_wide) +2025-11-12 04:05:12,997 - INFO - Crawle: https://mariadb.com/docs/ +2025-11-12 04:05:13,000 - INFO - Lade robots.txt von: https://mariadb.com/robots.txt +2025-11-12 04:05:13,884 - ERROR - Allgemeiner Fehler beim Crawlen von https://mariadb.com/docs/: [Errno 13] Permission denied: '/app' +2025-11-12 04:05:15,889 - INFO - Quelle https://mariadb.com/docs/ erfolgreich gecrawlt. +2025-11-12 04:05:15,893 - INFO - Lösche Zustandsdatei. +2025-11-12 04:05:15,895 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:05:15,898 - INFO - Finished source: https://mariadb.com/docs/ +2025-11-12 04:05:15,901 - INFO - Processing source: https://html.spec.whatwg.org/ +2025-11-12 04:05:15,904 - INFO - Starte Quelle: https://html.spec.whatwg.org/ (Modus: domain_wide) +2025-11-12 04:05:15,906 - INFO - Crawle: https://html.spec.whatwg.org/ +2025-11-12 04:05:15,908 - INFO - Lade robots.txt von: https://html.spec.whatwg.org/robots.txt +2025-11-12 04:07:39,943 - ERROR - Allgemeiner Fehler beim Crawlen von https://html.spec.whatwg.org/: [Errno 13] Permission denied: '/app' +2025-11-12 04:07:41,948 - INFO - Quelle https://html.spec.whatwg.org/ erfolgreich gecrawlt. +2025-11-12 04:07:41,954 - INFO - Lösche Zustandsdatei. +2025-11-12 04:07:41,956 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:07:41,971 - INFO - Finished source: https://html.spec.whatwg.org/ +2025-11-12 04:07:41,973 - INFO - Processing source: https://restfulapi.net/ +2025-11-12 04:07:41,975 - INFO - Starte Quelle: https://restfulapi.net/ (Modus: domain_wide) +2025-11-12 04:07:41,977 - INFO - Crawle: https://restfulapi.net/ +2025-11-12 04:07:41,979 - INFO - Lade robots.txt von: https://restfulapi.net/robots.txt +2025-11-12 04:07:42,652 - ERROR - Allgemeiner Fehler beim Crawlen von https://restfulapi.net/: [Errno 13] Permission denied: '/app' +2025-11-12 04:07:44,656 - INFO - Quelle https://restfulapi.net/ erfolgreich gecrawlt. +2025-11-12 04:07:44,661 - INFO - Lösche Zustandsdatei. +2025-11-12 04:07:44,667 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:07:44,670 - INFO - Finished source: https://restfulapi.net/ +2025-11-12 04:07:44,672 - INFO - Processing source: https://react.dev/ +2025-11-12 04:07:44,676 - INFO - Starte Quelle: https://react.dev/ (Modus: domain_wide) +2025-11-12 04:07:44,679 - INFO - Crawle: https://react.dev/ +2025-11-12 04:07:44,682 - INFO - Lade robots.txt von: https://react.dev/robots.txt +2025-11-12 04:07:45,182 - ERROR - Allgemeiner Fehler beim Crawlen von https://react.dev/: [Errno 13] Permission denied: '/app' +2025-11-12 04:07:47,186 - INFO - Quelle https://react.dev/ erfolgreich gecrawlt. +2025-11-12 04:07:47,190 - INFO - Lösche Zustandsdatei. +2025-11-12 04:07:47,193 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:07:47,196 - INFO - Finished source: https://react.dev/ +2025-11-12 04:07:47,199 - INFO - Processing source: https://expressjs.com/ +2025-11-12 04:07:47,202 - INFO - Starte Quelle: https://expressjs.com/ (Modus: domain_wide) +2025-11-12 04:07:47,204 - INFO - Crawle: https://expressjs.com/ +2025-11-12 04:07:47,206 - INFO - Lade robots.txt von: https://expressjs.com/robots.txt +2025-11-12 04:07:47,452 - WARNING - robots.txt verbietet: https://expressjs.com/ +2025-11-12 04:07:49,456 - INFO - Quelle https://expressjs.com/ erfolgreich gecrawlt. +2025-11-12 04:07:49,460 - INFO - Lösche Zustandsdatei. +2025-11-12 04:07:49,464 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:07:49,469 - INFO - Finished source: https://expressjs.com/ +2025-11-12 04:07:49,472 - INFO - Processing source: https://docs.docker.com/ +2025-11-12 04:07:49,475 - INFO - Starte Quelle: https://docs.docker.com/ (Modus: domain_wide) +2025-11-12 04:07:49,477 - INFO - Crawle: https://docs.docker.com/ +2025-11-12 04:07:49,481 - INFO - Lade robots.txt von: https://docs.docker.com/robots.txt +2025-11-12 04:07:49,763 - ERROR - Allgemeiner Fehler beim Crawlen von https://docs.docker.com/: [Errno 13] Permission denied: '/app' +2025-11-12 04:07:51,767 - INFO - Quelle https://docs.docker.com/ erfolgreich gecrawlt. +2025-11-12 04:07:51,771 - INFO - Lösche Zustandsdatei. +2025-11-12 04:07:51,774 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:07:51,776 - INFO - Finished source: https://docs.docker.com/ +2025-11-12 04:07:51,779 - INFO - Processing source: https://docs.openwebui.com/ +2025-11-12 04:07:51,782 - INFO - Starte Quelle: https://docs.openwebui.com/ (Modus: domain_wide) +2025-11-12 04:07:51,785 - INFO - Crawle: https://docs.openwebui.com/ +2025-11-12 04:07:51,787 - INFO - Lade robots.txt von: https://docs.openwebui.com/robots.txt +2025-11-12 04:07:52,030 - WARNING - robots.txt verbietet: https://docs.openwebui.com/ +2025-11-12 04:07:54,035 - INFO - Quelle https://docs.openwebui.com/ erfolgreich gecrawlt. +2025-11-12 04:07:54,044 - INFO - Lösche Zustandsdatei. +2025-11-12 04:07:54,047 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:07:54,050 - INFO - Finished source: https://docs.openwebui.com/ +2025-11-12 04:07:54,052 - INFO - Processing source: https://www.php.net/manual/en/ +2025-11-12 04:07:54,055 - INFO - Starte Quelle: https://www.php.net/manual/en/ (Modus: domain_wide) +2025-11-12 04:07:54,057 - INFO - Crawle: https://www.php.net/manual/en/ +2025-11-12 04:07:54,060 - INFO - Lade robots.txt von: https://www.php.net/robots.txt +2025-11-12 04:07:54,648 - WARNING - discarding data: None +2025-11-12 04:07:54,679 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.php.net/manual/en/: [Errno 13] Permission denied: '/app' +2025-11-12 04:07:56,683 - INFO - Quelle https://www.php.net/manual/en/ erfolgreich gecrawlt. +2025-11-12 04:07:56,689 - INFO - Lösche Zustandsdatei. +2025-11-12 04:07:56,693 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:07:56,696 - INFO - Finished source: https://www.php.net/manual/en/ +2025-11-12 04:07:56,700 - INFO - Processing source: https://www.postgresql.org/docs/ +2025-11-12 04:07:56,703 - INFO - Starte Quelle: https://www.postgresql.org/docs/ (Modus: domain_wide) +2025-11-12 04:07:56,706 - INFO - Crawle: https://www.postgresql.org/docs/ +2025-11-12 04:07:56,709 - INFO - Lade robots.txt von: https://www.postgresql.org/robots.txt +2025-11-12 04:07:57,042 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.postgresql.org/docs/: [Errno 13] Permission denied: '/app' +2025-11-12 04:07:59,047 - INFO - Quelle https://www.postgresql.org/docs/ erfolgreich gecrawlt. +2025-11-12 04:07:59,052 - INFO - Lösche Zustandsdatei. +2025-11-12 04:07:59,055 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:07:59,058 - INFO - Finished source: https://www.postgresql.org/docs/ +2025-11-12 04:07:59,060 - INFO - Processing source: https://nginx.org/en/docs/ +2025-11-12 04:07:59,064 - INFO - Starte Quelle: https://nginx.org/en/docs/ (Modus: domain_wide) +2025-11-12 04:07:59,066 - INFO - Crawle: https://nginx.org/en/docs/ +2025-11-12 04:07:59,069 - INFO - Lade robots.txt von: https://nginx.org/robots.txt +2025-11-12 04:07:59,282 - ERROR - Allgemeiner Fehler beim Crawlen von https://nginx.org/en/docs/: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:01,287 - INFO - Quelle https://nginx.org/en/docs/ erfolgreich gecrawlt. +2025-11-12 04:08:01,291 - INFO - Lösche Zustandsdatei. +2025-11-12 04:08:01,294 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:08:01,298 - INFO - Finished source: https://nginx.org/en/docs/ +2025-11-12 04:08:01,301 - INFO - Processing source: https://docs.nginx.com/nginx/admin-guide/ +2025-11-12 04:08:01,304 - INFO - Starte Quelle: https://docs.nginx.com/nginx/admin-guide/ (Modus: domain_wide) +2025-11-12 04:08:01,306 - INFO - Crawle: https://docs.nginx.com/nginx/admin-guide/ +2025-11-12 04:08:01,309 - INFO - Lade robots.txt von: https://docs.nginx.com/robots.txt +2025-11-12 04:08:02,393 - ERROR - Allgemeiner Fehler beim Crawlen von https://docs.nginx.com/nginx/admin-guide/: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:04,399 - INFO - Quelle https://docs.nginx.com/nginx/admin-guide/ erfolgreich gecrawlt. +2025-11-12 04:08:04,403 - INFO - Lösche Zustandsdatei. +2025-11-12 04:08:04,406 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:08:04,409 - INFO - Finished source: https://docs.nginx.com/nginx/admin-guide/ +2025-11-12 04:08:04,411 - INFO - Processing source: https://wordpress.org/documentation/ +2025-11-12 04:08:04,415 - INFO - Starte Quelle: https://wordpress.org/documentation/ (Modus: domain_wide) +2025-11-12 04:08:04,418 - INFO - Crawle: https://wordpress.org/documentation/ +2025-11-12 04:08:04,420 - INFO - Lade robots.txt von: https://wordpress.org/robots.txt +2025-11-12 04:08:05,248 - ERROR - Allgemeiner Fehler beim Crawlen von https://wordpress.org/documentation/: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:07,252 - INFO - Quelle https://wordpress.org/documentation/ erfolgreich gecrawlt. +2025-11-12 04:08:07,256 - INFO - Lösche Zustandsdatei. +2025-11-12 04:08:07,259 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:08:07,262 - INFO - Finished source: https://wordpress.org/documentation/ +2025-11-12 04:08:07,265 - INFO - Processing source: https://github.com/fail2ban/ +2025-11-12 04:08:07,268 - INFO - Starte Quelle: https://github.com/fail2ban/ (Modus: domain_wide) +2025-11-12 04:08:07,271 - INFO - Crawle: https://github.com/fail2ban/ +2025-11-12 04:08:07,273 - INFO - Lade robots.txt von: https://github.com/robots.txt +2025-11-12 04:08:08,043 - ERROR - Allgemeiner Fehler beim Crawlen von https://github.com/fail2ban/: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:10,047 - INFO - Quelle https://github.com/fail2ban/ erfolgreich gecrawlt. +2025-11-12 04:08:10,052 - INFO - Lösche Zustandsdatei. +2025-11-12 04:08:10,056 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:08:10,058 - INFO - Finished source: https://github.com/fail2ban/ +2025-11-12 04:08:10,061 - INFO - Processing source: httpshttps://grafana.com/docs/ +2025-11-12 04:08:10,064 - INFO - Starte Quelle: httpshttps://grafana.com/docs/ (Modus: domain_wide) +2025-11-12 04:08:10,067 - INFO - Crawle: httpshttps://grafana.com/docs/ +2025-11-12 04:08:10,070 - INFO - Lade robots.txt von: /robots.txt +2025-11-12 04:08:10,072 - ERROR - Fehler beim Abruf von /robots.txt: /robots.txt +2025-11-12 04:08:10,074 - WARNING - robots.txt verbietet: httpshttps://grafana.com/docs/ +2025-11-12 04:08:12,078 - INFO - Quelle httpshttps://grafana.com/docs/ erfolgreich gecrawlt. +2025-11-12 04:08:12,084 - INFO - Lösche Zustandsdatei. +2025-11-12 04:08:12,088 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:08:12,091 - INFO - Finished source: httpshttps://grafana.com/docs/ +2025-11-12 04:08:12,094 - INFO - Processing source: https://nodejs.org/docs/latest/ +2025-11-12 04:08:12,102 - INFO - Starte Quelle: https://nodejs.org/docs/latest/ (Modus: domain_wide) +2025-11-12 04:08:12,104 - INFO - Crawle: https://nodejs.org/docs/latest/ +2025-11-12 04:08:12,107 - INFO - Lade robots.txt von: https://nodejs.org/robots.txt +2025-11-12 04:08:12,220 - WARNING - robots.txt verbietet: https://nodejs.org/docs/latest/ +2025-11-12 04:08:14,224 - INFO - Quelle https://nodejs.org/docs/latest/ erfolgreich gecrawlt. +2025-11-12 04:08:14,230 - INFO - Lösche Zustandsdatei. +2025-11-12 04:08:14,234 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:08:14,238 - INFO - Finished source: https://nodejs.org/docs/latest/ +2025-11-12 04:08:14,240 - INFO - Processing source: https://www.virtualmin.com/docs/ +2025-11-12 04:08:14,243 - INFO - Starte Quelle: https://www.virtualmin.com/docs/ (Modus: domain_wide) +2025-11-12 04:08:14,246 - INFO - Crawle: https://www.virtualmin.com/docs/ +2025-11-12 04:08:14,249 - INFO - Lade robots.txt von: https://www.virtualmin.com/robots.txt +2025-11-12 04:08:15,381 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.virtualmin.com/docs/: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:17,385 - INFO - Quelle https://www.virtualmin.com/docs/ erfolgreich gecrawlt. +2025-11-12 04:08:17,388 - INFO - Lösche Zustandsdatei. +2025-11-12 04:08:17,392 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:08:17,394 - INFO - Finished source: https://www.virtualmin.com/docs/ +2025-11-12 04:08:17,398 - INFO - Processing source: https://developer.mozilla.org/en-US/docs/Web/ +2025-11-12 04:08:17,403 - INFO - Starte Quelle: https://developer.mozilla.org/en-US/docs/Web/ (Modus: domain_wide) +2025-11-12 04:08:17,406 - INFO - Crawle: https://developer.mozilla.org/en-US/docs/Web/ +2025-11-12 04:08:17,409 - INFO - Lade robots.txt von: https://developer.mozilla.org/robots.txt +2025-11-12 04:08:18,351 - ERROR - Allgemeiner Fehler beim Crawlen von https://developer.mozilla.org/en-US/docs/Web/: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:20,355 - INFO - Quelle https://developer.mozilla.org/en-US/docs/Web/ erfolgreich gecrawlt. +2025-11-12 04:08:20,359 - INFO - Lösche Zustandsdatei. +2025-11-12 04:08:20,362 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:08:20,365 - INFO - Finished source: https://developer.mozilla.org/en-US/docs/Web/ +2025-11-12 04:08:20,368 - INFO - Processing source: https://owasp.org/www-project-top-ten/ +2025-11-12 04:08:20,371 - INFO - Starte Quelle: https://owasp.org/www-project-top-ten/ (Modus: domain_wide) +2025-11-12 04:08:20,373 - INFO - Crawle: https://owasp.org/www-project-top-ten/ +2025-11-12 04:08:20,375 - INFO - Lade robots.txt von: https://owasp.org/robots.txt +2025-11-12 04:08:20,707 - ERROR - Allgemeiner Fehler beim Crawlen von https://owasp.org/www-project-top-ten/: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:22,711 - INFO - Quelle https://owasp.org/www-project-top-ten/ erfolgreich gecrawlt. +2025-11-12 04:08:22,717 - INFO - Lösche Zustandsdatei. +2025-11-12 04:08:22,721 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:08:22,724 - INFO - Finished source: https://owasp.org/www-project-top-ten/ +2025-11-12 04:08:22,726 - INFO - Processing source: https://cheatsheetseries.owasp.org/ +2025-11-12 04:08:22,730 - INFO - Starte Quelle: https://cheatsheetseries.owasp.org/ (Modus: domain_wide) +2025-11-12 04:08:22,733 - INFO - Crawle: https://cheatsheetseries.owasp.org/ +2025-11-12 04:08:22,736 - INFO - Lade robots.txt von: https://cheatsheetseries.owasp.org/robots.txt +2025-11-12 04:08:23,037 - INFO - Inhalt verworfen (Länge: 489 < 500): https://cheatsheetseries.owasp.org/ +2025-11-12 04:08:25,060 - INFO - Crawle: https://cheatsheetseries.owasp.org/Glossary.html +2025-11-12 04:08:25,317 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/Glossary.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:27,321 - INFO - Crawle: https://cheatsheetseries.owasp.org/IndexASVS.html +2025-11-12 04:08:27,635 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/IndexASVS.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:29,639 - INFO - Crawle: https://cheatsheetseries.owasp.org/IndexMASVS.html +2025-11-12 04:08:29,845 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/IndexMASVS.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:31,850 - INFO - Crawle: https://cheatsheetseries.owasp.org/IndexProactiveControls.html +2025-11-12 04:08:32,060 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/IndexProactiveControls.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:34,064 - INFO - Crawle: https://cheatsheetseries.owasp.org/IndexTopTen.html +2025-11-12 04:08:34,266 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/IndexTopTen.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:36,270 - INFO - Crawle: https://cheatsheetseries.owasp.org/News.xml +2025-11-12 04:08:36,320 - ERROR - parsed tree length: 1, wrong data type or not valid HTML +2025-11-12 04:08:36,323 - ERROR - empty HTML tree: None +2025-11-12 04:08:36,325 - WARNING - discarding data: None +2025-11-12 04:08:36,367 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/News.xml: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:38,371 - INFO - Crawle: https://cheatsheetseries.owasp.org/bundle.zip +2025-11-12 04:08:41,239 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/bundle.zip: 'utf-8' codec can't decode byte 0x97 in position 11: invalid start byte +2025-11-12 04:08:43,245 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/AJAX_Security_Cheat_Sheet.html +2025-11-12 04:08:43,518 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/AJAX_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:45,521 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Abuse_Case_Cheat_Sheet.html +2025-11-12 04:08:45,833 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Abuse_Case_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:47,837 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Access_Control_Cheat_Sheet.html +2025-11-12 04:08:48,020 - INFO - Inhalt verworfen (Länge: 204 < 500): https://cheatsheetseries.owasp.org/cheatsheets/Access_Control_Cheat_Sheet.html +2025-11-12 04:08:50,047 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Attack_Surface_Analysis_Cheat_Sheet.html +2025-11-12 04:08:50,286 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Attack_Surface_Analysis_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:52,290 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html +2025-11-12 04:08:52,638 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:54,643 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html +2025-11-12 04:08:54,926 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:56,930 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Testing_Automation_Cheat_Sheet.html +2025-11-12 04:08:57,541 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Testing_Automation_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:08:59,545 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Automotive_Security.html +2025-11-12 04:08:59,752 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Automotive_Security.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:01,756 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Bean_Validation_Cheat_Sheet.html +2025-11-12 04:09:02,186 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Bean_Validation_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:04,190 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Browser_Extension_Vulnerabilities_Cheat_Sheet.html +2025-11-12 04:09:05,015 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Browser_Extension_Vulnerabilities_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:07,019 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/C-Based_Toolchain_Hardening_Cheat_Sheet.html +2025-11-12 04:09:07,592 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/C-Based_Toolchain_Hardening_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:09,597 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/CI_CD_Security_Cheat_Sheet.html +2025-11-12 04:09:09,874 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/CI_CD_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:11,877 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Choosing_and_Using_Security_Questions_Cheat_Sheet.html +2025-11-12 04:09:12,113 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Choosing_and_Using_Security_Questions_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:14,118 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html +2025-11-12 04:09:14,422 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:16,426 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html +2025-11-12 04:09:16,747 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:18,752 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Cookie_Theft_Mitigation_Cheat_Sheet.html +2025-11-12 04:09:19,033 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Cookie_Theft_Mitigation_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:21,037 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Credential_Stuffing_Prevention_Cheat_Sheet.html +2025-11-12 04:09:21,269 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Credential_Stuffing_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:23,273 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html +2025-11-12 04:09:24,119 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:26,128 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html +2025-11-12 04:09:26,485 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:28,488 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html +2025-11-12 04:09:28,823 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:30,827 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/DOM_Clobbering_Prevention_Cheat_Sheet.html +2025-11-12 04:09:31,123 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/DOM_Clobbering_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:33,127 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html +2025-11-12 04:09:33,649 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:35,654 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Database_Security_Cheat_Sheet.html +2025-11-12 04:09:35,875 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Database_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:37,879 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html +2025-11-12 04:09:38,130 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:40,135 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Dependency_Graph_SBOM_Cheat_Sheet.html +2025-11-12 04:09:40,401 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Dependency_Graph_SBOM_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:42,404 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html +2025-11-12 04:09:42,762 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:44,767 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Django_REST_Framework_Cheat_Sheet.html +2025-11-12 04:09:45,055 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Django_REST_Framework_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:47,059 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Django_Security_Cheat_Sheet.html +2025-11-12 04:09:47,329 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Django_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:49,335 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html +2025-11-12 04:09:49,709 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:51,713 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/DotNet_Security_Cheat_Sheet.html +2025-11-12 04:09:52,690 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/DotNet_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:54,695 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Drone_Security_Cheat_Sheet.html +2025-11-12 04:09:54,986 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Drone_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:56,990 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Error_Handling_Cheat_Sheet.html +2025-11-12 04:09:57,368 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Error_Handling_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:09:59,372 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html +2025-11-12 04:09:59,612 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:01,617 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html +2025-11-12 04:10:01,842 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:03,847 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html +2025-11-12 04:10:04,227 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:06,231 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html +2025-11-12 04:10:06,561 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:08,567 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html +2025-11-12 04:10:08,868 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:10,872 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html +2025-11-12 04:10:11,064 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:13,067 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Infrastructure_as_Code_Security_Cheat_Sheet.html +2025-11-12 04:10:13,296 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Infrastructure_as_Code_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:15,301 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html +2025-11-12 04:10:15,672 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:17,676 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_in_Java_Cheat_Sheet.html +2025-11-12 04:10:17,872 - INFO - Inhalt verworfen (Länge: 193 < 500): https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_in_Java_Cheat_Sheet.html +2025-11-12 04:10:19,894 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html +2025-11-12 04:10:20,194 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:22,199 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html +2025-11-12 04:10:22,389 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:24,394 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/JAAS_Cheat_Sheet.html +2025-11-12 04:10:24,689 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/JAAS_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:26,693 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html +2025-11-12 04:10:27,362 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:29,366 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Java_Security_Cheat_Sheet.html +2025-11-12 04:10:30,414 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Java_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:32,418 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html +2025-11-12 04:10:32,699 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:34,703 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Kubernetes_Security_Cheat_Sheet.html +2025-11-12 04:10:35,296 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Kubernetes_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:37,302 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.html +2025-11-12 04:10:37,557 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:39,561 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/LLM_Prompt_Injection_Prevention_Cheat_Sheet.html +2025-11-12 04:10:40,044 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/LLM_Prompt_Injection_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:42,048 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Laravel_Cheat_Sheet.html +2025-11-12 04:10:42,404 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Laravel_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:44,408 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Legacy_Application_Management_Cheat_Sheet.html +2025-11-12 04:10:44,642 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Legacy_Application_Management_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:46,645 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html +2025-11-12 04:10:46,975 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:48,979 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Logging_Vocabulary_Cheat_Sheet.html +2025-11-12 04:10:49,371 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Logging_Vocabulary_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:51,375 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html +2025-11-12 04:10:51,714 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:53,718 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Microservices_Security_Cheat_Sheet.html +2025-11-12 04:10:53,987 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Microservices_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:55,991 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Microservices_based_Security_Arch_Doc_Cheat_Sheet.html +2025-11-12 04:10:56,310 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Microservices_based_Security_Arch_Doc_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:10:58,314 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Mobile_Application_Security_Cheat_Sheet.html +2025-11-12 04:10:58,616 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Mobile_Application_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:00,620 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Multifactor_Authentication_Cheat_Sheet.html +2025-11-12 04:11:00,990 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Multifactor_Authentication_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:02,995 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html +2025-11-12 04:11:03,310 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:05,313 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Network_Segmentation_Cheat_Sheet.html +2025-11-12 04:11:05,546 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Network_Segmentation_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:07,550 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/NoSQL_Security_Cheat_Sheet.html +2025-11-12 04:11:07,807 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/NoSQL_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:09,811 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/NodeJS_Docker_Cheat_Sheet.html +2025-11-12 04:11:10,134 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/NodeJS_Docker_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:12,139 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html +2025-11-12 04:11:12,772 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:14,777 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/OAuth2_Cheat_Sheet.html +2025-11-12 04:11:15,002 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/OAuth2_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:17,006 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html +2025-11-12 04:11:17,307 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:19,311 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/PHP_Configuration_Cheat_Sheet.html +2025-11-12 04:11:19,522 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/PHP_Configuration_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:21,525 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html +2025-11-12 04:11:21,791 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:23,795 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Pinning_Cheat_Sheet.html +2025-11-12 04:11:24,054 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Pinning_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:26,057 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Prototype_Pollution_Prevention_Cheat_Sheet.html +2025-11-12 04:11:26,259 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Prototype_Pollution_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:28,264 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Query_Parameterization_Cheat_Sheet.html +2025-11-12 04:11:28,597 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Query_Parameterization_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:30,601 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/REST_Assessment_Cheat_Sheet.html +2025-11-12 04:11:30,864 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/REST_Assessment_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:32,868 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html +2025-11-12 04:11:33,164 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:35,169 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html +2025-11-12 04:11:35,564 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:37,568 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/SAML_Security_Cheat_Sheet.html +2025-11-12 04:11:37,897 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/SAML_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:39,902 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html +2025-11-12 04:11:40,243 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:42,247 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html +2025-11-12 04:11:42,741 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:44,745 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Secure_AI_Model_Ops_Cheat_Sheet.html +2025-11-12 04:11:44,968 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Secure_AI_Model_Ops_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:46,973 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Secure_Cloud_Architecture_Cheat_Sheet.html +2025-11-12 04:11:47,339 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Secure_Cloud_Architecture_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:49,344 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Secure_Code_Review_Cheat_Sheet.html +2025-11-12 04:11:49,657 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Secure_Code_Review_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:51,661 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Secure_Product_Design_Cheat_Sheet.html +2025-11-12 04:11:51,885 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Secure_Product_Design_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:53,888 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Securing_Cascading_Style_Sheets_Cheat_Sheet.html +2025-11-12 04:11:54,109 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Securing_Cascading_Style_Sheets_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:56,112 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html +2025-11-12 04:11:56,474 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:11:58,479 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Serverless_FaaS_Security_Cheat_Sheet.html +2025-11-12 04:11:58,754 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Serverless_FaaS_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:00,759 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html +2025-11-12 04:12:01,144 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:03,148 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Software_Supply_Chain_Security_Cheat_Sheet.html +2025-11-12 04:12:03,434 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Software_Supply_Chain_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:05,438 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Symfony_Cheat_Sheet.html +2025-11-12 04:12:05,844 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Symfony_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:07,847 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html +2025-11-12 04:12:08,018 - INFO - Inhalt verworfen (Länge: 227 < 500): https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html +2025-11-12 04:12:10,041 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html +2025-11-12 04:12:10,384 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:12,394 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Payment_Gateway_Integration.html +2025-11-12 04:12:12,597 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Payment_Gateway_Integration.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:14,601 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Threat_Modeling_Cheat_Sheet.html +2025-11-12 04:12:14,858 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Threat_Modeling_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:16,861 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Transaction_Authorization_Cheat_Sheet.html +2025-11-12 04:12:17,132 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Transaction_Authorization_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:19,135 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html +2025-11-12 04:12:19,325 - INFO - Inhalt verworfen (Länge: 254 < 500): https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html +2025-11-12 04:12:21,351 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html +2025-11-12 04:12:21,635 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:23,640 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html +2025-11-12 04:12:23,923 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:25,926 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/User_Privacy_Protection_Cheat_Sheet.html +2025-11-12 04:12:26,193 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/User_Privacy_Protection_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:28,198 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Virtual_Patching_Cheat_Sheet.html +2025-11-12 04:12:28,478 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Virtual_Patching_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:30,482 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html +2025-11-12 04:12:30,775 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:32,779 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Vulnerable_Dependency_Management_Cheat_Sheet.html +2025-11-12 04:12:33,055 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Vulnerable_Dependency_Management_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:35,060 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/WebSocket_Security_Cheat_Sheet.html +2025-11-12 04:12:35,379 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/WebSocket_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:37,384 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Web_Service_Security_Cheat_Sheet.html +2025-11-12 04:12:37,619 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Web_Service_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:39,624 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html +2025-11-12 04:12:40,267 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:42,271 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/XML_Security_Cheat_Sheet.html +2025-11-12 04:12:42,925 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/XML_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:44,929 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html +2025-11-12 04:12:45,676 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:47,680 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/XS_Leaks_Cheat_Sheet.html +2025-11-12 04:12:48,039 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/XS_Leaks_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:50,043 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/Zero_Trust_Architecture_Cheat_Sheet.html +2025-11-12 04:12:50,384 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/Zero_Trust_Architecture_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:52,388 - INFO - Crawle: https://cheatsheetseries.owasp.org/cheatsheets/gRPC_Security_Cheat_Sheet.html +2025-11-12 04:12:52,896 - ERROR - Allgemeiner Fehler beim Crawlen von https://cheatsheetseries.owasp.org/cheatsheets/gRPC_Security_Cheat_Sheet.html: [Errno 13] Permission denied: '/app' +2025-11-12 04:12:54,901 - INFO - Crawle: https://cheatsheetseries.owasp.org/index.html +2025-11-12 04:12:55,080 - INFO - Inhalt verworfen (Länge: 489 < 500): https://cheatsheetseries.owasp.org/index.html +2025-11-12 04:12:57,105 - INFO - Quelle https://cheatsheetseries.owasp.org/ erfolgreich gecrawlt. +2025-11-12 04:12:57,111 - INFO - Lösche Zustandsdatei. +2025-11-12 04:12:57,114 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:12:57,118 - INFO - Finished source: https://cheatsheetseries.owasp.org/ +2025-11-12 04:12:57,121 - INFO - Processing source: https://nodered.org/docs/ +2025-11-12 04:12:57,125 - INFO - Starte Quelle: https://nodered.org/docs/ (Modus: domain_wide) +2025-11-12 04:12:57,127 - INFO - Crawle: https://nodered.org/docs/ +2025-11-12 04:12:57,130 - INFO - Lade robots.txt von: https://nodered.org/robots.txt +2025-11-12 04:12:57,345 - WARNING - robots.txt verbietet: https://nodered.org/docs/ +2025-11-12 04:12:59,350 - INFO - Quelle https://nodered.org/docs/ erfolgreich gecrawlt. +2025-11-12 04:12:59,354 - INFO - Lösche Zustandsdatei. +2025-11-12 04:12:59,357 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:12:59,359 - INFO - Finished source: https://nodered.org/docs/ +2025-11-12 04:12:59,361 - INFO - Processing source: https://www.home-assistant.io/docs/ +2025-11-12 04:12:59,364 - INFO - Starte Quelle: https://www.home-assistant.io/docs/ (Modus: domain_wide) +2025-11-12 04:12:59,367 - INFO - Crawle: https://www.home-assistant.io/docs/ +2025-11-12 04:12:59,370 - INFO - Lade robots.txt von: https://www.home-assistant.io/robots.txt +2025-11-12 04:12:59,775 - ERROR - Allgemeiner Fehler beim Crawlen von https://www.home-assistant.io/docs/: [Errno 13] Permission denied: '/app' +2025-11-12 04:13:01,779 - INFO - Quelle https://www.home-assistant.io/docs/ erfolgreich gecrawlt. +2025-11-12 04:13:01,783 - INFO - Lösche Zustandsdatei. +2025-11-12 04:13:01,787 - INFO - Datenbankverbindung für Hashes geschlossen. +2025-11-12 04:13:01,790 - INFO - Finished source: https://www.home-assistant.io/docs/ +2025-11-12 04:13:01,794 - INFO - All crawling tasks finished. +2025-11-12 04:13:01,796 - INFO - --- Gesamte Crawling-Statistik --- +2025-11-12 04:13:01,799 - INFO - Gesamtdauer: 540.93 Sekunden +2025-11-12 04:13:01,801 - INFO - Besuchte Seiten insgesamt: 166 +2025-11-12 04:13:01,804 - INFO - Gespeicherte Seiten insgesamt: 0 +2025-11-12 04:13:01,806 - INFO - Fehler insgesamt: 151 +2025-11-12 04:13:01,808 - INFO - Gesamtdatenvolumen: 0.00 MB +2025-11-12 04:13:01,811 - INFO - ------------------------------------ diff --git a/crawler_logs.db b/crawler_logs.db new file mode 100644 index 0000000..21f7d65 Binary files /dev/null and b/crawler_logs.db differ diff --git a/git_sync.sh b/git_sync.sh new file mode 100755 index 0000000..2d59fe7 --- /dev/null +++ b/git_sync.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# ===================================================== +# Git Projekt Management Script +# ===================================================== +# Funktionen: +# 1) Erstmaliges Hochladen (Repo initialisieren + push) +# 2) Änderungen hochladen (commit + push) +# 3) Projekt vom Server herunterladen (clone) +# 4) Benutzername für Git-Server speichern +# ===================================================== + +# === Konfiguration === +REMOTE_URL="http://192.168.19.10:3020/madgerm/web-crawler.git" +# Wenn dein Login eine E-Mail ist, benutze %40 statt @ im REMOTE_URL: +# Beispiel: http://madgerm%40msn.com@192.168.19.10:3020/madgerm/MiniMal.git + +echo "---------------------------------------------" +echo " GIT PROJECT MANAGER" +echo "---------------------------------------------" +echo "1) Projekt erstmalig hochladen" +echo "2) Änderungen hochladen (Standard)" +echo "3) Projekt vom Server herunterladen" +echo "4) Git-Login (Benutzername) speichern" +echo +read -p "Wähle eine Option [1-4, Standard=2]: " choice + +# Wenn der Benutzer einfach Enter drückt, wird 2 gesetzt +choice=${choice:-2} + +case $choice in + 1) + echo ">>> Projekt wird initialisiert und hochgeladen..." + git init + git branch -M main + git add . + git commit -m "Initial commit" + git remote add origin "$REMOTE_URL" + git push -u origin main + ;; + 2) + echo ">>> Änderungen werden zum Server hochgeladen..." + git add . + git commit -m "Auto sync: $(date '+%Y-%m-%d %H:%M:%S')" + git push origin main + ;; + 3) + echo ">>> Projekt wird vom Server heruntergeladen..." + read -p "Zielordner (Standard: aktuelles Verzeichnis): " TARGET_DIR + TARGET_DIR=${TARGET_DIR:-.} + git clone "$REMOTE_URL" "$TARGET_DIR" + ;; + 4) + echo ">>> Git-Benutzernamen speichern..." + read -p "Gib deinen Git-Login (z.B. madgerm@msn.com) ein: " GITUSER + if [ -n "$GITUSER" ]; then + git config --global user.name "$GITUSER" + git config --global credential.username "$GITUSER" + echo "✅ Benutzername gespeichert:" + echo " user.name = $(git config --global user.name)" + echo " credential.username = $(git config --global credential.username)" + echo + echo "💡 Hinweis: Wenn du dein Passwort speichern möchtest:" + echo " git config --global credential.helper store" + else + echo "⚠️ Kein Benutzername eingegeben. Vorgang abgebrochen." + fi + ;; + *) + echo "Ungültige Eingabe. Abbruch." + ;; +esac diff --git a/src/__pycache__/crawler_core.cpython-312.pyc b/src/__pycache__/crawler_core.cpython-312.pyc new file mode 100644 index 0000000..7cd1024 Binary files /dev/null and b/src/__pycache__/crawler_core.cpython-312.pyc differ diff --git a/src/__pycache__/db_logger.cpython-312.pyc b/src/__pycache__/db_logger.cpython-312.pyc new file mode 100644 index 0000000..1147b61 Binary files /dev/null and b/src/__pycache__/db_logger.cpython-312.pyc differ diff --git a/src/__pycache__/duplicate_detector.cpython-312.pyc b/src/__pycache__/duplicate_detector.cpython-312.pyc new file mode 100644 index 0000000..e78f8f6 Binary files /dev/null and b/src/__pycache__/duplicate_detector.cpython-312.pyc differ diff --git a/src/__pycache__/hash_manager.cpython-312.pyc b/src/__pycache__/hash_manager.cpython-312.pyc new file mode 100644 index 0000000..54fe2f2 Binary files /dev/null and b/src/__pycache__/hash_manager.cpython-312.pyc differ diff --git a/src/__pycache__/html_cleaner.cpython-312.pyc b/src/__pycache__/html_cleaner.cpython-312.pyc new file mode 100644 index 0000000..f257829 Binary files /dev/null and b/src/__pycache__/html_cleaner.cpython-312.pyc differ diff --git a/src/__pycache__/logger_setup.cpython-312.pyc b/src/__pycache__/logger_setup.cpython-312.pyc new file mode 100644 index 0000000..a00f20d Binary files /dev/null and b/src/__pycache__/logger_setup.cpython-312.pyc differ diff --git a/src/__pycache__/state_manager.cpython-312.pyc b/src/__pycache__/state_manager.cpython-312.pyc new file mode 100644 index 0000000..a055c36 Binary files /dev/null and b/src/__pycache__/state_manager.cpython-312.pyc differ diff --git a/src/__pycache__/stats_manager.cpython-312.pyc b/src/__pycache__/stats_manager.cpython-312.pyc new file mode 100644 index 0000000..eae8f4f Binary files /dev/null and b/src/__pycache__/stats_manager.cpython-312.pyc differ diff --git a/src/__pycache__/storage.cpython-312.pyc b/src/__pycache__/storage.cpython-312.pyc new file mode 100644 index 0000000..9115f28 Binary files /dev/null and b/src/__pycache__/storage.cpython-312.pyc differ diff --git a/src/__pycache__/url_utils.cpython-312.pyc b/src/__pycache__/url_utils.cpython-312.pyc new file mode 100644 index 0000000..02b0389 Binary files /dev/null and b/src/__pycache__/url_utils.cpython-312.pyc differ diff --git a/src/crawler_core.py b/src/crawler_core.py new file mode 100644 index 0000000..0737739 --- /dev/null +++ b/src/crawler_core.py @@ -0,0 +1,277 @@ +import trafilatura, asyncio +from asyncio import PriorityQueue +import aiohttp +import logging +from urllib.parse import urljoin, urlparse +from urllib import robotparser +from bs4 import BeautifulSoup + +from .url_utils import normalize_url +from .storage import save +from .html_cleaner import clean_html +from .duplicate_detector import DuplicateDetector +from .hash_manager import HashManager +from .state_manager import StateManager +from .stats_manager import CrawlStats + +try: + from simhash import Simhash +except ImportError: + Simhash = None + + +class RobotsTxtChecker: + def __init__(self, user_agent): + self.user_agent = user_agent + self.parsers = {} # Cache: netloc -> robotparser.RobotFileParser + + async def _get_parser(self, session, url): + parsed_url = urlparse(url) + netloc = parsed_url.netloc + + if netloc not in self.parsers: + robots_url = urljoin(url, "/robots.txt") + rp = robotparser.RobotFileParser() + rp.set_url(robots_url) + + logging.info(f"Lade robots.txt von: {robots_url}") + + try: + # Asynchroner Abruf der robots.txt mit aiohttp + async with session.get(robots_url, timeout=aiohttp.ClientTimeout(total=10)) as response: + if response.status == 200: + text = await response.text() + rp.parse(text.splitlines()) + elif response.status == 404: + # Keine robots.txt gefunden, alles erlaubt + pass + else: + logging.warning(f"Fehler beim Abruf von {robots_url}: Status {response.status}") + except aiohttp.ClientError as e: + logging.error(f"Fehler beim Abruf von {robots_url}: {e}") + # Bei Fehler oder Timeout wird der Parser ohne Regeln gecacht (implizit alles erlaubt) + + self.parsers[netloc] = rp + + return self.parsers[netloc] + + async def is_allowed(self, session, url): + rp = await self._get_parser(session, url) + return rp.can_fetch(self.user_agent, url) + +# Instanziierung des Checkers +robots_checker = None + + +def extract_text(html): + text = trafilatura.extract(html) + + # fallback bei Gesetzen / technischen Dokus + if not text or len(text) < 400: + soup = BeautifulSoup(html, "lxml") + text = soup.get_text(" ", strip=True) + + if not text or len(text) < 100: + return None + + return text + + +def extract_title(html): + """Extrahiert den Titel aus dem HTML-Inhalt.""" + soup = BeautifulSoup(html, "lxml") + title_tag = soup.find("title") + if title_tag: + return title_tag.string.strip() + return "Kein Titel gefunden" + + +def allowed_link(base_url_normalized, base_path, next_url, crawl_mode, blocked_patterns, path_strict): + # Normalisiere die URL für Deduplizierung und Fragment-/Tracking-Entfernung + normalized_url = normalize_url(next_url) + + if any(pat in normalized_url for pat in blocked_patterns): + return False + + if crawl_mode == "single_page": + return False + + # Domain-Limit + # base_url_normalized ist die normalisierte Start-URL der Quelle + if urlparse(normalized_url).netloc != urlparse(base_url_normalized).netloc: + return False + + # Pfadbegrenzung + if crawl_mode == "path_limited": + # Strikte Pfadbegrenzung: Der Pfad der gefundenen URL muss mit dem Basis-Pfad beginnen. + # Wir verwenden urlparse, um den Pfad der gefundenen URL zu erhalten. + next_path = urlparse(normalized_url).path + if not next_path.startswith(base_path): + return False + + # Legacy path_strict (falls noch verwendet, obwohl path_limited dies ersetzen sollte) + if path_strict and not normalized_url.startswith(base_url_normalized): + return False + + return normalized_url # Gibt die normalisierte URL zurück, wenn erlaubt + + +async def crawl_source(session, source, config): + stats = CrawlStats() + global robots_checker + if not robots_checker: + user_agent = config.get("USER_AGENT", {}).get("value") + robots_checker = RobotsTxtChecker(user_agent) + + start_urls = source["start_urls"] + crawl_mode = source.get("crawl_mode", "domain_wide") + blocked_patterns = source.get("blocked_patterns", []) + path_strict = source.get("path_strict", False) + + # Lade Konfigurationswerte + page_limit = config.get("PAGE_LIMIT", {}).get("value", 200) + crawl_delay = config.get("CRAWL_DELAY", {}).get("value", 1) + min_content_length = config.get("MIN_CONTENT_LENGTH", {}).get("value", 500) + priority_patterns = config.get("priority_patterns", {}).get("value", []) + duplicate_detection_config = config.get("duplicate_detection", {}).get("value", {}) + incremental_crawling_config = config.get("incremental_crawling", {}).get("value", {}) + state_management_config = config.get("state_management", {}).get("value", {}) + output_dir = config.get("OUTPUT_DIR", {}).get("value") + + # Initialisiere DuplicateDetector, falls aktiviert + duplicate_detector = None + if duplicate_detection_config.get("enable"): + duplicate_detector = DuplicateDetector( + similarity_threshold=duplicate_detection_config.get("similarity_threshold", 95) + ) + + # Initialisiere HashManager, falls aktiviert + hash_manager = None + if incremental_crawling_config.get("enable"): + db_file = incremental_crawling_config.get("db_file", "crawled_hashes.db") + hash_manager = HashManager(db_file) + + # --- Zustandsverwaltung --- + state_manager = None + queue = PriorityQueue() + visited = set() + normalized_start_urls = [normalize_url(u) for u in start_urls] + base_url_normalized = normalized_start_urls[0] + + if state_management_config.get("enable"): + state_file_template = state_management_config.get("state_file", "crawler_state.json") + source_netloc = urlparse(base_url_normalized).netloc + state_file = state_file_template.replace(".json", f"_{source_netloc}.json") + state_manager = StateManager(state_file) + + loaded_state = state_manager.load_state() + if loaded_state: + logging.info(f"Lade Zustand aus {state_file}") + queue, visited = loaded_state + else: + for u in normalized_start_urls: + await queue.put((0, u)) + else: + for u in normalized_start_urls: + await queue.put((0, u)) + # ------------------------- + + # Extrahiere den Basis-Pfad für path_limited Modus + base_path = urlparse(base_url_normalized).path + + logging.info(f"Starte Quelle: {start_urls[0]} (Modus: {crawl_mode})") + + try: + while not queue.empty() and len(visited) < page_limit: + priority, url = await queue.get() # Entpacke Priorität und URL + + if url in visited: + continue + visited.add(url) + stats.total_visited += 1 + + logging.info(f"Crawle: {url}") + + # Asynchrone robots.txt Prüfung + if not await robots_checker.is_allowed(session, url): + logging.warning(f"robots.txt verbietet: {url}") + await asyncio.sleep(crawl_delay) # Respektiere den Delay, auch wenn übersprungen wird + continue + + try: + # Asynchroner Abruf mit aiohttp + async with session.get(url, timeout=aiohttp.ClientTimeout(total=20)) as response: + response.raise_for_status() # Wirft HTTPStatusError für 4xx/5xx + html = await response.text() + + cleaned_html = clean_html(html, config) + text = extract_text(cleaned_html) + + if text and len(text) >= min_content_length: + # Simhash-Berechnung, falls für inkrementelles Crawling benötigt + new_simhash_value = None + if hash_manager and Simhash: + new_simhash_value = Simhash(text).value + + # Inkrementelles Crawling: Prüfen, ob der Inhalt unverändert ist + if hash_manager and new_simhash_value is not None: + if hash_manager.is_unchanged(url, new_simhash_value): + logging.info(f"Inhalt unverändert, übersprungen: {url}") + continue + + # Duplikat-Erkennung + if duplicate_detector and duplicate_detector.is_duplicate(text): + logging.info(f"Duplikat übersprungen: {url}") + else: + # Speichern und Hashes aktualisieren + title = extract_title(cleaned_html) + save(url, title, text, output_dir) + stats.total_saved += 1 + stats.total_data_volume += len(text.encode('utf-8')) + if duplicate_detector: + duplicate_detector.add_hash(text) + if hash_manager and new_simhash_value is not None: + hash_manager.update_hash(url, new_simhash_value) + else: + logging.info(f"Inhalt verworfen (Länge: {len(text) if text else 0} < {min_content_length}): {url}") + + if crawl_mode != "single_page": + soup = BeautifulSoup(html, "lxml") + for link in soup.find_all("a", href=True): + next_url = urljoin(url, link["href"]) + + # allowed_link gibt False oder die normalisierte URL zurück + normalized_next_url = allowed_link(base_url_normalized, base_path, next_url, crawl_mode, blocked_patterns, path_strict) + + if normalized_next_url and normalized_next_url not in visited: + # Priorität basierend auf Mustern bestimmen + prio = 0 if any(pat in normalized_next_url for pat in priority_patterns) else 1 + await queue.put((prio, normalized_next_url)) + + except aiohttp.ClientError as e: + logging.error(f"aiohttp Fehler beim Abruf von {url}: {e}") + stats.errors += 1 + except Exception as e: + logging.error(f"Allgemeiner Fehler beim Crawlen von {url}: {e}") + stats.errors += 1 + + await asyncio.sleep(crawl_delay) + + except (KeyboardInterrupt, Exception): + if state_manager: + logging.warning("Prozess unterbrochen. Speichere aktuellen Zustand...") + state_manager.save_state(queue, visited) + logging.info("Zustand gespeichert.") + raise + else: + # This block executes when the try block completes without an exception. + logging.info(f"Quelle {base_url_normalized} erfolgreich gecrawlt.") + if state_manager: + logging.info("Lösche Zustandsdatei.") + state_manager.delete_state() + # Schließe die Datenbankverbindung des HashManagers + if hash_manager: + hash_manager.close() + + stats.finish() + return stats \ No newline at end of file diff --git a/src/db_logger.py b/src/db_logger.py new file mode 100644 index 0000000..4c174cd --- /dev/null +++ b/src/db_logger.py @@ -0,0 +1,169 @@ +import logging +import sqlite3 +import warnings + +# Platzhalter für mysql.connector, um Importfehler zu vermeiden, wenn es nicht installiert ist +try: + import mysql.connector + MYSQL_AVAILABLE = True +except ImportError: + MYSQL_AVAILABLE = False + warnings.warn("Das 'mysql-connector-python' Paket ist nicht installiert. Der MySQLHandler ist nicht verfügbar.") + +class SQLiteHandler(logging.Handler): + """Ein logging.Handler, der Log-Einträge in eine SQLite-Datenbank schreibt.""" + + def __init__(self, db_file): + super().__init__() + self.db_file = db_file + self._conn = None + self._cursor = None + self._db_initialized = False + + def _init_db(self): + """Initialisiert die Datenbankverbindung und erstellt die Tabelle, falls sie nicht existiert.""" + try: + self._conn = sqlite3.connect(self.db_file) + self._cursor = self._conn.cursor() + self._cursor.execute(''' + CREATE TABLE IF NOT EXISTS logs ( + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + level VARCHAR(10), + message TEXT + ) + ''') + self._conn.commit() + self._db_initialized = True + except sqlite3.Error as e: + warnings.warn(f"Fehler beim Initialisieren der SQLite-Datenbank: {e}") + self._conn = None + + def emit(self, record): + """Schreibt einen Log-Eintrag in die Datenbank.""" + if not self._db_initialized: + self._init_db() + + if not self._conn: + return + + try: + log_entry = (record.levelname, self.format(record)) + self._cursor.execute('INSERT INTO logs (level, message) VALUES (?, ?)', log_entry) + self._conn.commit() + except sqlite3.Error as e: + warnings.warn(f"Fehler beim Schreiben des Logs in die SQLite-Datenbank: {e}") + + def close(self): + """Schließt die Datenbankverbindung.""" + if self._conn: + self._conn.close() + super().close() + +class MySQLHandler(logging.Handler): + """Ein logging.Handler, der Log-Einträge in eine MySQL-Datenbank schreibt.""" + + def __init__(self, host, user, password, database): + super().__init__() + if not MYSQL_AVAILABLE: + self._conn = None + return + + self.db_config = { + 'host': host, + 'user': user, + 'password': password, + 'database': database + } + self._conn = None + self._cursor = None + self._db_initialized = False + + def _init_db(self): + """Initialisiert die Datenbankverbindung und erstellt die Tabelle, falls sie nicht existiert.""" + try: + self._conn = mysql.connector.connect(**self.db_config) + self._cursor = self._conn.cursor() + self._cursor.execute(''' + CREATE TABLE IF NOT EXISTS logs ( + id INT AUTO_INCREMENT PRIMARY KEY, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + level VARCHAR(10), + message TEXT + ) + ''') + self._conn.commit() + self._db_initialized = True + except mysql.connector.Error as e: + warnings.warn(f"Fehler beim Initialisieren der MySQL-Datenbank: {e}") + self._conn = None + + def emit(self, record): + """Schreibt einen Log-Eintrag in die Datenbank.""" + if not MYSQL_AVAILABLE or (not self._db_initialized and not self._conn): + self._init_db() + + if not self._conn: + return + + try: + log_entry = (record.levelname, self.format(record)) + self._cursor.execute('INSERT INTO logs (level, message) VALUES (%s, %s)', log_entry) + self._conn.commit() + except mysql.connector.Error as e: + warnings.warn(f"Fehler beim Schreiben des Logs in die MySQL-Datenbank: {e}") + + def close(self): + """Schließt die Datenbankverbindung.""" + if self._conn: + self._conn.close() + super().close() + +def log_stats_to_mysql(stats, source_url, db_config): + """Schreibt Crawl-Statistiken in eine separate MySQL-Tabelle.""" + if not MYSQL_AVAILABLE: + warnings.warn("MySQL-Connector nicht verfügbar. Statistiken können nicht geloggt werden.") + return + + conn = None + try: + conn = mysql.connector.connect(**db_config) + cursor = conn.cursor() + + # Tabelle erstellen, falls sie nicht existiert + cursor.execute(''' + CREATE TABLE IF NOT EXISTS crawl_stats ( + id INT AUTO_INCREMENT PRIMARY KEY, + source_url VARCHAR(255), + duration_seconds FLOAT, + total_visited INT, + total_saved INT, + total_data_volume_bytes BIGINT, + errors INT, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP + ) + ''') + + # Statistiken einfügen + query = ( + "INSERT INTO crawl_stats (source_url, duration_seconds, total_visited, " + "total_saved, total_data_volume_bytes, errors) " + "VALUES (%s, %s, %s, %s, %s, %s)" + ) + values = ( + source_url, + stats.duration, + stats.total_visited, + stats.total_saved, + stats.total_data_volume, + stats.errors + ) + cursor.execute(query, values) + conn.commit() + logging.info(f"Statistiken für {source_url} erfolgreich in MySQL geloggt.") + + except mysql.connector.Error as e: + warnings.warn(f"Fehler beim Schreiben der Statistiken in die MySQL-Datenbank: {e}") + finally: + if conn and conn.is_connected(): + cursor.close() + conn.close() \ No newline at end of file diff --git a/src/duplicate_detector.py b/src/duplicate_detector.py new file mode 100644 index 0000000..dbfcad1 --- /dev/null +++ b/src/duplicate_detector.py @@ -0,0 +1,62 @@ +import logging + +try: + from simhash import Simhash +except ImportError: + Simhash = None + logging.warning("Die 'simhash'-Bibliothek wurde nicht gefunden. Die Duplikat-Erkennung ist deaktiviert.") + +class DuplicateDetector: + """ + Erkennt und verwaltet Textduplikate mittels SimHash. + """ + def __init__(self, similarity_threshold=95): + """ + Initialisiert den DuplicateDetector. + + Args: + similarity_threshold (int): Der prozentuale Schwellenwert für die Ähnlichkeit, + ab dem zwei Texte als Duplikate gelten. + """ + if Simhash is None: + self.enabled = False + return + + self.enabled = True + self.hashes = set() + self.similarity_threshold = similarity_threshold + + def is_duplicate(self, text): + """ + Überprüft, ob ein Text ein Duplikat eines bereits gesehenen Textes ist. + + Args: + text (str): Der zu überprüfende Text. + + Returns: + bool: True, wenn der Text ein Duplikat ist, andernfalls False. + """ + if not self.enabled: + return False + + new_hash = Simhash(text) + + for existing_hash in self.hashes: + similarity = (1 - (new_hash.distance(existing_hash) / 64.0)) * 100 + if similarity >= self.similarity_threshold: + logging.info(f"Duplikat gefunden mit einer Ähnlichkeit von {similarity:.2f}%.") + return True + return False + + def add_hash(self, text): + """ + Fügt den SimHash des Textes zur Menge der bekannten Hashes hinzu. + + Args: + text (str): Der Text, dessen Hash hinzugefügt werden soll. + """ + if not self.enabled: + return + + new_hash = Simhash(text) + self.hashes.add(new_hash) diff --git a/src/hash_manager.py b/src/hash_manager.py new file mode 100644 index 0000000..251ecb2 --- /dev/null +++ b/src/hash_manager.py @@ -0,0 +1,88 @@ +import sqlite3 +import logging +from datetime import datetime + +logger = logging.getLogger(__name__) + +class HashManager: + """Verwaltet die Speicherung und das Laden von Hashes für das inkrementelle Crawling via SQLite.""" + + def __init__(self, db_path): + """ + Initialisiert den HashManager und die Datenbankverbindung. + + Args: + db_path (str): Der Pfad zur SQLite-Datenbankdatei. + """ + self.db_path = db_path + self.conn = None + try: + self.conn = sqlite3.connect(self.db_path) + self._init_db() + except sqlite3.Error as e: + logger.error(f"Datenbankfehler beim Verbinden mit {self.db_path}: {e}") + raise + + def _init_db(self): + """Erstellt die 'hashes'-Tabelle, falls sie nicht existiert.""" + try: + with self.conn: + self.conn.execute(""" + CREATE TABLE IF NOT EXISTS hashes ( + url TEXT PRIMARY KEY, + simhash TEXT NOT NULL, + timestamp TEXT NOT NULL + ) + """) + except sqlite3.Error as e: + logger.error(f"Fehler beim Initialisieren der Datenbanktabelle: {e}") + + def is_unchanged(self, url, new_simhash): + """ + Prüft, ob sich der Inhalt einer URL geändert hat, indem der SimHash in der DB verglichen wird. + + Args: + url (str): Die zu prüfende URL. + new_simhash (int): Der neue SimHash des Inhalts. + + Returns: + bool: True, wenn die URL bekannt und der SimHash unverändert ist, sonst False. + """ + if not self.conn: + return False + try: + cursor = self.conn.cursor() + cursor.execute("SELECT simhash FROM hashes WHERE url = ?", (url,)) + result = cursor.fetchone() + if result and result[0] == str(new_simhash): + return True + except sqlite3.Error as e: + logger.error(f"Fehler beim Prüfen des Hashes für URL {url}: {e}") + return False + + def update_hash(self, url, simhash): + """ + Aktualisiert den Hash und den Zeitstempel für eine gegebene URL in der Datenbank. + + Args: + url (str): Die URL, deren Hash aktualisiert wird. + simhash (int): Der neue SimHash des Inhalts. + """ + if not self.conn: + return + timestamp = datetime.utcnow().isoformat() + try: + with self.conn: + self.conn.execute(""" + INSERT OR REPLACE INTO hashes (url, simhash, timestamp) + VALUES (?, ?, ?) + """, (url, str(simhash), timestamp)) + logger.debug(f"Hash für URL {url} in der Datenbank aktualisiert.") + except sqlite3.Error as e: + logger.error(f"Fehler beim Aktualisieren des Hashes für URL {url}: {e}") + + def close(self): + """Schließt die Datenbankverbindung.""" + if self.conn: + self.conn.close() + logger.info("Datenbankverbindung für Hashes geschlossen.") \ No newline at end of file diff --git a/src/html_cleaner.py b/src/html_cleaner.py new file mode 100644 index 0000000..6a3888d --- /dev/null +++ b/src/html_cleaner.py @@ -0,0 +1,40 @@ +import re +from bs4 import BeautifulSoup, Comment + +def clean_html(html_content: str, config: dict) -> str: + """ + Bereinigt den HTML-Inhalt, indem unerwünschte Tags und Textmuster entfernt werden. + + Args: + html_content: Der rohe HTML-Inhalt als String. + config: Das Konfigurationsobjekt. + + Returns: + Der bereinigte HTML-Inhalt als String. + """ + cleaner_config = config.get('html_cleaner', {}).get('value', {}) + remove_tags = cleaner_config.get('remove_tags', []) + remove_patterns = cleaner_config.get('remove_patterns', []) + + if not html_content: + return "" + + soup = BeautifulSoup(html_content, 'html.parser') + + # 1. Entferne unerwünschte Tags + for tag_name in remove_tags: + for tag in soup.find_all(tag_name): + tag.decompose() + + # 2. Entferne unerwünschte Textmuster aus dem verbleibenden Inhalt + if remove_patterns: + combined_pattern = "|".join(remove_patterns) + + for element in soup.find_all(string=True): + if element.parent.name in ['style', 'script'] or isinstance(element, Comment): + continue + + new_string = re.sub(combined_pattern, '', str(element), flags=re.IGNORECASE) + element.replace_with(new_string) + + return str(soup) \ No newline at end of file diff --git a/src/logger_setup.py b/src/logger_setup.py new file mode 100644 index 0000000..2014799 --- /dev/null +++ b/src/logger_setup.py @@ -0,0 +1,73 @@ +import logging +import sys +from logging.handlers import RotatingFileHandler +from src.db_logger import SQLiteHandler, MySQLHandler +def setup_logging(config): + """ + Konfiguriert das Logging-System basierend auf der Konfigurationsdatei. + """ + log_config = config.get('log', {}).get('value', {}) + + # Deaktivieren, wenn keine Handler konfiguriert sind + if not log_config or not log_config.get('handlers'): + logging.disable(logging.CRITICAL) + return + + # Root-Logger konfigurieren + logger = logging.getLogger() + if logger.hasHandlers(): + logger.handlers.clear() + logger.setLevel(logging.INFO) + + # Formatter erstellen + formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + + handlers_to_add = log_config.get('handlers', []) + + # File-Handler dynamisch erstellen + if 'file' in handlers_to_add: + file_conf = log_config.get('file', {}) + log_file = file_conf.get('log_file', 'crawler.log') + max_size_mb = file_conf.get('max_size_mb', 10) + keep_last = file_conf.get('keep_last', 5) + max_bytes = max_size_mb * 1024 * 1024 + + file_handler = RotatingFileHandler( + log_file, maxBytes=max_bytes, backupCount=keep_last + ) + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + # Console-Handler dynamisch erstellen + if 'console' in handlers_to_add: + stream_handler = logging.StreamHandler(sys.stdout) + stream_handler.setFormatter(formatter) + logger.addHandler(stream_handler) + + # SQLite-Handler dynamisch erstellen + if 'sqlite' in handlers_to_add: + sqlite_conf = log_config.get('sqlite', {}) + db_file = sqlite_conf.get('db_file') + if db_file: + sqlite_handler = SQLiteHandler(db_file=db_file) + sqlite_handler.setFormatter(formatter) + logger.addHandler(sqlite_handler) + + # MySQL-Handler dynamisch erstellen + if 'mysql' in handlers_to_add: + mysql_conf = log_config.get('mysql', {}) + # Nur initialisieren, wenn alle Konfigurationsschlüssel vorhanden sind + if all(k in mysql_conf for k in ['host', 'user', 'password', 'database']): + mysql_handler = MySQLHandler( + host=mysql_conf['host'], + user=mysql_conf['user'], + password=mysql_conf['password'], + database=mysql_conf['database'] + ) + mysql_handler.setFormatter(formatter) + logger.addHandler(mysql_handler) + + if logger.hasHandlers(): + logging.info("Logging-System erfolgreich initialisiert.") + else: + logging.disable(logging.CRITICAL) diff --git a/src/state_manager.py b/src/state_manager.py new file mode 100644 index 0000000..0eb189a --- /dev/null +++ b/src/state_manager.py @@ -0,0 +1,59 @@ +import json +import os +from queue import PriorityQueue +from typing import Set, Tuple, List, Optional + +class StateManager: + """Manages the state of the crawler for pausing and resuming.""" + + def __init__(self, state_file: str): + """ + Initializes the StateManager. + + Args: + state_file: The path to the file where the state will be saved. + """ + self.state_file = state_file + + def save_state(self, queue: PriorityQueue, visited: Set[str]): + """ + Saves the current state of the queue and visited set to a file. + + Args: + queue: The PriorityQueue to save. + visited: The set of visited URLs to save. + """ + state = { + "queue": list(queue.queue), + "visited": list(visited) + } + with open(self.state_file, 'w') as f: + json.dump(state, f, indent=4) + + def load_state(self) -> Optional[Tuple[PriorityQueue, Set[str]]]: + """ + Loads the state from the state file if it exists. + + Returns: + A tuple containing the PriorityQueue and the set of visited URLs, + or None if no state file is found. + """ + if not os.path.exists(self.state_file): + return None + + with open(self.state_file, 'r') as f: + state = json.load(f) + + queue = PriorityQueue() + for item in state["queue"]: + # The items are stored as lists in JSON, convert them back to tuples + queue.put(tuple(item)) + + visited = set(state["visited"]) + + return queue, visited + + def delete_state(self): + """Deletes the state file if it exists.""" + if os.path.exists(self.state_file): + os.remove(self.state_file) \ No newline at end of file diff --git a/src/stats_manager.py b/src/stats_manager.py new file mode 100644 index 0000000..6de8192 --- /dev/null +++ b/src/stats_manager.py @@ -0,0 +1,19 @@ +import time + +class CrawlStats: + def __init__(self): + self.start_time = time.time() + self.end_time = None + self.total_visited = 0 + self.total_saved = 0 + self.total_data_volume = 0 + self.errors = 0 + + def finish(self): + self.end_time = time.time() + + @property + def duration(self): + if self.end_time: + return self.end_time - self.start_time + return time.time() - self.start_time \ No newline at end of file diff --git a/src/storage.py b/src/storage.py new file mode 100644 index 0000000..4a793ff --- /dev/null +++ b/src/storage.py @@ -0,0 +1,70 @@ +import hashlib +import json +import logging +from datetime import datetime +from pathlib import Path +from urllib.parse import urlparse + +def get_safe_path(url): + """ + Generiert einen sicheren, hierarchischen Pfad basierend auf der URL. + Ersetzt ungültige Dateisystemzeichen und verwendet die Domain/den Pfad. + """ + parsed = urlparse(url) + netloc = parsed.netloc + path = parsed.path.strip('/') + + # Ersetze ungültige Zeichen im Pfad (z.B. Query-Trenner) durch Unterstriche + # Da die URL bereits normalisiert ist, sollte die Query leer sein, aber wir sichern ab. + safe_path = path.replace(':', '_').replace('*', '_').replace('"', '_').replace('<', '_').replace('>', '_').replace('|', '_').replace('?', '_').replace('&', '_') + + # Füge die Domain hinzu + full_path = Path(netloc) / safe_path + + # Wenn der Pfad leer ist (z.B. bei der Root-URL), verwenden wir 'index' + if not full_path.name: + full_path = full_path / "index" + + # Füge die Dateiendung hinzu (z.B. .json, da wir später JSON speichern) + return full_path.with_suffix(".json") + +def save(url, title, content, output_dir): + """ + Speichert die extrahierten Daten (URL, Titel, Zeitstempel, Inhalt) als JSON-Objekt. + """ + + # 1. Generiere den Zeitstempel im ISO 8601 Format + timestamp = datetime.now().isoformat() + + # 2. Erstelle das JSON-Datenobjekt + data = { + "url": url, + "title": title, + "timestamp": timestamp, + "content": content + } + + # 3. Generiere den hierarchischen Pfad (für Browsability) + relative_path = get_safe_path(url) + + # 4. Kombiniere Output-Dir und relativen Pfad + output_path = Path(output_dir) + fname = output_path / relative_path + + # Stelle sicher, dass das Verzeichnis existiert + fname.parent.mkdir(parents=True, exist_ok=True) + + # Speichere nur, wenn die Datei nicht existiert + if not fname.exists(): + # Speichere das JSON-Objekt + try: + json_content = json.dumps(data, ensure_ascii=False, indent=4) + fname.write_text(json_content, encoding="utf-8") + logging.info(f"Gespeichert als {fname.relative_to(output_path)}") + except Exception as e: + logging.error(f"Fehler beim Speichern von JSON für {url}: {e}") + else: + # Wenn die Datei existiert, überspringen wir das Speichern. + logging.info(f"Datei existiert bereits: {fname.relative_to(output_path)}") + + return fname \ No newline at end of file diff --git a/src/url_utils.py b/src/url_utils.py new file mode 100644 index 0000000..69a9305 --- /dev/null +++ b/src/url_utils.py @@ -0,0 +1,30 @@ +from urllib.parse import urlparse, parse_qs, urlunparse, urlencode + +# Liste gängiger Tracking-Parameter, die entfernt werden sollen +TRACKING_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'session', 'ref', 'gclid', 'fbclid'] + +def normalize_url(url): + """ + Normalisiert eine URL, indem Fragmente entfernt und gängige Tracking-Parameter + aus der Query-Komponente entfernt werden. + """ + parsed = urlparse(url) + + # 1. Fragment entfernen + # 2. Query-Parameter bereinigen + + query_params = parse_qs(parsed.query) + + # Tracking-Parameter entfernen + cleaned_params = {k: v for k, v in query_params.items() if k.lower() not in TRACKING_PARAMS} + + # Query neu zusammensetzen + # Da parse_qs Listen von Werten zurückgibt, müssen wir sie für urlunparse/urlencode + # in das Standardformat zurückführen (z.B. k=v1&k=v2) + + cleaned_query = urlencode(cleaned_params, doseq=True) + + # urlunparse erwartet eine Liste von 6 Elementen: scheme, netloc, path, params, query, fragment + normalized_url = urlunparse(parsed._replace(query=cleaned_query, fragment='')) + + return normalized_url \ No newline at end of file diff --git a/src/web_server.py b/src/web_server.py new file mode 100644 index 0000000..3250ebd --- /dev/null +++ b/src/web_server.py @@ -0,0 +1,32 @@ +import http.server +import socketserver +import os + +class ReadmeHandler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + readme_path = os.path.join(os.path.dirname(__file__), '..', 'README.md') + try: + with open(readme_path, 'r', encoding='utf-8') as f: + content = f.read() + self.send_response(200) + self.send_header('Content-type', 'text/plain; charset=utf-8') + self.end_headers() + self.wfile.write(content.encode('utf-8')) + except FileNotFoundError: + self.send_error(404, 'File Not Found: README.md') + +def start_web_server(): + PORT = 8000 + httpd = None + try: + handler = ReadmeHandler + httpd = socketserver.TCPServer(("", PORT), handler) + print(f"Serving on port {PORT}") + httpd.serve_forever() + finally: + if httpd: + httpd.server_close() + print("Server closed.") + +if __name__ == "__main__": + start_web_server() \ No newline at end of file diff --git a/start.py b/start.py new file mode 100644 index 0000000..8b58ffb --- /dev/null +++ b/start.py @@ -0,0 +1,62 @@ +import json +import os +import logging +import asyncio +import aiohttp +from src.logger_setup import setup_logging +from src.crawler_core import crawl_source +from src.stats_manager import CrawlStats + +def display_stats(all_stats): + """Displays aggregated statistics from all crawl runs.""" + total_visited = sum(s.total_visited for s in all_stats) + total_saved = sum(s.total_saved for s in all_stats) + total_errors = sum(s.errors for s in all_stats) + total_duration = sum(s.duration for s in all_stats) + total_data_mb = sum(s.total_data_volume for s in all_stats) / (1024 * 1024) + + logging.info("--- Gesamte Crawling-Statistik ---") + logging.info(f"Gesamtdauer: {total_duration:.2f} Sekunden") + logging.info(f"Besuchte Seiten insgesamt: {total_visited}") + logging.info(f"Gespeicherte Seiten insgesamt: {total_saved}") + logging.info(f"Fehler insgesamt: {total_errors}") + logging.info(f"Gesamtdatenvolumen: {total_data_mb:.2f} MB") + logging.info("------------------------------------") + +async def main(): + """ + Main function to initialize and run the web crawler for all sources. + """ + # Load configuration + with open('config.json', 'r') as f: + config = json.load(f) + + # Load URL list + with open('url_list.json', 'r') as f: + url_list = json.load(f) + + # Setup logging + setup_logging(config) + logger = logging.getLogger(__name__) + + all_stats = [] + + try: + async with aiohttp.ClientSession() as session: + for source in url_list: + logger.info(f"Processing source: {source['start_urls'][0]}") + stats = await crawl_source(session, source, config) + all_stats.append(stats) + logger.info(f"Finished source: {source['start_urls'][0]}") + + except Exception as e: + logger.critical(f"A critical error occurred in the main loop: {e}", exc_info=True) + finally: + logger.info("All crawling tasks finished.") + display_stats(all_stats) + + +if __name__ == "__main__": + # Change the current working directory to the script's directory + os.chdir(os.path.dirname(os.path.abspath(__file__))) + asyncio.run(main()) \ No newline at end of file diff --git a/url_list.json b/url_list.json new file mode 100644 index 0000000..f065616 --- /dev/null +++ b/url_list.json @@ -0,0 +1,308 @@ +[ + { + "start_urls": [ + "https://sqlite.org/docs.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/ao_1977/BJNR006130976.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/gmbhg/BJNR004770892.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/pangv_2022/BJNR492110021.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/ustg_1980/BJNR119530979.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/uwg_2004/BJNR141400004.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/hgb/BJNR002190897.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/bgb/BJNR001950896.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/fahrschausbo_2012/BJNR131800012.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/fahrlpr_fv/BJNR004200018.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/fahrlg_2018/BJNR216210017.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/fahrlausbv/BJNR001500018.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/pbefg/BJNR002410961.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/g_kg_1998/BJNR148510998.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/fev_2010/BJNR198000010.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/stvzo_2012/BJNR067910012.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/stvg/BJNR004370909.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/stvoausnv_8/BJNR113000998.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/stvoausnv_5/BJNR062300994.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/stvoausnv_9/BJNR317100998.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/stvoausnv_12/BJNR086600005.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/stvoausnv_4/BJNR011240992.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gesetze-im-internet.de/stvo_2013/BJNR036710013.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://eur-lex.europa.eu/legal-content/DE/TXT/HTML/?uri=CELEX:32016R0679" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.gnu.org/software/bash/manual/bash.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.json.org/json-en.html" + ], + "crawl_mode": "single_page" + }, + { + "start_urls": [ + "https://www.bussgeldkatalog.org/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://docs.budibase.com/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://shadowhelix.de/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://markdown.de/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://mariadb.com/docs/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://html.spec.whatwg.org/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://restfulapi.net/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://react.dev/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://expressjs.com/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://docs.docker.com/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://docs.openwebui.com/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://www.php.net/manual/en/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://www.postgresql.org/docs/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://nginx.org/en/docs/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://docs.nginx.com/nginx/admin-guide/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://wordpress.org/documentation/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://github.com/fail2ban/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "httpshttps://grafana.com/docs/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://nodejs.org/docs/latest/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://www.virtualmin.com/docs/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://developer.mozilla.org/en-US/docs/Web/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://owasp.org/www-project-top-ten/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://cheatsheetseries.owasp.org/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://nodered.org/docs/" + ], + "crawl_mode": "domain_wide" + }, + { + "start_urls": [ + "https://www.home-assistant.io/docs/" + ], + "crawl_mode": "domain_wide" + } +] \ No newline at end of file