---
author:
- |
Tristan Nolde\
Fachhochschule Südwestfalen\
Schriftliche Ausarbeitung im Modul\
„Projektgruppe"\
im Studiengang\
M.Sc. Angewandte Künstliche Intelligenz\
eingereicht bei\
Prof. Dr.-Ing. Doga Arinir
bibliography: literatur.bib
title: Automatische Daten Extraktion aus Web Quellen
---
# Automatische Daten Extraktion aus Web Quellen
## Erklärung
Ich erkläre hiermit, dass ich die vorliegende Arbeit selbstständig
verfasst und dabei keine anderen als die angegebenen Hilfsmittel benutzt
habe. Sämtliche Stellen der Arbeit, die im Wortlaut oder dem Sinn nach
Werken anderer Autoren entnommen sind, habe ich als solche kenntlich
gemacht. Die Arbeit wurde bisher weder gesamt noch in Teilen einer
anderen Prüfungsbehörde vorgelegt und auch noch nicht veröffentlicht.
2023-10-03
Tristan Nolde
## Einleitung
Im Projekt *Transparenzregister* steht die Analyse sowie Visualisierung
von Unternehmensdaten im Vordergrund. Zu diesen Daten gehören unter
anderem *Key Performance Indicators (KPIs)* im
Bereich Finanzen (z.B. Verlauf des Umsatzes oder Jahresüberschusses)
aber auch grundlegende Stammdaten wie den Sitz des Unternehmens oder die
Besetzung des Aufsichtsrates. Um diese Informationen jedoch darstellen
zu können, bedarf es zunächst der Beschaffung und Aufbereitung von
Daten. In der im Projekt verankerten Architektur bedeutet dies, die
Extraktion von Rohdaten aus verschiedenen Quellen, die Stücke der
benötigten Informationen beinhalten, un den Transport dieser in einem
geeigneten Format in die sog. *Staging DB*. Aus dieser heraus bedienen sich die
verschiedenen Analyse-Prozesse und stellen letztendlich einen vollkommen
aufbereitenden Datenbestand in der *Production DB* zur Verfügung.
Diese Hausarbeit befasst sich mit der Bereitstellung eben dieser
Informationen für die *Staging DB*. Neben der initialen Vermittlung von in
der Forschung und Industrie bewährten Methoden, soll primär die
praktische Umsetzung der Daten Extraktion für die Projektgruppe im
Vordergrund stehen. Dafür werden Verfahren vorgestellt, um die
gewünschten Informationen aus verschiedenen Quellen zu beziehen. Zum
Ende der Arbeit wird ebenfalls ein erster Blick auf die rechtlichen
Rahmenbedingungen dieser Daten-Extraktion sowie ihrer Speicherung
geworfen. Da die während der Arbeit entstehenden Applikationen auch
betrieben und gewartet werden müssen, wird ein Blick auf [Apache
Airflow](https://airflow.apache.org/) als Workflow Orchestrator
geworfen.
Als Quellen für die gewünschte Datenbasis dienen unter anderem die
Folgenden:
- Unternehmensregister
- Bundesanzeiger
- Tagesschau
- Handelsblatt
### Web Mining
Das Web Mining als Unterdisziplin des Data Minings - \"\[..\] einer
Sammlung von Methoden und Vorgehensweisen, mit denen große Datenmengen
effizient analysiert werden können\" [1] - beschäftigt sich
vorwiegend mit der Auswertung von Quellen aus dem Internet. Da das
Internet zu großen Teilen aus textuellen Informationen in Form von
Websites besteht, bildet das Web Mining Schnittmengen zum Text Mining.
Besonders im Text Mining wird Wert auf die Analyse von Dokumenten in
Form von HTML oder XML Dateien gelegt. Das Web Mining legt lediglich die
Schicht der Beschaffung jener Dokumente dazu. [1]
### Extract, Transform, Load (ETL)
Ein grundlegender Prozess, der in allen Data Mining Sub-Formen Anwendung
findet ist der des *ETL*. Dieser beschreibt unabhängig von der
ursprünglichen Datenquelle oder ihrem Ziel, den Wunsch, Daten von einem
System in einer aufbereiteten Form in das Zielsystem zu transportieren.
[1]
Da die Ergebnisse dieses Prozesses die Grundlage für Folge-Schritte wie
eine weitere Analyse oder bereits Darstellung legt, ist hier ein
besonderes Augenmerk auf die Qualität der Rohdaten sowie Ergebnisse zu
legen. Ein bekanntes Sprichwort in diesem Kontext ist *\"Garbage in,
Garbage out\"*. Darunter wird der Zustand verstanden, dass niederwertige
Rohdaten selbst mit komplexer Aufbereitung nicht in qualitativen
Ergebnissen münden können. So ist im Schritte der Extraktion besonderes
Augenmerk auf die Auswahl der Datenquelle bzw. Dokumente zu legen.
[1]
Die Bedeutung von Qualität findet sich ebenfalls in der Transformation
wieder. Hier gilt es nicht nur Daten von einen in das andere Format zu
überführen (Harmonisierung), sondern auch ungeeignete Datensätze oder
Passagen zu erkennen und nicht weiter zu betrachten (Filterung).
[1]
Zuletzt gilt es die aufbereiteten und selektierten Daten in das
Zielsystem - hier die Staging Datenbank - zu überführen. Dieser Prozess
kann je nach zeitlicher Ausführung (initiales Laden, Aktualisierung)
unterschiedlich ablaufen, da entweder neue Daten hinzugefügt oder
Bestehende angepasst werden. [1]
## Web Scraping & Crawling
Eine technische Anwendung des Web Minings ist in Form des *Web
Scrapings* (auch: Web Crawling) zu finden. Zunächst beschreibt das
Scrapen die Extraktion von Daten aus Websites. Dies kann entweder
manuell oder automatisch geschehen. Sollte das zur Automatisierung
entwickelte Programm nicht nur auf einer Zielseite verweilen, sondern
über auf der Seite gefundene Links zu weiteren Seiten vordringen, so ist
vom *Crawling* die Rede. Dieses Vorgehen ist unter anderem bei
Suchmaschinen wie Google anzutreffen. Da diese Technik unter anderem
eingesetzt werden kann, um sensible Daten wie Telefonnummern zu finden,
wurden mit der Zeit einige Mechanismen wie die *robots.txt* Datei
entwickelt, in jener der Betreiber einer Website festlegt, welche
Zugriffe erlaubt sind. [2]
Allerdings hat dieser Mechanismus eine elementare Schachstelle: Die
*robots.txt* Einträge dienen lediglich als Richtlinie für Clients, so
dass Crawler in der Lage sind diese schlichtweg zu ignorieren und
dennoch relevante Daten von der Seite zu scrapen. [3]
### Bezug von Stammdaten aus dem Unternehmensregister
Im Unternehmensregister werden veröffentlichungspflichtige Daten
deutscher Unternehmen wie etwa die Firmengründung oder Liquidation in
elektronischer Art zur Verfügung gestellt. [@unternehmensregister]
Besonders relevant, um überhaupt ein Inventar an Unternehmen aufstellen
zu können, sind die dort zu findenden Registerinformationen. In diesen
werden Daten aus Handels-, Genossenschafts- und Partnerschaftsregistern
bereit gestellt. Das Ziel ist es aus diesen grundlegende Informationen
wie den Sitz des Unternehmens, seine Gesellschaftsform sowie ggf.
verfügbare Relationen zu Personen oder anderen Unternehmen zu beziehen.
Das dazugehörige Ziel-Datenmodell ist im Anhang zu finden.
Zu Beginn der Entwicklung wird zunächst einer erster manueller Download
Prozess von Daten gestartet. Die Website des Unternehmensregisters
stellt eine schlagwort-basierte Suchmaske bereit über die der Nutzer auf
eine Historie der gefundenen Unternehmen geleitet wird. Bereits hier
stellt sich heraus, dass die Suche des Unternehmensregisters keine 1:1
Abgleich des Unternehmensnamens sondern eine ähnlichkeits-basierte Suche
verwendet. So förderte die unten dargestellte Suche nach der \"Bayer
AG\" auch Einträge wie die Bayer Gastronomie GmbH zu Tage:
Auch eine Anpassung des Suchbegriffes durch die Verwendung regulärer
Ausdrücke (z.B. \^̈Bayer AG\$)̈ kann die Qualität der Ergebnisse nicht
verbessern. Da es sich bei diesen ähnlichen Unternehmen jedoch auch
tatsächlich um Tochtergesellschaften handeln könnte, die für die
Verflechtsungsanalyse besonders interessant sind, werden nicht passende
Unternehmensnamen nicht entfernt sondern ebenfalls verarbeitet.
Ist ein Unternehmen ausgewählt, stellt das Unternehmensregister
verschiedene Format zur Verfügung. Für eine automatische Auswertung der
Daten eignet sich besonders der *SI - strukturierter Registerinhalt*,
welcher ein XML-Dokument im Format der XJustiz beinhaltet. Es wird das
Schema *xjustiz_0400_register_3_2.xsd* verwendet.
Da das Unternehmensregister keine öffentliche *Application Programming Interface (API)* hostet, über die dieses Dokument bezogen
werden kann, erfolgt eine Automatisierung des über den Nutzer in der
Web-Oberfläche getätigten Aktionsfluss mithilfe von
[Selenium](https://www.selenium.dev/).
Eine Schritt-für-Schritt Beschreibung des Prozesses würde den Rahmen der
Hausarbeit sprengen, daher wird ein High-Level Überblick in Form eines
Ablaufdiagramms im Anhang bereit gestellt.
Im nachgelagerten Schritt werden die heruntergeladenen *.xml* Dateien in
eine *.json* konvertiert und in das Zielformat überführt. Darauf werden
die Daten über einen zentral gelegenen Service, der die Verbindung zur
StagingDB (MongoDB) in entsprechende Methoden kapselt, in die Persistenz
überführt.
### Bezug von Finanzdaten aus dem Bundesanzeiger
Jede Kapitalgesellschaft in Deutschland ist gemäß §242 des *Handelsgesetzbuches (HGB)* dazu
verpflichtet zu Gründung der Gesellschaft sowie nach jedem abgeschlossen
Geschäftsjahr eine Aufstellung der finanziellen Situation des
Unternehmens zu veröffentlichen. Diese besteht mindestens aus der
Gewinn- und Verlustrechnung, kann jedoch je nach Gesellschaftsform
weitere Details verpflichtend beinhalten. Im Bundesanzeiger, einem
Amtsblatt sowie Online Plattform geleitet vom Bundesministerium der
Justiz, werden diese Jahresabschlüsse der Allgemeinheit zur Verfügung
gestellt.
Aus Sicht des Transparenzregisters eignet sich der Bundesanzeiger daher
als besonders gute Quelle, um Finanzdaten zu bereits bekannten
Unternehmen (siehe vorheriges Kapitel) zu beziehen, um den
wirtschaftlichen Erfolg einer Kapitalgesellschaft quantifizieren zu
können.
Auf GitHub, einem zentralen Anlaufpunkt für Open-Source Projekte, kann
eine Python Bibliothek namens
[deutschland](https://github.com/bundesAPI/deutschland) gefunden, welche
eine Reihe deutscher Datenquellen kapselt und in einem festen Format zur
Verfügung stellt. Unter anderem beinhaltet diese Bibliothek auch ein
Sub-Paket namens *bundesanzeiger*, welches ein Interface zur Oberfläche
des Bundesanzeigers zur Verfügung stellt. Das Projekt wird unter der
[Apache License
2.0](https://github.com/bundesAPI/deutschland/blob/main/LICENSE)
betrieben und ist folglich auch für den Einsatz im Projekt geeignet.
Die erste Integration der Bibliothek zeigt jedoch schnell eine große
Schwäche auf: Im Zuge des Datenbezugs werden die Rohdaten (HTML) bereits
in einfachen Text übersetzt. Dies hat zur Folge, dass wertvolle
Informationen, die sich aus der Struktur des Dokumentes ergeben (z.B.
der Einsatz von Tabellen, um die Aktiva wie Passiva des
Jahresabschlusses aufzulisten). Es wird daher ein [Fork des
Repositories](https://github.com/TrisNol/deutschland/tree/feat/bundesanzeiger-raw-report)
angelegt und der Quellcode so adaptiert, dass auch der Inhalt in seiner
Ursprungsform bereitgestellt wird.
Der damit verbundene Aufwand, um sich in die Bibliothek einzuarbeiten,
gibt hilfreiche Einblicke in die genaue Funktionsweise der
Implementierung: Über eine Reihe gezielter HTTP-Anfragen gekoppelt mit
einer Auswertung über Web Scraping navigiert die Bibliothek durch die
Seiten des Bundesanzeigers. Wird für eine Seite (z.B. Jahresabschlüsse)
ein CAPTCHA - ein Authentifizierungs-Mechanismus im
Challenge-Response-Verfahren, der den Zugang durch Bots verhindern soll
[5] - abgefragt, so wird dieser mittels eines Machine Learning
Models des Chaos Computer Club e.V. gelöst.
Auf Basis dieser Grundlage lassen sich unter anderem Informationen wie
die Angabe des Wirtschaftsprüfers eines Jahresabschlusses extrahieren:
```python
def extract_auditor_company(self, report: str) -> str | None:
soup = BeautifulSoup(report, features="html.parser")
temp = soup.find_all("b")
for elem in temp:
br = elem.findChildren("br")
if len(br) > 0:
return elem.text.split("\n")[1].strip()
return None
def extract_auditors(self, report: str) -> list:
auditor_company = self.extract_auditor_company(report)
auditor_regex = r"[a-z A-Z,.'-]+, Wirtschaftsprüfer"
hits = re.findall(auditor_regex, report)
return [
Auditor(hit.replace(", Wirtschaftsprüfer", "").lstrip(), auditor_company)
for hit in hits
]
```
Diese Methoden werden auf eine anhand des Dokumententyps gefilterte
Liste der bezogenen *Reports* angewandt. Eine Erweiterung auf die
Extraktion von Finanzdaten steht aus.
## Extraktion von News Artikeln
Auch wenn das Ziel des ETL-Prozesses eine MongoDB - also eine NoSQL
Document Datenbank, die in einer Collection Dokumente jeglicher Struktur
akzeptiert - ist, wird vor der Entwicklung ein Datenformat definiert.
Dies soll gewährleisten, dass Folge-Komponenten wie die
Sentiment-Analyse auf eine feste Definition der Daten zurückgreifen
können. Letztlich soll so die Codequalität und Stabilität erhalten
werden, da ein nicht-typisiertes Format zu Fehlern führt. Vor der
Entwicklung der Komponenten, um Daten aktueller News Artikel zu
beziehen, wird daher das folgende Zielformat als Python DataClass
definiert und in einem zentralen *models/* Sub-Modul des Repos zur
Verfügung gestellt:
```python
@dataclass
class News:
id: str
title: str
date: str
text: str
source_url: str
def to_dict(self) -> dict:
return asdict(self)
Neben dem Inhalt des Artikels in Form des Titels, sowie dem Text werden
wertvolle Meta-Informationen wie das Veröffentlichungsdatum sowie der
Link des Artikels mitgeführt. So wird der späteren Analyse die
Möglichkeit gegeben, ihre Ergebnisse auch in den zeitlichen Kontext zu
legen und im Zuge der Visualisierung Nutzer auf den ursprünglichen
Artikel zu navigieren. Des Weiteren wird eine ID als eindeutiger
Identifier mitgeführt, um eindeutige Referenzen zu ermöglichen und
Duplikate zu verhindern. Die Werte des Feldes orientieren sich an den
IDs der jeweiligen Quelle - unter anderem Universally Unique Identifier (UUIDs). Die Anreicherung der Datensätze mit den
betroffenen Unternehmen sowie dem Sentiment erfolgt in der
nachgelagerten Analyse.
### RSS Feeds
Im Zuge der Vorbereitung des Projektes wurden RSS (Rich Site Summary) Feeds als mögliche Quelle für eine
regelmäßige Bereitstellung aktueller Nachrichten-Artikel und Events
identifiziert. Ein RSS Feed verfolgt das Ziel Informationen
regelmäßig über eine Web-Schnittstelle an Kunden zu liefern. Die Inhalte
bzw. deren Änderungen werden dabei in einem standardisierten,
maschinen-lesbaren Format zur Verfügung gestellt. [6]
Der genaue Aufbau eines RSS Feeds wird im von der Harvard Law School
entwickelten RSS 2.0 Standard beschrieben. Er beschreibt zunächst XML
als unterliegendes Dateiformat vor. Die Wurzel eines RSS Dokumentes wird
von einem sog. *Channel* gebildet, der in sich mehrere *Items* kapselt.
Neben dem Titel, Link des Feeds muss ein *Channel* eine Beschreibung
enthalten - weitere Meta-Informationen wie die Sprache oder
Informationen zum Urheberrecht sind möglich. Ein *Item* hingegen setzt
sich aus mindestens dem Titel, Link, Beschreibung, Autoren, Kategorie
\[\...\] zusammen. [7] Ein simpler RSS Feed könnte wie folgt
aussehen:
```xml
W3Schools Home Page
https://www.w3schools.com
Free web building tutorialsRSS Tutorial
https://www.w3schools.com/xml/xml_rss.asp
New RSS tutorial on W3SchoolsXML Tutorial
https://www.w3schools.com/xml
New XML tutorial on W3Schools
```
Bezogen auf die Anbindung an das Transparenzregister fällt auf, dass
diese Quelle zunächst nur Basis-Informationen des Artikels und noch
nicht den eigentlichen Inhalt, welcher für die Sentiment-Analyse sowie
Ermittlung betroffener Unternehmen besonders interessant sein könnte,
enthält.
Als erste Quelle wird das
[Handelsblatts](https://www.handelsblatt.com/rss-feeds/) gewählt. Es
stellt eine Reihe verschiedener Feeds zur Verfügung, die sich mit
unterschiedlichen Themen beschäftigen. Für eine Auswertung von
Unternehmens-Daten wird daher anfangs der RSS Feed
[Schlagzeilen](https://www.handelsblatt.com/contentexport/feed/schlagzeilen)
eingesetzt.
Eine Abfrage dieser Daten gestaltet sich aufgrund der standardisierten
XML Struktur über eine gezielte HTTP-Anfrage und nachträgliche
Auswertung des eigentlichen Artikels recht einfach:
```python
import requests
import xmltodict
from bs4 import BeautifulSoup
class HandelsblattRSS:
def __init__(self):
self.base_url = "https://www.handelsblatt.com/contentexport/feed"
def get_news_for_category(self, category: str = "unternehmen") -> dict:
url = f"{self.base_url}/{category}"
result = requests.get(url=url)
if result.status_code == 200:
return xmltodict.parse(result.text)["rss"]["channel"]["item"]
return None
def get_news_details_text(self, url: str) -> dict:
content = requests.get(url)
soup = BeautifulSoup(content.text, features="html.parser")
return " ".join(
[elem.text.replace("\n", " ") for elem in soup.find_all("p")][:]
)
Die eigentlichen Inhalte des Artikels weisen kein Standard Format mehr
auf, auch wenn diese HTML basiert sind, und müssen daher mit einer
Bibliothek wie BeautifulSoup verarbeitet werden. Im obigen Code werden
dabei lediglich alle Textinhalte ausgewertet und konkateniert.
Mit der obigen Python-Klasse können jedoch lediglich Daten extrahiert
werden, die Transformation sowie das Laden in die Staging DB erfordern
weitere Entwicklung.
```python
handelsblatt = HandelsblattRSS()
items = handelsblatt.get_news_for_category()
from datetime import datetime
from tqdm import tqdm
import pandas as pd
news = []
for news_article in tqdm(items):
info = {
"id": news_article["guid"],
"title": news_article["title"],
"date": datetime.strptime(
news_article["pubDate"], "%a, %d %b %Y %H:%M:%S %z"
).strftime("%Y-%m-%dT%H:%M:%S%z"),
"source_url": news_article["link"],
"text": handelsblatt.get_news_details_text(news_article["link"])
}
news.append(info)
```
### Tagesschau REST API
Als weitere Quelle für News Artikel wird die [Tagesschau
API](https://tagesschau.api.bund.dev/) eingesetzt. Diese wird von
bund.dev - einer Bemühung Verwaltungsdaten und Informationen über APIs
zur Verfügung zu stellen[8], bereitgestellt und ermöglicht
Anwendern einen maschinen-lesbaren Zugang zu Artikeln der Tagesschau.
Obwohl der Zugang zu diesen Daten ebenfalls über HTTP GET Anfragen
möglich ist, so entspricht das Datenformat keinem Standard, sondern dem
Produkt-eigenen Datenschema, welches vom Betreiber mit
[Swagger](https://swagger.io/) dokumentiert wurde, und erfordert daher
eine andere Aufbereitungs-Routine. Der Abruf der Daten bleibt jedoch
simpel und eine gezielte Suche nach Kategorien ist möglich.
```python
import json
import requests
from bs4 import BeautifulSoup
class TagesschauAPI:
def __init__(self):
self.base_url = "https://www.tagesschau.de/api2"
def get_news_for_sector(self, sector: str) -> dict:
url = f"{self.base_url}/news/"
regions = ",".join([str(i) for i in range(1, 16)])
result = requests.get(url=url, params={"regions": regions, "ressort": sector})
return result.json()
def custom_search(self, query: str) -> dict:
url = f"{self.base_url}/search/"
result = requests.get(url=url, params={"searchText": query})
return result.json()
def get_news_details_text(self, url: str) -> dict:
content = requests.get(url)
soup = BeautifulSoup(content.text, features="html.parser")
return " ".join(
[elem.text.replace("\n", " ") for elem in soup.find_all("p")][1:]
)
```
Auch hier werden erneut Details über den ursprünglichen Artikel bezogen.
Dafür wird der HTML-Inhalt der ursprünglichen Seite mit BeautifulSoup
geparsed und jeglicher Text des Dokumentes extrahiert.
## Orchestrierung & Operations
Sobald die obigen Lösungen das Entwicklungsstadium verlassen und
folglich eine Ausführungsumgebung benötigen, stellen sich die folgenden
Fragen:
- Wie kann die Ausführung der Anwendungen geplant und zeitbasiert
gestartet werden? (Scheduling)
- Wie lässt sich ein Monitoring auf die Ausführungsumgebung anwenden?
(Transparenz)
- Wie lässt sich die Ausführungsumgebung um weitere Anwendungen
erweitern? (Skalierbarkeit)
Zum einen wird ein Scheduling benötigt, um die Extraktion aktueller News
Artikel in festgelegten Intervallen (z.B. täglich) durchzuführen. Des
Weiteren muss es möglich sein, umfassende Informationen über diese
Ausführung zu erhalten. Da der Nutzer nicht vor dem Monitor sitzt, das
Skript manuell startet und seine Ausführung überwacht. Folglich ist es
schwierig im Nachhinein nachzuvollziehen, woran die Ausführung
gescheitert ist und wann dies geschah. Zuletzt muss die Lösung in der
Lage sein, auch weitere Anwendungen aufzunehmen, da auch Prozesse wie
die Extraktion der Stammdaten oder der Finanzdaten in Intervallen - wenn
auch größer, da Jahresabschlüsse immerhin nur jährlich anfallen -
ausgeführt werden können.
Eine einfache Lösung ist der Einsatz vom in Unix integrierten Cron
Dienst. Dieser ermöglicht den zeitbasierten Start von Skripten oder
Anwendungen. [9]
```sh
0 8 * * * /var/scripts/daily_cron.sh
```
Problematisch gestaltet sich beim Cron jedoch die Bereitstellung von
Fehlermeldungen sowie die Kapselung von Ausführungsumgebungen, welches
vor allem bei Python Anwendungen von hoher Bedeutung ist, damit es nicht
zu kollidierenden Abhängigkeiten kommt.
Eine in der Industrie verbreitete Lösung stellt Apache Airflow dar.
Wie in der obigen Grafik dargestellt, setzt sich eine Airlflow Instanz
aus verschiedenen Sub-Komponenten zusammen. Neben einem Web Server, der
eine grafische Bedienoberfläche sowie dazugehörige
API-Endpunkte
stellt, besteht der Kern aus dem Scheduler sowie Executor und der
dazugehörigen Datenbank. Anwender legen in Form eines
*Directed Acyclic Graphs (DAG)* einen
Anwendungsstrang an, der über den Scheduler aufgerufen und im Executor
sowie seinen *Workern* - also dedizierten Ausführungsumgebungen -
letztlich gestartet wird. Besonders in einer auf Kubernetes
bereitgestellten Instanz, der administrative Rechte über das Cluster
gegeben wurden, kann Airflow eigenständig für jede Ausführung einen
temporären Pod hochfahren, der somit die jeweiligen
DAGs komplett
isoliert ausführt und nach Ausführung wieder terminiert, um Ressourcen
zu sparen. Zusätzlich besteht die Möglichkeiten den Quellcode der
DAGs automatisch
über einen *Git-sync* Mechanismus mit einem Git Repo zu verbinden. So
kann neuer Code automatisch auf die Instanz übertragen werden, ohne dass
dieser manuell auf den Server kopiert werden muss. [10]
Zunächst wird im Rahmen des Transparenzregisters jedoch ein simples
Deployment auf Basis von Docker Compose bereitgestellt. Das
Deployment besteht aus den Kern-Komponenten sowie einem einzelnen
Worker. So gehen zwar die flexiblen Möglichkeiten des Hochfahrens
individueller Pods verloren, jedoch ist die Einrichtung umso einfacher.
Um die Ausführung der Extraktion der News Artikel zu automatisieren,
wird der Quellcode in das Format eines DAG überführt und an den Airflow Server
übertragen.
Aus Sicherheitsgründen wurden die Zugangsdaten zur StagingDB in die
Airflow Variables überführt, so dass die genauen Daten nicht im
Quellcode ersichtlich sind sowie zentral verwaltet werden können, um
schnell zwischen verschiedenen Ziel-Datenbanken wechseln zu können.
Diese werden in der grafischen Oberfläche durch den Admin Account unter
dem Menüpunkt *Admin/Variables* bzw. dem Link */variable* verwaltet.

Über den Einsatz des *\@task.virtualenv* Decorators wird sichergestellt,
dass jede Methode ihr eigenes Virtual Environment nutzt und so keine
Kollision von Python Abhängigkeiten auftreten können. Über das Argument
*schedule=\
\"@daily"* wird der DAG auf eine tägliche Ausführung terminiert.
Der Startzeitpunkt (*start_date*) muss vor dem aktuellen Datum liegen,
damit der Scheduler sofort agiert. Nach der Definition der einzelnen
Schritte, wird die Hauptlogik in Form eines geordneten Aufrufs der eben
definierten Methode und Übergabe der nötigen Parameter definiert.
## Rechtliche Rahmenbedingungen
Der Einsatz von Web Scraping / Crawling, um die Daten der vorgestellten
Quellen zu beziehen, lässt sich auf verschiedene Bereiche geltenden
Rechts verweisen, um zu bewerten auf welche weiteren Rahmenbedingungen
Rücksicht genommen werden muss.
Zum einen ist das Urheberrecht der Daten zu berücksichtigen. Da es sich
bei dem Projekt um nicht-kommerzielle Forschung handelt, ist ein
Scraping grundsätzlich gemäß §60d des Urheberrechtsgestzes (UrhG) erlaubt. Jedoch ist darauf zu achten,
dass Mechanismen wie die *robots.txt* sowie weitere technische Hürden -
etwa IP-Sperren - nicht umgangen werden. Außerdem darf dem Betreiber der
Website durch die erzeugte Last keine Schäden beigefügt
werden. [11]
Des Weiteren ist besonderes Augenmerk auf den Datenschutz zu legen. Da
im Transparenzregister eine Reihe von personenbezogenen Daten wie Namen,
Geburtsdaten und Wohnort sowie Firmenzugehörigkeiten verarbeitet werden,
muss sichergestellt sein, dass Richtlinien der
Datenschutzgrundverordnung (DSGVO) eingehalten
werden. Auch wenn es sich bei den verarbeiteten Daten um frei
zugängliches Material handelt, muss dennoch ein Rahmenwerk aufgebaut
werden, welches die Ziele des Projektes klar absteckt, um seinen
Forschungscharakter zu wahren, und ggf. entsprechende Maßnahmen wie etwa
ein zeitlich begrenzte Speicherdauer und Zugangsbeschränkung
implementiert werden. [11]
## Zusammenfassung
Im Rahmen der Hausarbeit wurden zunächst die theoretischen Grundlagen
bestehend aus dem Web Scraping und Crawling sowie dem
ETL-Prozess
vermittelt. Darauf folgte die Vertiefung dieser Themen anhand der
praktischen Umsetzung in der Data Extraktion für das Transparenzregister
aus unterschiedlichen Quellen. Zu diesen zählten unterschiedliche
Provider für News Artikel wie etwa der RSS Feed des Handelsblattes sowie die
Tagesschau REST API der *bund.dev* Gruppe. Darauf folgend wurden
Unternehmens Stammdaten aus dem Unternehmensregister und Bewegungsdaten
aus dem Bundesanzeiger bezogen. Zuletzt folgte die Ermittlung einer
geeigneten Ausführungsplattform in Form von Apache Airflow sowie das
Deployment der ersten DAGs auf dieser. Rechtliche
Rahmenbedingungen, die die weitere Entwicklung beeinflussen werden,
wurden am Schluss dargestellt.
### Fazit
Beim Bezug von Daten aus staatlichen Quellen zeigte sich schnell, dass
noch keine vollends in APIs überführte Lösungen vorliegen. So gestaltete
sich die Exktaktion von Daten hier im Vergleich zu den News Artikeln
deutlich komplizierter und vor allem fehleranfälliger. Sollte sich die
Struktur der Website nur im geringsten ändern - zum Beispiel indem
Buttons umbenannt oder komplett ersetzt werden - so wird die Anwendung
nicht mehr korrekt agieren können. Da es sich ohnehin um öffentliche
Daten für den allgemeinen Zugriff handelt, wäre hier eine anständige
Lösung auf Seiten der Betreiber wünschenswert.
Der Einsatz von Apache Airflow stellte sich in dem gegebenen Aufbau als
fehleranfällig heraus. Dadurch, dass die Instanz nur über einen einzigen
Worker verfügt, der sich jedoch von verschiedenen Anwendungen geteilt
wird, die dennoch unterschiedliche Abhängigkeiten einsetzen müssen, war
das Aufsetzen der DAGs deutlich komplizierter und auch der
Einsatz von Bibliotheken, um Duplikate zu vermeiden, konnte dem Worker
nur schwer beigebracht werden. Hier wurde deutlich, dass - trotz
komplizierteren initialen Setups - ein auf Kubernetes und dem
*KubernetesPodOperator* basiertes Deployment hier eleganter wäre.
Dennoch ist das Monitoring von Airflow im Vergleich zu anderen Lösungen
zu loben, so dass ein weiterer Einsatz geplant ist.
### Ausblick
Nun gilt es die entwickelten Puzzle-Stücke zusammenzusetzen und die
StagingDB mit immer mehr Daten zu versorgen. Da News Artikel nun
regelmäßig eingespeist werden und solange sich an den APIs der Betreiber
nichts ändert auch keine Code Änderungen erforderlich sind, wird der
Fokus klar auf dem Beziehen weiteren Stamm- und Bewegungsdaten der
Unternehmen liegen, um so die weiteren Prozesse wie die
Verflechtugnsanalyse und Datenvisualisierung mit Informationen zu
versorgen. Sollten sich im Laufe der Entwicklung weitere News-Quellen
als wertvoll erweisen, kann auf der bestehenden Code-Basis schnell eine
weitere Integration geschaffen werden.
Des Weiteren mag der im vorherigen Absatz angesprochene Vorschlag,
Airflow auf Kubernetes aufzusetzen, eine sinnvolle Ergänzung sein. In
Zukunft werden auch Finanzdaten in regelmäßigen Abständen vom
Bundesanzeiger bezogen werden müssen, so dass die Investition in eine
besser skalierbare Lösung wertvoll wäre.
Ein weiterer indirekter Vorteil von Airflow ist, dass Entwickler dazu
gezwungen sind, ihren Code möglichst modular zu halten, um so die
Ausführungsumgebung zu abstrahieren. Der Einsatz von Airflow und das
Verpacken des Codes in einen DAG sollte lediglich bestehende
Klassen/Methoden verwenden und in die richtige Reihenfolge bringen bzw.
verknüpfen. Der Aufbau einer klaren Interface-Struktur und guten
Teststrategie ist daher sehr wichtig. Der damit verbundene Aufwand muss
entsprechend eingeplant werden.
## Anhang
### Company Datenmodell

### Ablauf Web Scraping im Unternehmensregister

### Airflow DAG
```python
import datetime
from airflow.decorators import dag, task
@dag("handelsblatt_news_dag", start_date=datetime.datetime(2000, 1, 1), schedule="@daily", catchup=False)
def main():
@task.virtualenv(system_site_packages=False, requirements=['requests', 'bs4', 'xmltodict'])
def fetch_data():
from datetime import datetime
from transparenzregister.utils.handelsblatt import HandelsblattRSS
handelsblatt = HandelsblattRSS()
items = handelsblatt.get_news_for_category()
data = []
for article in items:
info = {
"id": article["guid"],
"title": article["title"],
"date": datetime.strptime(
article["pubDate"], "%a, %d %b %Y %H:%M:%S %z"
).strftime("%Y-%m-%dT%H:%M:%S%z"),
"source_url": article["link"],
"text": handelsblatt.get_news_details_text(article["link"]),
}
data.append(info)
return data
@task.virtualenv(requirements=["pymongo"], task_id="Process_data")
def process_data(list_news: list) -> list:
from airflow.models import Variable
from transparenzregister.utils.mongo import MongoConnector, MongoNewsService
from transparenzregister.models.news import News
connector = MongoConnector(
hostname=Variable.get("mongodb_host"),
database="transparenzregister",
username=Variable.get("mongodb_username"),
password=Variable.get("mongodb_password"),
port=None
)
service = MongoNewsService(connector)
num_inserted = 0
for info in list_news:
article = News(**info)
if service.get_by_id(article.id) is None:
service.insert(article)
num_inserted += 1
return num_inserted
raw_data = fetch_data()
inserted_entries = process_data(raw_data)
print(f"Number of inserted entries: {inserted_entries}")
return inserted_entries
news_dag = main()
### Literaturverzeichnis
- **[1]** Samanpour, A. R. (2016). *Studienunterlagen: Business Intelligence 1* (1. Auflage). Wissenschaftliche Genossenschaft Südwestfalen eG. Iserlohn.
- **[2]** IONOS. (2020). *Was ist Web Scraping?*. [Link](https://www.ionos.de/digitalguide/websites/web-entwicklung/was-ist-web-scraping/) (Zugriff am: 16. September 2023).
- **[3]** IONOS. (2016). *Indexierungsmanagement mit der robots.txt*. [Link](https://www.ionos.de/digitalguide/hosting/hosting-technik/indexierungsmanagement-mit-der-robotstxt/) (Zugriff am: 16. September 2023).
- **[4]** Bundesanzeiger Verlag GmbH. *Unternehmensregister*. [Link](https://www.unternehmensregister.de/) (Zugriff am: 24. September 2023).
- **[5]** Google. *Was ist CAPTCHA?*. [Link](https://support.google.com/a/answer/1217728?hl=en) (Zugriff am: 3. Oktober 2023).
- **[6]** Markgraf, D. P. (2018). *RSS-Feed*. Springer Fachmedien Wiesbaden GmbH. [Link](https://wirtschaftslexikon.gabler.de/definition/rss-feed-53722/version-276790) (Zugriff am: 16. September 2023).
- **[7]** Harvard Law. (2014). *RSS 2.0*. [Link](https://cyber.harvard.edu/rss/) (Zugriff am: 16. September 2023).
- **[8]** bundDEV. *Wir dokumentieren Deutschland*. [Link](https://bund.dev/) (Zugriff am: 16. September 2023).
- **[9]** ubuntu Deutschland e.V. (2023). *Cron*. [Link](https://wiki.ubuntuusers.de/Cron/) (Zugriff am: 1. Oktober 2023).
- **[10]** The Apache Software Foundation. *Apache Airflow*. [Link](https://airflow.apache.org/docs/apache-airflow/2.7.1/index.html) (Zugriff am: 1. Oktober 2023).
- **[11]** Universität Hamburg - Fakultät für Wirtschafts- und Sozialwissenschaften. (2023). *Handreichung zur rechtskonformen Durchführung von Web-Scraping Projekten in der nicht-kommerziellen wissenschaftlichen Forschung*. [PDF Link](https://www.wiso.uni-hamburg.de/forschung/forschungslabor/downloads/20200130-handreichung-web-scraping.pdf) (Zugriff am: 2. Oktober 2023).