Syntaxfehler in crawler.py behoben und Admin-Benachrichtigungen für Website-Fehler implementiert
This commit is contained in:
49
crawler.py
49
crawler.py
@@ -5,6 +5,7 @@ import difflib
|
|||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from urllib.parse import urljoin, urlparse
|
from urllib.parse import urljoin, urlparse
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -34,6 +35,18 @@ scraper = cloudscraper.create_scraper(
|
|||||||
# --- DATENBANK SETUP ---
|
# --- DATENBANK SETUP ---
|
||||||
DB_PATH = os.path.join(BASE_DIR, "polit_scraper.db")
|
DB_PATH = os.path.join(BASE_DIR, "polit_scraper.db")
|
||||||
|
|
||||||
|
def sende_admin(titel, nachricht):
|
||||||
|
admin_url = f"{os.getenv('NTFY_BASE')}/polit-scraper-admin"
|
||||||
|
try:
|
||||||
|
requests.post(
|
||||||
|
admin_url,
|
||||||
|
data=nachricht.encode("utf-8"),
|
||||||
|
headers={"Title": titel.encode("utf-8"), "Priority": "4"},
|
||||||
|
auth=(os.getenv("ADMIN_USER"), os.getenv("ADMIN_PW")),
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
except: pass
|
||||||
|
|
||||||
def setup_db():
|
def setup_db():
|
||||||
conn = sqlite3.connect(DB_PATH, timeout=20)
|
conn = sqlite3.connect(DB_PATH, timeout=20)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
@@ -58,19 +71,13 @@ def setup_db():
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def ist_sinnvoller_text(text):
|
def ist_sinnvoller_text(text):
|
||||||
# Ignoriere Texte, die sehr kurz sind (wahrscheinlich Navigations-Links)
|
|
||||||
if len(text.strip()) < 300:
|
if len(text.strip()) < 300:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Prüfe auf zu viele generische Wörter, die auf Navigations-Seiten hindeuten
|
|
||||||
nav_keywords = ["startseite", "lokales", "aktuelles", "veranstaltungen", "kontakt", "impressum", "baustelle", "meldungen"]
|
nav_keywords = ["startseite", "lokales", "aktuelles", "veranstaltungen", "kontakt", "impressum", "baustelle", "meldungen"]
|
||||||
text_lower = text.lower()
|
text_lower = text.lower()
|
||||||
|
|
||||||
match_count = sum(text_lower.count(k) for k in nav_keywords)
|
match_count = sum(text_lower.count(k) for k in nav_keywords)
|
||||||
# Wenn mehr als 5 Navigations-Begriffe vorkommen, ist es vermutlich eine Nav-Seite
|
|
||||||
if match_count > 5:
|
if match_count > 5:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def queue_article(url, text, source_url):
|
def queue_article(url, text, source_url):
|
||||||
@@ -112,17 +119,10 @@ def hole_artikel_text(url):
|
|||||||
try:
|
try:
|
||||||
antwort = scraper.get(url, timeout=10)
|
antwort = scraper.get(url, timeout=10)
|
||||||
soup = BeautifulSoup(antwort.text, "html.parser")
|
soup = BeautifulSoup(antwort.text, "html.parser")
|
||||||
|
|
||||||
# Müll entfernen
|
|
||||||
for element in soup(["header", "footer", "nav", "script", "style", "aside"]):
|
for element in soup(["header", "footer", "nav", "script", "style", "aside"]):
|
||||||
element.decompose()
|
element.decompose()
|
||||||
|
|
||||||
# Gezielter suchen
|
|
||||||
bereich = soup.find("main") or soup.find("article") or soup.find("div", class_="content")
|
bereich = soup.find("main") or soup.find("article") or soup.find("div", class_="content")
|
||||||
|
if not bereich: return None
|
||||||
if not bereich:
|
|
||||||
return None
|
|
||||||
|
|
||||||
text = bereich.get_text(separator=" ", strip=True)
|
text = bereich.get_text(separator=" ", strip=True)
|
||||||
return text[:12000]
|
return text[:12000]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -143,35 +143,16 @@ def verarbeite_website(url):
|
|||||||
try:
|
try:
|
||||||
conn = sqlite3.connect(DB_PATH, timeout=20)
|
conn = sqlite3.connect(DB_PATH, timeout=20)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
import requests
|
|
||||||
from urllib.parse import urljoin, urlparse
|
|
||||||
|
|
||||||
# ...
|
|
||||||
def sende_admin(titel, nachricht):
|
|
||||||
admin_url = f"{os.getenv('NTFY_BASE')}/polit-scraper-admin"
|
|
||||||
try:
|
|
||||||
requests.post(
|
|
||||||
admin_url,
|
|
||||||
data=nachricht.encode("utf-8"),
|
|
||||||
headers={"Title": titel.encode("utf-8"), "Priority": "4"},
|
|
||||||
auth=(os.getenv("ADMIN_USER"), os.getenv("ADMIN_PW")),
|
|
||||||
timeout=5
|
|
||||||
)
|
|
||||||
except: pass
|
|
||||||
|
|
||||||
# ... in verarbeite_website ...
|
|
||||||
antwort = scraper.get(url, timeout=15)
|
antwort = scraper.get(url, timeout=15)
|
||||||
if antwort.status_code != 200:
|
if antwort.status_code != 200:
|
||||||
logger.error(f"Website Fehler: {url} Status {antwort.status_code}")
|
logger.error(f"Website Fehler: {url} Status {antwort.status_code}")
|
||||||
sende_admin("Crawler Fehler", f"URL {url} lieferte Status {antwort.status_code}.")
|
sende_admin("Crawler Fehler", f"URL {url} lieferte Status {antwort.status_code}.")
|
||||||
return
|
return
|
||||||
soup = BeautifulSoup(antwort.text, "html.parser")
|
|
||||||
|
|
||||||
# Müll entfernen
|
soup = BeautifulSoup(antwort.text, "html.parser")
|
||||||
for element in soup(["header", "footer", "nav", "script", "style", "aside"]):
|
for element in soup(["header", "footer", "nav", "script", "style", "aside"]):
|
||||||
element.decompose()
|
element.decompose()
|
||||||
|
|
||||||
# Gezielter suchen
|
|
||||||
hauptbereich = soup.find("main") or soup.find("article") or soup.find("div", class_="content")
|
hauptbereich = soup.find("main") or soup.find("article") or soup.find("div", class_="content")
|
||||||
if not hauptbereich:
|
if not hauptbereich:
|
||||||
logger.debug(f"Überspringe Seite ohne Inhalts-Struktur: {url}")
|
logger.debug(f"Überspringe Seite ohne Inhalts-Struktur: {url}")
|
||||||
|
|||||||
Reference in New Issue
Block a user