Archiv der Kategorie: Politik

Eine chronologisch sortierte Historie zu #aufschrei

Im Januar 2013 wurde auf Twitter unter dem Hashtag #aufschrei eine Aktion gestartet, die eine (nicht nur) deutschlandweite Debatte über Sexismus auslöste. Bisher fehlte eine Historie mit allen Tweets zu diesem Hashtag. Aufgrund der eingeschränkten Möglichkeit von Twitter über die API darauf zuzugreifen, wird dieses nie der Fall sein. Aber zumindest für die ersten zwei Wochen konnte ich ein Archiv erstellen und in einer angenehmen Form darstellen:

http://aufschrei.konvergenzfehler.de/

Scraping

Im Herbst 2012 entwickelte ich ein Script, um die Tweets für das #refugeecamp Berlin (später #rfcamp) einzusammeln. Für mich war es etwas Fingerübung in Python und mit der Twitter-API. Eigentlich hatte ich vor, daraus eine Webseite zu erstellen, die die Aktionen (Spendenaktionen, Temperaturwerte, Bilder, Gespräche mit Politikern und vor allem die Übergriffe der Polizei) darstellt und wichtige Zeitpunkte hervorhebt. Dank Zeitmangel und fehlenden Fähigkeiten wurde daraus nichts.

Ende Januar stieß mich map an und meinte “Du hast da doch mal dieses Script zum Scraping geschrieben – kannst Du die mal auf #aufschrei ansetzen? Uns fehlt der allererste Tweet.” Gefragt, getan. Die Tools lagen noch alle einsatzbereit auf einem Server und eine knappe Stunde später hatte ich etwa 50000 Tweets runtergeladen. Großer Jubel, als wir feststellten, dass der allererste Tweet dabei war.

Das Script sucht mit der Twitter Search API nach dem angegebenen Hashtag. Dabei geht es vom aktuellen Zeitpunkt iterativ immer weiter zurück, bis keine Tweets mehr gefunden werden. Die Search API findet Tweets der vergangenen neun Tage – mehr bietet Twitter nicht an. Außerdem gibt es Beschränkungen auf 200 Tweets pro Request, aber es kann das Datum des letzten gefundene Tweets als Startadresse für eine neue Suche angegeben werden.

Zwei Kleinigkeiten, über die ich schon beim Scraping für das #refugeecamp gestolpert war: Twitter erlaubt sich, Benutzer_innen aus der Search API zu verbannen, die viel mit einem Hashtag twittern. Die Benutzerin ist nicht geblockt, aber wird nicht mehr mittels einer Suche zurück gegeben. Die andere Kleinigkeit ist die Einstellung auf “gib mir alles” und nicht nur die “most trending Tweets”. Letzteres ist der Default, weshalb die Tweets nicht chronologisch ankommen, sondern nach irgendeiner Heuristik von Twitter, die meint, das seien die interessantesten Ergebnisse für diese Suche.

Fast schon unnötig es explizit anzugeben, aber es werden nur Tweets von Accounts erfasst, die zu dem Zeitpunkt auf public geschaltet waren. Wer einen protected Account hat, wird über die Search API nicht gefunden.

JSON-Daten – und nun?

Das Scraping-Script spuckte mir nach Tagen aufgeteilt JSON-Dateien aus. Da lag nun alles drin, aber ansehnlich war es nicht. Da ich eh schon mal ein Projekt mit Django machen wollte und sogar noch ein paar Anfänge zur #refugeecamp-Geschichte rumliegen hatte, begab ich mich an die Datenbankmodellierung. Da Django das Datenbank-Backend ziemlich egal ist, wählte ich PostgreSQL. Doch trotz eines fertigen Models mussten die Daten in die Datenbank kommen. Das klappt bei Django ganz gut mit Fixtures, was JSON-Dumps der Datenbank sind.

Also baute ich ein Script, was mir die JSON-Search-API-Dateien in JSON-Django-Fixtures umwandelte. Damit waren die 93667 Tweets in der Datenbank gespeichert. Recht fix schrieb ich ein paar Django-Views und einfache Templates, die mir eine chronologische Sortierung ausgaben.

Aber es war hässlich.

Das mag daran liegen, dass ich aus der Zeit von HTML2 komme und es seit 1995 gewohnt bin, Webseiten in einem Texteditor zu schreiben. CSS? Gab es nicht. JavaScript? Wurde im Browser abgeschaltet, weil böse. Pures HTML – mehr nicht! Und Webdesign? Ich fahre kein Snowboard.

Irgendwer verwies mich auf Bootstrap. Nachdem ich die Doku drei Mal durchlies, konnte ich es anwenden. So wurde die Ausgabe in Django immer schöner und ganz langsam gefiel mir mein Werk.

Weniger ist mehr

So ganz im klaren darüber, was die Webseite eigentlich können sollte, war ich mir nicht. Aber es gab ein paar Ideen. So war eine der Ideen, eine interaktive Oberfläche zu bieten, in der mit Crowd-Working die Tweets mit Tags versehen werden (beispielsweise Spam, KackscheisseVorfall, etc.). Das hätte Interaktion benötigt und Schreibzugriff auf die Datenbank. Oder ich wollte coole Auswertungen machen, so mit Graphen und Zeitleiste. Alles automatisiert aus den Daten in der Datenbank. Immer wieder überlegte ich, wie ich das machen sollte. Zwischendurch lag deswegen das Projekt monatelang in der Schublade (oder eher auf dem Server) und ich packte es nicht mehr an.

Aber bevor ich gar nichts mache und die Daten vergammeln, fällte ich die Entscheidung, nur eine Timeline anzuzeigen. Einfach und simpel.

Lokal ist besser als Remote

Mit meiner ersten Darstellung einer Timeline fiel mir auf, dass Links in Tweets vom Twitter-eigenen URL-Shortener t.co zum “Zwecke der Qualitätssteigerung” gekürzt werden. Dadurch lässt sich nicht erkennen, wohin verlinkt wird, geschweige eine Ahnung vom Inhalt zu erhalten.

Also, noch einen Scraper bauen, der alle Links durchgeht, die Original-URL wiederherstellt und am besten sogar noch die HTML-Titelzeile abholt und in der Datenbank speichert. Das machte ich mit der Python Bibliothek BeautifulSoup. Zwischendurch rannte ich in einen nicht lösbaren Speicherkiller-Bug von BeautifulSoup, weshalb das Scraping immer wieder neu angestoßen werden musste. Nach ein paar Stunden hatte ich 13992 Links zusammen und bewahrte sie wertvoll in der Datenbank auf.

Dieses Scraping lief erst im Mai und Juni 2013. Etliche Links werden als Fehler angezeigt, da die Webseite nicht mehr vorhanden war – ob nun tote Webseiten, gelöscht oder vom öffentlich-rechtlichen Rundfunk depubliziert worden.

Außerdem checkte ich die Links, ob es Bilder sind. Nicht jeden Webseiten-Bilder-Dienst habe ich unterstützt und bei manchen ist es auch verdammt schwer an die Original-URL eines Bilds zu kommen. Aber ein paar große sind dabei. Diese Bilder speicherte ich auch weg. 783 Bilder mit 80MB liegen lokal vor.

Und noch etwas wollte weggespeichert werden: Die Avatare der 25888 Twitter-Benutzer_innen, die an der Aktion mitgewirkt haben. Der ein oder andere Account war inzwischen gelöscht oder gesperrt, aber viele Avatare sind zusammen gekommen. Diese stellen einen alten Stand dar – aktuelle Avatare lade ich nicht runter.

Alles wegwerfen und neu anfangen

Ein paar Django Views waren zusammen gekommen, die Timeline konnte angezeigt werden, etwas Statistik war auch möglich. Aber alles sah zusammengestückelt aus. Nicht wie aus einem Guss. Da hilft nur eins: mit dem gesammelten Wissen noch mal neu machen.

Aber das geht dann recht fix. An einem Wochenende ist in etwa zehn Stunden die jetzige Seite mit allen Views entstanden. Direkt auf Bootstrap 3 aufgesetzt, mit diversen Templates, die einfach nur included werden, und nicht all zu viel Schnickschnack. Als kleine und binnen weniger Minuten umgesetzte Fingerübung kamen noch die reine Bilder– und die Linklisten-Timeline hinzu. Der “zufällige Tweet” war auch keine große Sache, musste nur etwas durchdacht werden.

Fehlte noch eine Startseite in einem etwas anderen Layout. Mit Bootstrap kein großes Problem.

Deploy

Bisher lief die Seite nur im Django Development Modus. Das ist kein Setup, was viele Requests abfrühstücken kann. Mit etwas Suche entschied ich mich für eine Kombination aus Nginx und uWSGI. Dieses Setup aufzusetzen ist nicht ganz einfach, aber es läuft sehr stabil.

Und was ich bei meinem aktuellen Job gelernt habe: Caches einsetzen. Viele Caches. Neben dem Datenbank-Cache gibt es noch einen Memcached und Nginx cached auch noch mal die uWSGI-Requests. Die Bilder werden aktuell nicht gecached, da sie im Laufe der Zeit vom Betriebssystem in die RAM-Caches geladen werden statt jedesmal auf die Festplatte zuzugreifen. Lägen sie Remote, würde auch hier ein Cache wieder Sinn machen.

Meine Freude war echt groß, als ich die Seite http://aufschrei.konvergenzfehler.de/ über dieses Setup aufrufen konnte. Und dass sie sogar auf einem Smartphone dank Bootstrap ohne Code-Änderung sehr gut aussieht!

Weiterentwicklung

Für mich ist das Projekt an dieser Stelle erstmal beendet, damit ich mich wieder anderen Dingen zuwenden kann. Aber wer gerne mit den Daten arbeiten oder die vorher genannten Ideen umsetzen möchte, kann das Projekt bei GitHub clonen. Das Fixture für die Datenbank liegt mit drin. Die Mediendateien (130MB) habe ich separat gepackt. Die Installation sollte “Standard-Django 1.5” sein.

Ich freue mich, wenn ich irgendwann in Zukunft noch tolle Projekte daraus entstehen sehe. Vielleicht eine schön aufgewertete Timeline. Oder sogar ein etwas größeres Projekt, was automatisch Hashtags mitschreibt und anzeigt, sodass in Zukunft nicht so viel Arbeit notwendig ist.

Hackerspaces im Abgeordnetenhaus von Berlin

Heute war ich einer Einladung des Ausschusses für Kulturelle Angelegenheiten im Abgeordnetenhaus von Berlin gefolgt. Der offizielle Tagesordnungspunkt hieß

  1. Besprechung gemäß § 21 Abs. 3 GO Abghs
    Vernetztes Denken in der Kultur – Berliner Hackerspaces als Träger und Produzenten kultureller Aktivitäten

Der Antrag zur Behandlung des Tagesordnungspunktes erfolgte bereits am 12. März 2012 auf Antrag der Piratenfraktion im Abgeordnetenhaus von Berlin. Ein halbes Jahr später wurde dieser Punkt auch aufgenommen und ich wurde als Sachverständiger von hackerspaces.org geladen.

Neben mir hat die Fraktion von Bündnis 90/Die Grünen Frank Rieger vom Chaos Computer Club e.V. als weiteren Sachverständigen zum Thema geladen.

Nach einem kurzen, frei gehaltenen Vortrag von Frank präsentierte ich ein paar Folien (PDF mit meinen Notizen), um das Thema Hackerspaces etwas näher zu bringen. Danach erfolgte eine zweifache Frage- und Antwortrunde für alle Fraktionen. Zu dem Tagesordnungspunkt wurde ein Wortprotokoll erstellt, das in der kommenden Woche auf der Webseite des Ausschusses veröffentlicht wird.

Möglicherweise ist der Ausschuss gewohnt, dass Gäste Gelder anfragen. Denn die häufigste Frage war, inwieweit der Senat Hackerspaces finanziell unterstützen kann. Dieses ist jedoch nicht unser Ziel gewesen, denn wir wollten erstmal das Bewusstsein für die Existenz von Hackerspaces als kulturschaffende Orte wecken. Berlin ist weltweit der Ort mit der größten Dichte an Hackerspaces (je nach Zählweise neun bis zwölf). Selbst Metropolen wie New York, San Francisco oder Paris kommen nicht auf diese Anzahl. Ein anderer Punkt war die nicht klare Zuordnung von Hackerspaces in den Bereich Kultur oder eher in den Bereich Bildung. Möglicherweise folgen weitere Gespräche mit den für Bildung zuständigen Ausschuss.

Meiner Ansicht nach, haben wir das Thema Hackerspaces gut vermittelt und die Aufmerksamkeit in allen Fraktionen geweckt. In seinen abschließenden Worten ließ uns Staatssekretär Schmitz wissen, dass er weiter mit uns in Kontakt bleiben möchte. Weitere Termine werden also folgen.

Ich bin auf das Wortprotokoll gespannt und werde es kommende Woche mit meinen Notizen ergänzen. Bisher gibt es eine kurze Zusammenfassung bei der Piratenfraktion Berlin.

 

Mehr Demokratie wagen

Am gestrigen Sonntag fand in Köln zum ersten Mal eine Bürgerbefragung in Form eines Bürgerentscheids statt. Die gestellte Frage lautete Soll der Godorfer Hafen weiter ausgebaut werden?“

Ganz kurz zu den Hintergründen: Im linksrheinischen Kölner Süden liegt an der Stadtgrenze zur Stadt Wesseling der Godorfer Hafen mit drei Becken, der hauptsächlich für die dortigen Raffinerien errichtet wurde. Der Hafen ist von der Autobahn A555 und dem Bahnanschluss der Häfen und Güterverkehr Köln AG (HGK) von Wesseling erreichbar. Seit mehr als 20 Jahren soll der Hafen um ein viertes Becken erweitert werden. Die Erweiterung betrifft das Naturschutzgebiet “Am Godorfer Hafen” (inoffiziell “Sürther Aue”). Nach ein wenig hin und her im Stadtrat in den Jahren 2006 und 2007 wurde mit dem Ausbau begonnen, dieser jedoch 2009 eingestellt und im März 2011 die Baugenehmigung entzogen.

Letzten Endes beschloss der Stadtrat die Durchführung eines Bürgerentscheids zu diesem Thema. Die formalen Ansprüche sind recht hoch gesetzt. Es müssen mindestens 10% der wahlberechtigten Bürger entweder mit Ja oder Nein stimmen, damit der Entscheid als gültig angesehen wird; ungültige Stimmen verfallen komplett. Sollte das Quorum nicht erreicht werden, gilt weiterhin der Ratsbeschluss zum Ausbau des Hafens – und falls doch ein Abstimmungsergebnis zustande kommt, hat sich der Stadtrat lediglich bereit erklärt, dieses in einer freiwilligen Selbstverpflichtung zu berücksichtigen.

Propagandaplakat an einer Bahnbrücke der Betreibergesellschaft Häfen und Güterverkehr Köln AG (HGK)

Ganz ehrlich: Egal wie abgestimmt wird, der Hafen wird gebaut. Letzten Endes gewinnt der Lobbyismus. Und das sind häufig die mit dem meisten Geld, den besseren Verbindungen “nach oben” oder den besseren Argumenten. Naturschutz hin oder her, die Stadt hat mehr Interesse an Wirtschaft und Arbeitsplätzen. Denn das gibt Geld ins Stadtsäckel – eine naturbelassene Aue nicht.

Inzwischen sind die Stimmen ausgezählt und mich persönlich überrascht das Ergebnis nicht: Das Quorum wurde nicht erreicht. Die Beteilugungsquote liegt bei 14,8%, sprich 130400 von 880937 wahlberechtigten Kölnern sind abstimmen gegangen. Keine der beiden Antwortmöglichkeiten konnte mindestens 10% der Stimmen auf sich vereinen. In der Pressemitteilung der Stadt spricht der Kölner Oberbürgermeister Jürgen Roters die geringe Wahlbeteiligung an. Schaut man auf die nach Stadtbezirken aufgeteilten Ergebnisse, ist ein Gefälle bei weiterer Entfernung vom Hafen festzustellen. Mir ist es durchaus verständlich, dass sich kaum Bürger bei einem Entscheid beteiligen, die mitunter 20 km von der Baustelle entfernt wohnen. Viel eher hätten die Bürger der direkt angrenzenden Stadt Wesseling oder auf der anderen Rheinseite gelegenen Stadt Niederkassel in die Befragung mit einbezogen werden sollen.

Nicht wirklich erstaunlich, aber bemerkenswert finde ich die Aussage des Oberbürgermeisters:

Unabhängig davon zeigt das Ergebnis eine breite Verteilung der Stimmen auf beiden Seiten. Die Analyse des Ergebnisses wird außerdem verdeutlichen, welche Einwohnergruppen mit der Befragung erreicht wurden. Daneben erhoffen wir uns Antworten auf die Frage, ob und wie weit solche Einwohnerbefragungen auch im Hinblick auf ihre nicht unerheblichen Kosten in der Breite von der Bevölkerung gewünscht werden.

Zählen wir zusammen: dieses war die erste Bürgerbefragung in Köln. Es wurde (absichtlich?) ein Thema gewählt, das nur einen kleinen Teil der Bürger in der Stadt interessiert und das selbst bei einem Ergebnis gegen den Ausbau des Hafens dieses nicht verpflichten für den Stadtrat ist. Durch das Nichterreichen des Quorums hat der Stadtrat seinen Persilschein, um dem Ausbau nachzukommen. Nun werden die Kosten einer Bürgerbefragung in Hinblick auf die geringe Beteiligungsquote als Grund verwendet, dieses Instrument der Demokratie zukünftig nicht weiter zu verwenden. Ein geschickter Schachzug des Stadtrates! Der Klüngel regiert Köln.