Experimente mit dem BonnerBlogs.de RSS-Datensatz

Ende 2016 bin ich auf Twitter über Sascha Foersters1 Anfrage gestoßen, ob jemand Zeit und Lust hätte, sich mit mit einem RSS-Datensatz der Bonner Blogosphäre2 zu beschäftigen. Die Rohdaten lagen in Form eines SQL-Dumps des hauseigenen RSS-Crawlers vor, wobei die folgenden Zahlen einem Snapshot von April 2017 entsprechen. Vorab ist zu erwähnen, dass ich schwerpunktmäßig kein Data Scientist, sondern Software/DevOps Engineer bin. Das Thema hat mich aber seit dem Studium und in den ersten Jobs (im Bereich Spracherkennung) stets begleitet und interessiert. Da das nun vor allem mein kleines Hobby und «Selbststudium: Data Science» ist, haben die ersten Schritte naturgemäß eine ganze Weile gebraucht und sind wohl am ehesten so zusammenzufassen:

«One small step for data science, one giant leap for Basti Tee.»

Anregungen fand ich vor allen in der Arbeit von David Kriesel, dessen Reverse Engineering von Spiegel Online3 ich sehr empfehlen kann und über dessen Name Dropping4 und eigenen, zumindest privaten Programmierpräferenzen ich schnell in den Tiefen des PyData Stacks5 versunken bin.

Toolchain

Initial hatte ich einen curl-basierten Downloader auf die Rohdaten angesetzt, der die HTML-Rohdaten je Artikel extrahiert und alles mit den vorhandenen Metadaten in eine dokumentenbasierte MongoDB6 stopft. Der BonnerBlogs-Crawler sammelt im Wesentlichen die folgenden Daten:

  • URI und Titel des Blogs
  • URI des Blogartikels
  • URI des zugehörigen Eintrags bei BonnerBlogs.de
  • Manuell vergebene Blogkategorie

Die Toolchain hatte ich dann komplett nach Python3 portiert und mit verschiedenen Vorverarbeitungen erweitert, konkret:

  • Verfügbarkeitsprüfung des Artikel-URI (Python Requests7, u.a.)
  • Download der HTML-Rohdaten (Python Requests, u.a.)
  • Extraktion des Artikel-Volltextes (BeautifulSoup8, u.a.)
  • Erkennung der Artikelsprache (langdetect9)
  • Extraktion des Veröffentlichungszeitpunkt
  • Schlagwortextraktion (TextRank10)
  • Tokenization für späteres Modelltraining (Stopword Removal, Stemming, Tokenization, vor allem mit NLTK11)
  • Skripte zur Ermittlung von Basisstatistiken (Mean, Std.-Abweichung etc.)

Der Code für die Basis-Toolchain ist nun bis auf einige Glue-Skripte komplett in Python3 und mithilfe der genannten Bibliotheken geschrieben und auf meinem GitHub12 zu finden ― free as in «freedom». Ein paar Workflow-Skripte, die wiederum die genannte Toolchain auf die BonnerBlogs-Daten anwenden, sind genauso Open-Source bei GitHub13 zu finden. Voraussetzung ist allerdings, dass ihr euch die Rohdaten bei Sascha Foerster besorgt.

Die ersten Visualisierungen hatte ich ebenfalls noch mit Python erzeugt. Aber das macht auf die Dauer und auch in Hinblick auf Akualisierungen, Interaktivität und die Veröffentlichung im Netz nicht glücklich. Daher hatte ich angefangen, mit d3.js14 zu experimentieren. Klingt nach Drogenkonsum – ist auch ein wenig so. Ein wahnsinnig interessantes Framework und in den richtigen Händen ein geradezu künstlerisches Werkzeug für Datenfreunde15.

Überblick

Seit Ende 2013 wird bei BonnerBlogs.de mitgeschrieben. Der Korpus enthält über 1000 Domains und 80.000 Blogeinträge, wobei rund 13% heute noch verfügbar sind. Also reden wir von circa 90 Artikeln pro Tag (Stand März 2017), die von den Betreibern in 34 Kategorien sortiert werden. Die Quote der nicht mehr verfügbaren Artikel wäre normal vielleicht eher eine Randnotiz, allerdings heißt das für diese Rohdaten, dass hier nix mehr zu holen ist. Der Crawler holt bisher nur Metadaten – die HTML-Rohdaten gehören leider nicht dazu. Ein paar Anregungen für das Crawling in der Zukunft später im Fazit.

Schauen wir mal auf die verwendeten Kommunikationsprotokolle:

In Zeiten von äußerst komfortablen Zertifikatstools wie Letsencrypt16 muss ich diese Statistik natürlich anführen. Nur knapp 20% der mir vorliegenden Blogs leiten um bzw. liefern ihren Content über eine sichere HTTPS-Verbindung aus. Pro-Argumente gibt es wirklich nicht mehr, Google bestraft solche Blogs im Suchmaschinenranking17 und in der Regel hängt hinter einem Blog ein CMS, dass auf der selben Domain administriert wird. Also mein Appell: Schaut euch LetsEncrypt an und seid kostenfrei und mit wenig Aufwand auf der sicheren Seite. Und wenn es euch auch überzeugt, dann erwägt auch eine Spende an das Projekt18. Solche Zertifikate waren vor einigen Jahren noch wahnsinnig kostspielig und kompliziert zu administrieren. Ende des Werbeblocks.

Der nächste grobe Überblick über den Korpus liefern die Artikelsprachen:

Nicht überraschend schreiben die meisten Blogger aus Bonn ihre Artikel auf Deutsch. Zweiplatziert ist ebensowenig überraschend Englisch. Mein persönlicher Favorit ist der Musikblog «3songsbonn»19. Ich hatte erst überlegt, die Statistik gar nicht erst aufzuführen, aber am Ende stolpert man beim Durchforsten des halben Prozents dann doch über viele Perlen. Oder wusstet ihr, dass es in Bonn eine polnische Modebloggerin und ihren «Plus Size Blog» gibt?20 Genaugenommen finden sich auch ein paar mehrsprachige Blogs im Korpus, allerdings habe ich hier noch keine Lösung, das beim Scrapen sauber aufzutrennen. Ein Beispiel ist der Kunstblog «Artplace.de»21, der zum Teil in italienischer Sprache verfasst ist.

Kategorien

In der Darstellung sieht man die von den Betreibern von BonnerBlogs.de manuell vergebenen Kategorien pro Blog als Packed Bubbles Darstellung. Umso größer die Blase, desto mehr Artikel existieren unterhalb dieser Kategorie. Ein Klick auf eine Blase führt auf die entsprechende Kategorieübersicht bei BonnerBlogs.de. Ich denke, hier geht noch mehr! Wie erwähnt werden die Kategorien pro Blog vergeben. Es war auch nicht schwer, einige Blogs zu finden, die thematisch aber durchaus breiter aufgestellt sind. Außerdem habe ich persönlich so meine Probleme mit eher generischen Kategorien wie «Persönlich», «Livestyle» oder «Gesellschaft», die nicht überraschend auch zu den dominanten Kategorien gehören. Ganz abgesehen davon, können sich Blogs natürlich auch mit der Zeit thematisch neu orientieren. Für weiterführende Experimente würde ich hier auf Artikelbasis kategorisieren und dafür automatische Verfahren der Textanalye heranziehen. Ein Mittel, das zum Beispiel bei Nachrichtenanbietern gern verwendet wird, ist eine Auswahl von Schlagwörtern, die zur Verlinkung von Artikeln herangezogen werden. In der Regel werden die bei Nachrichtenhäusern manuell vergeben. Ich hatte dagegen mit einer Python-basierten TextRank-Implementierung22 experimentiert, die schon sehr interessante Ergebnisse geliefert hat. Der Algorithmus ist roboust, hat eine gewisse Tradition und ist so performant, dass man ihn auch auf Subsets des Korpus in Echtzeit anwenden kann. Denkbare Erweiterungen für BonnerBlogs.de:

  • Worüber haben die Bonner in den letzten zwei Wochen gebloggt?
  • Wie haben sich Themen/Schlagwörter – hust Bonner Bäder – über die Zeit entwickelt?
  • Worüber schreibt der Autor von Blog XY?
  • Wie haben sich dessen Themen verändert?
  • . . . ?

Außerdem gibt es auch einen bunten Blumenstrauß von Machine Learning Algorithmen, die sich mit dem Clustering von Artikeln nach thematischer Ähnlichkeit beschäftigen. Ein recht bekannter Ansatz ist Latent Dirichlet Allocation23, für den es wie so oft auch bereits eine gute Python-Implementierung gibt und dessen Einsatz auch kürzlich in einem sehr guten Codecentric-Blogartikel24 erläutert wurde. Wie so oft, bin ich auch hier noch nicht dazu gekommen, tiefer einzusteigen und belastbare Experimente mit den Blogdaten durchzuführen.

Veröffentlichungszeitpunkte

Wie bereits erwähnt, standen die Veröffentlichungszeitpunkte der Artikel nicht zur Verfügung. Daher blieb nichts anderes übrig als ein Skript zu schreiben, dass sich diese Information aus den Rohdaten besorgt. Aus dieser Problemstellung ist ein Stück Software entstanden, dass diese Aufgabe für etwa 80% der Daten gut löst25 – was übrigens auch wesentlich ergiebiger ist, als ein ähnliches Werkzeug in newspaper3k26. Den quantitativen Beweis müsste ich aber nochmal durchführen und hier nachreichen. Für die Artikel, für dich ich VÖ-Zeitpunkte finden konnte, wurden vor allem die entsprechenden Metadaten sauber ausgewiesen. Ein gutes Drittel hatte etwa ein HTML-Metafeld published_time oder datePublished27 angegeben. Ansonsten gab es auch einige json-ld28 und Dublin-Core 29 Metadaten, der Rest war vor allem HTML-Parsing und Suchen nach div-Elementen, die publishDate oder ähnliche Klassen oder ID’s nutzten.

Der Veröffentlichungszeitpunkt ist zum Auswerten derartiger Quellen (eigentlich) bereits ein sehr mächtiges Werkzeug. Wer das nicht glaubt, der sollte mal David Kriesels Ausführungen im Rahmen des SpiegelMinings lesen.30 Daher schmerzt es schon sehr, dass ich hier nur mit einer Annäherung arbeiten konnte. In der folgenden Abbildung deuten sich trotz vielen interessanten Details schon die ersten Probleme an:

Die Abbildung listet die Blogs mit den meisten Artikeln und deren relative Artikeldichte über die Zeit – allerdings pro Blog, d.h. die Größenverhältnisse sind nur innerhalb eines Blog korrekt. Wenn ihr über die Kreise hovert, seht ihr die Anzahl Artikel pro Blog und Kalendermonat. Die Visualisierung liefert einen Überblick über Fragen wie: Seit wann existiert ein Blog? Wann hat ein Blogger oder eine Bloggerin viel oder wenig gebloggt oder gar aufgehört, zu bloggen?

Hier fehlen noch einige Blogs, u.a. Johannes’31 1ppm-Blog32 müsste eigentlich dabei sein, allerdings konnte ich bei einigen der Top-Blogs noch keine verlässlichen Zeitstempel rausparsen. Außerdem habe ich gesehen, dass z.B. hagen-bauer.de33 oder fingerboardTV34 nicht aufgehört haben zu bloggen, obwohl das hier so aussieht. Hier hat sich eventuell der RSS-Feed geändert, aber das habe ich bisher noch nicht genauer untersuchen können. Einen wichtigen Aha-Moment bei der Auswertung des Korpus hatte ich nach der Betrachtung der folgenden Abbildung:

Wir lassen hier das Jahr 2017 mal außer Acht, denn die neusten Artikel im Dump waren irgendwann um Anfang April veröffentlicht worden. Die Anzahl der Artikeln, die durch den RSS-Crawler gefunden werden, wächst seit Ende 2013 stetig an, wie man an der zusätzlichen Trendkurve ganz gut ablesen kann. Das klingt sinnvoll, wenn man beachtet, dass BonnerBlogs.de seit Ende 2013 crawled und natürlich erst mit wachsender Bekanntheit neue Blogs den Weg in diesen Aggregator finden und eventuell Blogbetreiber gar selbst auf die Idee kommen, ihren Blog dort anzumelden. Daraus kann man eine handvoll Schlussfolgerungen für weitere Analysen ziehen. Wenn man zum Beispiel die Geschichte der Bonner Blogosphäre analysieren möchte, kommt man nicht daran vorbei, zumindest die noch verfügbaren Blogs aus den rund 1100 Quellen komplett zu scrapen und nicht nur die Daten des Crawlers heranzuziehen. Noch deutlicher wird es mithilfe der nächsten Abbildung:

Etwa die Frage nach dem ältesten Bonner Blog lässt sich anhand der Daten nicht beantworten, da Artikel vor Ende 2013 nicht enthalten sind, auch wenn ein Blog schon länger existiert. Und da einige Blogs mittlerweile offline sind, ist ein Teil der Historie auch gänzlich verloren – wobei ich zugegebenermaßen nicht weiß, was Google’s Caches hier noch hergeben. In jedem Fall wäre es bei über 1000 gesammelten Blog-Domains eine sehr interessante Datenbasis. Auch aus rein archivarischem Interesse für das digitale Bonn. Nicht weniger spannend wäre es, die 1000+ Blogfeeds nochmal auf tote Links zu prüfen und dann erstmal mit den noch in den Feeds verfügbaren Artikeln zu arbeiten. Das sollten so grob 10 bis 20 Artikel pro Blog sein – das hängt vom Betreiber, dem CMS, dem Feedcatcher und anderen Faktoren ab. Aber von 10.000 bis 20.000 Artikeln sprechen wir sicherlich.

Aber damit wir nicht komplett unglücklich aus dieser Sektion aussteigen, kann man sich ja zumindest man anschauen, wann die Bonner Bloggerin und der Bonner Blogger am liebsten bloggen. Das geben die Daten in jedem Fall her.

Die Darstellung der Artikel pro Kalendermonat zeigt ein deutliches Sommerloch und anscheind schreibt man am liebsten nach der großen Weihnachtslethargie und bevor dann so langsam die Schwimmbäder aufmachen. Und auch innerhalb der Woche sieht es nach relativ normalen Motivationskurven aus. Die Produktivität ist zu Beginn der Woche anscheinend am höchstens und fällt dann mit jedem Wochentag ab. Ich hätte allerdings eher damit gerechnet, dass dann am Wochenende deutlich mehr gebloggt wird. Vielleicht gibt es ja einen Zusammenhang zwischen der Häufigkeit, mit der an Wochenenden gebloggt wird und der Frage, ob wir von einem rein privaten Blog35 oder einem Blog mit kommerziellem Background22 sprechen. Ein Punkt für weiterführende Arbeiten.

Fazit

Eigentlich kann man sich auf Basis der vorliegenden Daten eine richtig nette, interaktive Webapplikation vorstellen, mit der ein Anwender die Bonner Blogosphere explorieren könnte. Aber das ist dann wohl weder ein Ein-Person-Hobbyprojekt, noch ein interessanter Businesscase – zur Erinnung: Software Entwickler, kein Sales Experte ;) Auch wenn mir klar ist, dass die Erkenntnisse noch nicht allzu tiefgreifend sind, gibt es einige Take-Home-Messages, die für nachfolgende Arbeiten berücksichtigt werden können:

  • Einige Feed-URIs sind veraltet oder die Blogs gänzlich offline – hier sollte man mal aktualisieren.
  • 90% der Blogs sind deutschsprachig, über 99% deutsch- oder englischsprachig. Sehr vielen Natural Language Processing / Machine Learning Algorithmen haben dafür Tools an Bord.
  • Die manuellen Kategorien sind mit Vorsicht zu genießen, da sie je Blog vergeben werden, was für thematisch breite Blogs problematisch sein kann.
  • Eine Analyse der Volltextdaten für die Analyse von Themen oder ähnliches halte ich für vielversprechend.
  • Der Korpus sollte auf Artikelebene kategorisiert werden.
  • Der RSS-Crawler sollte dringend mehr Informationen abgreifen, die er ohnehin «in die Hände bekommt», u.a. Veröffentlichungszeitpunkte der Artikel, Titel des Blogartikels und idealerweise den rohen HTML-Content des Artikels zum Zeitpunkt des ersten Besuchs (wir reden insgesamt von Small Data, der Speicherbedarf für alle Rohdaten des Korpus ist anno 2017 nicht der Rede wert)
  • Wenn man sich die Mühe machen will, könnte man alle bekannten Blogs nochmal komplett neu scrapen, um eine vollständige Blogdatenbasis zu erhalten. Insbesondere ältere Blogs werden in Auswertungen bestraft, da nur Artikel ab Ende 2013 vom Crawler berücksichtigt wurden.
  • Für interessante Datenanalysen der Bonner Blogs (eventuell als interaktive Webanwendungen) halte ich kleinere Zeitfenster von heute in die Vergangenheit (z.B. die letzten drei Monate) für vielversprechend. Etwas hinderlich daran ist bisher, dass die Rohdaten statische SQL Dumps sind und nicht regelmäßig über ein öffentliches Web-API aktualisiert werden können ;)

Selbstverständlich ist Feedback gern gesehen und wenn ihr Fragen und Anregungen habt, hinterlasst eine Nachricht unter dem verlinkten Tweet oder über die anderen Kommunikationswege, an die ich aktuell gerade angeschlossen bin.36

Anhang

PyData software stack

Nachfolgend eine Liste von Python-basierten Bibliotheken, über die ich im PyData Umfeld gestolpert bin. Ich habe ausschließlich mit Open-Source Tools gearbeitet. Darüber hinaus sind alle Bibliotheken bei GitHub gehostet. Was will man mehr?

Referenzen

Und zum Schluss noch die im Artikel referenzierten Quellen.