Python application to crawl news websites, using the scrapy framework .
The crawler is based on 'spiders' which crawl a specific news website and extract information.
The sz, taz and heise spiders:
- crawl main page for category pages
- crawls all category pages for article links
The postillon spider:
- crawls the archive for article links
- in order to dynamically load the article-previews for the desired years/months selenium is used
- once the article-previews are present the page_source is handed over to scrapy
Every spider:
- matches all crawled links with database to avoid crawling duplicate articles
- crawls article links for article and meta data (as defined in
items.py
) - saves data in database in collection
scraped_articles
(viapipelines.py
) - uses the common methods in
utils.py
.
crawl_time # datetime.now()
short_url # String 'https://taz.de/!5642421/'
long_url # String 'https://taz.de/Machtkampf-in-Bolivien/!5642421/'
news_site # String: taz, sz, heise, postillon, golem
title # String
authors # List(String)
description # String: Teaser for article, sometimes same as lead/intro
intro # String: Lead/intro, sometimes same as description/teaser
text # String
keywords # List(String) - should not contain newssite
published_time # datetime or None
image_links # List(String)
links # List(String)
For execution you need to save the database authentification data in your ~/.profile
:
export MONGO_USER="s0..."
export MONGO_HOST="host:port"
export MONGO_DATABASE="s0..."
export MONGO_PWD="password"
or save directly in settings.py
.
Warning: If you save directly in settings.py
, don't commit changes without removing your password!
You also need access to the database (directly or via VPN)
More settings are available in settings.py
, for example setting the mongoDB collection names.
-
Install Selenium
pip3 install selenium
- In VM Environment install globally:
sudo -H pip3 install selenium
- In VM Environment install globally:
-
Install Chromium driver
- Ubuntu:
sudo apt-get install chromium-chromedriver
- Alternatively install firefox-geckodriver (if option to use Firefox ist set)
sudo apt install firefox-geckodriver
- Alternatively install firefox-geckodriver (if option to use Firefox ist set)
- Debian:
sudo apt-get install chromedriver
- Ubuntu:
-
Install Chromium or Chrome (or Firefox if option to use Firefox instead is set)
- Ubuntu:
sudo apt-get update sudo apt-get install chromium-browser
- This is a transactional package, which installs the chromium snap:
# 1. Enable snapd sudo apt update && sudo apt install snapd && sudo snap install core # 2. Install chromium snap sudo snap install chromium
- This is a transactional package, which installs the chromium snap:
- Debian:
sudo apt-get install chromium chromium-l10n
- Ubuntu:
- Fix missing locale error by installing de_DE locale
sudo locale-gen de_DE sudo update-locale
- Verify locale is present:
locale -a | grep de_DE
- Verify locale is present:
- Install mongodb
- Run mongo daemon:
mongod
- Run with custom db path:
mongod --dbpath=./data-mongodb
- Run with custom db path:
- Open mongo shell:
mongo
- Create database:
use scraped_articles
- Create user:
db.createUser({user:"username",pwd:"password",roles:["readWrite", "dbAdmin"]})
- Update environment variables in
~/.profile
:export MONGO_USER="username" export MONGO_HOST="localhost:27017" export MONGO_DATABASE="scraped_articles" export MONGO_PWD="password"
For a test run, you can limit each spider separately:
testrun_cats = 3
- limits the categories to crawl to this number. if zero, no limit.
testrun_arts = 2
- limits the article links to crawl to this number. if zero, no limit.
Execute in terminal (in folder crawler):
scrapy crawl SPIDERNAME
(sueddeutsche, taz, heise)
For example: scrapy crawl taz
Don't forget to set the testrun variables to zero before pushing to git.
git pull
the latest version into your server crawler directory.
If necessary, change in settings.py
the database authentification data (as described in 'Setup') and mongoDB collection names.
Use scrapyd-deploy
to deploy.
Read more:
- https://scrapyd.readthedocs.io/en/stable/
- https://github.com/scrapy/scrapyd-client#deploying-a-project
The endpoint is set in scrape.cfg
.
To run a spider, you need to execute:
curl http://localhost:6800/schedule.json -d project=inews_crawler -d spider=SPIDERNAME
The script scrape.sh
runs all three spiders.
For running the spiders regularily (like once a day) you can use a cron job.
crontab -l -u local
shows all cron jobs for user local
Editing is possible with crontab -e
.
00 01 * * * /bin/sh /home/local/crawler/scrape.sh
will execute the spider script once a day at 1 a.m.
It is possible to crawl sueddeutsche articles way back in the past.
limit_pages = 1
sets the amount of additional category pages of 50 articles each, maximum of 400 pages.
For building the archive, you can use 400
, but for daily use 0
or 1
should be enough.
If you want to build the archive, please study the options in settings.py
to slow down the spider.
Don't forget to set the testrun variables to zero.
There is no public archive accessible by pagination. Don't forget to set the testrun variables to zero.
It is possible to crawl heise articles way back in the past.
limit_pages = 1
sets the amount of additional category pages of 15 articles each.
For building the archive, you can use 0
to get the maximum amount of articles, but for daily use 3
or 4
should be enough.
If you want to build the archive, please study the options in settings.py
to slow down the spider.
Don't forget to set the testrun variables to zero.
It is possible to crawl postillon articles way back in the past.
year_to_crawl
sets the year to be crawled. In addition limit_min_month_of_year_to_crawl
can be used to define the first month of the year to be crawled.
For building the archive, you can set both to False
to get the maximum amount of articles, but for daily use year_to_crawl=2020
and limit_min_month_of_year_to_crawl=10
(assuming it is October 2020) should be enough.
If you want to build the archive, please study the options in settings.py
to slow down the spider.
Don't forget to set the testrun variables to zero or False.
It is possible to crawl golem articles way back in the past.
number_of_months
sets the months to be crawled. Set it to 1 to crawl only articles from current month.
max_articles_per_month
limits the amount of articles to be crawled from (past) months. Set it to -1 for no limit.
There are different levels of logging:
- DEBUG: scrapy events
- INFO: duplicate messages, database messages ("Post added to MongoDB", "already in db"), paywall message ("is paywalled")
- WARNING: property was not found ("Cannot parse title",...)
- ERROR: a function could not be executed
- CRITICAL: a serious error, indicating that the program itself may be unable to continue running
All logs will be saved here: /logs/inews_crawler
INFO and WARNING events will be also saved (via utils.py
) as log items in the database in the collection
log_crawler
.
log_time # datetime.now()
url # String 'https://taz.de/!5642421/'
news_site # String: taz, sz, heise, postillon, golem
title # String
property # String: text, title, keywords, ...
level # String: warning, info
This collection is connected to ElasticSearch and Kibana by executing connector.py
.
For running connector.py
, it is necessary to have a file in the same directory containing
the authentification data for mongoDB and ElasticSearch named connector_secrets.py
:
#!/usr/bin/env python3
MONGO_USER = USER
MONGO_HOST = HOSTNAME
MONGO_DATABASE = DATABASE
MONGO_PWD = PASSWORD
MONGO_AUTH_MECHANISM = AUTH_MECHANISM
MONGO_URI = 'mongodb://' + MONGO_USER + ':' + MONGO_PWD + '@' + MONGO_HOST \
+ '/?authSource=' + MONGO_DATABASE + '&authMechanism=' + MONGO_AUTH_MECHANISM
MONGO_COLLECTION = 'log_crawler'
ELASTICSEARCH_HOST = 'localhost'
ELASTICSEARCH_INDEX = 'mongo_log_crawler'
It makes sense to execute connector.py
as cron job every time the spider cron job is finished.
If the spiders are running at 1 a.m.,
00 02 * * * /usr/bin/python3 /home/local/mongodb_connectors/scrape_log/connector.py
will execute the connector at 2 a.m.
Kibana is a tool which helps to visualize the logging data.
There will be a certain amount of log entries with level 'warning', but if the warnings increase strongly
the news site code might be changed and the spider code needs to be adapted.
If the amount of scraped articles is changing considerably, it might also be a pointing to altered code of the
news site. In this case, check also the logs at /logs/inews_crawler
for logs with level ERROR or CRITICAL.
Log in via ssh and port forwarding:
ssh -L 5601:localhost:5601 local@news.f4.htw-berlin.de
If Kibana does not run, execute
systemctl start kibana
Kibana will be accessible at http://localhost:5601
You can find the dashboard with 4 predefined visualizations here.
If you want to crawl more often than once a day, you can change each
visualization
to other intervals by clicking Buckets X-axis
and changing Minimum interval
from daily to hourly and clicking Save
.
Happy crawling!
Python Anwendung zum Crawlen von Newsseiten, unter Verwendung des Framworks scrapy.
Der Crawler basiert auf 'Spider', die eine spezifische Newsseite crawlen und Informationen extrahieren.
Die sz, taz und heise Spider:
- crawlt die Hauptseite nach Kategorienseiten
- crawlt alle Kategorienseiten nach Links zu Artikeln
Die Postillon Spider:
- crawlt das Archiv nach Links zu Artikeln
- verwendet Selenium um die Artikel-Previews dynamisch für die gewünschten Jahre/Monate zu laden
- sorgt für eine Übergabe des Quellcodes der Seite von Selenium an Scrapy, sobald die Artikel-Previews präsent sind
Jede Spider:
- gleicht alle gefundenen Links mit der Datenbank ab, um mehrfaches crawlen zu vermeiden.
- crawlt die Artikellinks und extrahiert den Artikel und Metadaten (wie in
items.py
definiert) - speichert die Daten in der Datenbank-Collection
scraped_articles
(viapipelines.py
). - unter Verwendung gemeinsamer Methoden in
utils.py
.
crawl_time # datetime.now()
short_url # String 'https://taz.de/!5642421/'
long_url # String 'https://taz.de/Machtkampf-in-Bolivien/!5642421/'
news_site # String: taz, sz, heise, postillon, golem
title # String
authors # List(String)
description # String: Teaser für Artikel, manchmal derselbe Text wie Lead/Intro
intro # String: Lead/Intro, manchmal derselbe Text wie Description/Teaser
text # String
keywords # List(String) - sollte nicht die Newsseite selber beinhalten
published_time # datetime oder None
image_links # List(String)
links # List(String)
Um den Crawler auszuführen, müssen die Datenbank-Authentifizierungsdaten in ~/.profile
gespeichert werden:
export MONGO_USER="s0..."
export MONGO_HOST="host:port
export MONGO_DATABASE="s0..."
export MONGO_PWD="password"
oder direkt in settings.py
gespeichert werden.
Warnung: Wenn direkt in settings.py
gespeichert wird, keine Commits machen ohne das Passwort zu entfernen!
Es wird außerdem Zugang zur Datenbank benötigt (direkt oder via VPN)
Mehr Einstellungen sind möglich in settings.py
, zum Beispiel die Namen der mongoDB-Collection.
-
Selenium installieren
pip3 install selenium
- In VM Umgebung muss das Paket global installiert werden:
sudo -H pip3 install selenium
- In VM Umgebung muss das Paket global installiert werden:
-
Chromium Treiber installieren
- Ubuntu:
sudo apt-get install chromium-chromedriver
- Alternativ firefox-geckodriver installieren (falls Option Firefox zu verwenden gesetzt ist)
sudo apt install firefox-geckodriver
- Alternativ firefox-geckodriver installieren (falls Option Firefox zu verwenden gesetzt ist)
- Debian:
sudo apt-get install chromedriver
- Ubuntu:
-
Chromium oder Chrome installieren (oder statdessen Firefox, falls die Option Firefox zu verwenden gesetzt ist)
- Ubuntu:
sudo apt-get update sudo apt-get install chromium-browser
- Es handelt sich um ein transaktionales Paket, womit das chromium snap installiert wird:
# 1. Enable snapd sudo apt update && sudo apt install snapd && sudo snap install core # 2. Install chromium snap sudo snap install chromium
- Es handelt sich um ein transaktionales Paket, womit das chromium snap installiert wird:
- Debian:
sudo apt-get install chromium chromium-l10n
- Ubuntu:
- Installiere mongodb
- Führ mongo daemon aus:
mongod
- Mit custom db-Pfad ausführen:
mongod --dbpath=./data-mongodb
- Mit custom db-Pfad ausführen:
- Öffne mongo shell:
mongo
- Erstelle Datenbank:
use scraped_articles
- Erstelle Benutzer:
db.createUser({user:"username",pwd:"password",roles:["readWrite", "dbAdmin"]})
- Aktualisiere Umgebungsvariablen in
~/.profile
:export MONGO_USER="username" export MONGO_HOST="localhost:27017" export MONGO_DATABASE="scraped_articles" export MONGO_PWD="password"
Für einen Testlauf kann jeder Spider einzeln begrenzt werden:
testrun_cats = 3
- begrenzt die Kategorien, die gecrawlt werden, auf diese Zahl. Bei 0 gibt es keine Begrenzung.
testrun_arts = 2
- begrenzt die Anzahl der Artikellinks, die gecrawlt werden, auf diese Zahl. Bei 0 gibt es keine Begrenzung.
Zum lokalen Ausführen kann im Terminal (im Order crawler
) folgender Befehl benutzt werden:
scrapy crawl SPIDERNAME
(sueddeutsche, taz, heise)
Zum Beispiel: scrapy crawl taz
Bitte nicht vergessen, die Testlauf-Variablen auf Null zu setzen, bevor in Git gepusht wird.
Mit git pull
die letzte Version in das crawler-Verzeichnis auf dem Server laden.
Falls notwendig, können in settings.py
die Datenbank-Authentifizierungs-Daten (wie bei 'Setup' beschrieben) und die
Namen der mongoDB-Collection geändert werden.
Mit scrapyd-deploy
wird deployed.
Siehe auch:
- https://scrapyd.readthedocs.io/en/stable/
- https://github.com/scrapy/scrapyd-client#deploying-a-project
Der Endpunkt ist in scrape.cfg
konfiguriert.
Zum Starten eines Spiders wird dieser Befehl ausgeführt:
curl http://localhost:6800/schedule.json -d project=inews_crawler -d spider=SPIDERNAME
Das Script scrape.sh
führt alle drei Spider aus.
Um die Spider regelmäßig laufen zu lassen (z.B. einmal pro Tag), kann ein Cron-Job genutzt werden.
crontab -l -u local
zeigt alle Cron-Jobs für User local
Die Cron-Jobs können mit crontab -e
editiert werden.
00 01 * * * /bin/sh /home/local/crawler/scrape.sh
führt das Spider-Script einmal am Tag um 1 Uhr aus.
Es ist möglich, Artikel aus der Süddeutschen Zeitung zu crawlen, die weit in der Vergangenheit publiziert wurden.
limit_pages = 1
bestimmt die Anzahl der zusätzlichen Kategorienseiten von jeweils 50 Artikeln,
mit einem Maximum von 400 Seiten.
Um einen Grundstock an Artikeln zu bilden, kann mit 400
gecrawlt werden, im täglichen Gebrauch sollte 0
or 1
genug sein.
Wenn es um den Grundstock geht, können die Optionen in settings.py
genutzt werden, um den Spider zu verlangsamen.
Dabei nicht vergessen, die Testlauf-Variablen vorher auf Null zu setzen.
Bei der taz gibt es kein öffentliches Archiv. Die Testlauf-Variablen sollten im Normalbetrieb auch auf Null gesetzt sein.
Bei Heise ist es auch möglich, auf sehr alte Artikel zuzugreifen.
limit_pages = 1
bestimmt die Anzahl der zusätzlichen Kategorienseiten von jeweils 15 Artikeln.
Um einen Grundstock an Artikeln zu bilden, kann mit 0
das Maximum an Artikeln gecrawlt werden.
Im täglichen Gebrauch sollte 3
or 4
genug sein.
Wenn es um den Grundstock geht, können die Optionen in settings.py
genutzt werden, um den Spider zu verlangsamen.
Dabei nicht vergessen, die Testlauf-Variablen vorher auf Null zu setzen.
Bei postillon ist es auch möglich, auf sehr alte Artikel zuzugreifen.
year_to_crawl
definiert das zu crawlende Jahr. Zusätzlich kann limit_min_month_of_year_to_crawl
verwendet werden um den ersten zu crawlenden Monat des definierten Jahres zu definieren.
Um einen Grundstock an Artikeln zu bilden, können beide Limitierungen auf False
gesetzt werden. Im täglichen Gebrauch sollte year_to_crawl=2020
und limit_min_month_of_year_to_crawl=10
(angenommen es sei October 2020) ausreichend sein.
Wenn es um den Grundstock geht, können die Optionen in settings.py
genutzt werden, um den Spider zu verlangsamen.
Dabei nicht vergessen, die Testlauf-Variablen vorher auf Null oder False zu setzen.
Es ist möglich, Golem-Artikel der Vergangenheit zu crawlen.
number_of_months
legt fest, wie viele Monate zurückgecrawled werden soll. Um nur aktuelle Artikel zu crawlen, setzt man den Parameter auf 1.
max_articles_per_month
legt fest, wie viele Artikel pro Monat gecrawled werden sollen. Möchte man keine Limitierung, dann setzt man den Parameter auf -1.
Es gibt verschiedene Level des Loggens:
- DEBUG: Scrapy-Events
- INFO: Nachrichten über Duplikate, die Datenbank betreffend ("Post added to MongoDB", "already in db"), die Paywall ("is paywalled")
- WARNING: Eine Property/ein Attribut wurde nicht gefunden ("Cannot parse title",...).
- ERROR: Eine Funktion konnte nicht ausgeführt werden.
- CRITICAL: Ein schwerer Fehler, eventuell kann das Programm nicht weiter ausgeführt werden.
Alle Logs werden auf dem Server hier gespeichert: /logs/inews_crawler
INFO- und WARNING-Events werden auch als Log-Items (via utils.py
) in der Datenbank-Collection log_crawler
gespeichert.
log_time # datetime.now()
url # String 'https://taz.de/!5642421/'
news_site # String: taz, sz, heise, postillon, golem
title # String
property # String: text, title, keywords, ...
level # String: warning, info
Diese Collection ist mit ElasticSearch und Kibana durch das Ausführen von connector.py
verbunden.
Um connector.py
ausführen zu können, ist es notwendig, dass im selben Verzeichnis eine Datei mit den
Authentifizierungsdaten von mongoDB und ElasticSearch liegt, die connector_secrets.py
benannt ist:
#!/usr/bin/env python3
MONGO_USER = USER
MONGO_HOST = HOSTNAME
MONGO_DATABASE = DATABASE
MONGO_PWD = PASSWORD
MONGO_AUTH_MECHANISM = AUTH_MECHANISM
MONGO_URI = 'mongodb://' + MONGO_USER + ':' + MONGO_PWD + '@' + MONGO_HOST \
+ '/?authSource=' + MONGO_DATABASE + '&authMechanism=' + MONGO_AUTH_MECHANISM
MONGO_COLLECTION = 'log_crawler'
ELASTICSEARCH_HOST = 'localhost'
ELASTICSEARCH_INDEX = 'mongo_log_crawler'
Es macht Sinn, connector.py
als Cron-Job jedes Mal auszuführen, wenn der Cron-Job der Spider beendet ist.
Wenn die Spider um 1 Uhr laufen, führt
00 02 * * * /usr/bin/python3 /home/local/mongodb_connectors/scrape_log/connector.py
den Connector um 2 Uhr aus.
Kibana ist ein Tool, das hier genutzt wird, um die Log-Daten zu visualisieren.
Es wird immer eine bestimmte Anzahl von Logeinträgen mit dem Level 'warning' geben, aber wenn die Anzahl von Warnungen
stark anwächst, kann es sein, dass die Newsseite ihren HTML-Code geändert hat und der Code des Spiders ebenfalls
angepasst werden muss.
Wenn die Anzahl der gescrapten Artikel sich bedeutend ändert, deutet das auch auf eine Veränderung des Newsseiten-Codes
hin. In diesem Fall sollten auch die Logs unter /logs/inews_crawler
nach Logeinträgen mit dem Level ERROR oder
CRITICAL durchsucht werden.
Login via SSH und Port Forwarding:
ssh -L 5601:localhost:5601 local@news.f4.htw-berlin.de
Wenn Kibana noch nicht läuft, startet es mit systemctl start kibana
.
Kibana findet sich dann unter http://localhost:5601
Das Dashboard mit 4 vordefinierten Visualisierungen befindet sich hier.
Wenn mehr als einmal pro Tag gecrawlt werden soll, können die einzelnen
Visualisierungen
auf ein anderes Intervall geändert werden durch Anklicken von Buckets X-axis
und verändern des Minimum interval
von
'daily' auf 'hourly', danach muss gespeichert werden.
Happy crawling!