Webscraping mit Raspberry Pi – Teil 2 – Beautifulsoup

Von jeder Website, die aufgerufen werden kann, können auch Daten extrahiert werden. Manche Websiten bieten APIs an, um eine direkte Schnittstelle zu haben. Wenn so etwas nicht existiert, kommt „Webscraping“ zum Zuge. Webscraping heisst, dass Daten direkt aus dem Quelltext einer HTML-Seite extrahiert werden.

Da in diesem Tutorial mit Beautifulsoup gearbeitet werden soll, muss eine HTML-Website angesteuert werden. Javascript kann mit BeautifulSoup4 nicht verwendet werden. Hierfür gibt es andere Möglichkeiten (Selenium).

Welche Daten möchte ich haben?

Meine Frau und ich haben schon lange die Vermutung, dass wir morgens im Bad immer wieder dieselben Lieder im Radio hören. Ich möchte herausfinden, ob dies nur eine subjektive Empfindung ist, oder sogar objektiv feststellbar ist.

BeiRadio Bremenvier gibt es eine Tabelle, auf der die zuletzt gespielten Lieder vom Radiosender Bremen4 angezeigt und ständig aktualisiert werden. Die Tabelle ist im HTML code recht einfach zu finden.

  • Das Element der Tabelle, dessen Informationen ich abspeichern möchte, markieren und Rechtsklick für „Element untersuchen (z.B. im Browser Chrome)
  • Tabellen auf Websites werden mit dem <table> – Tag eingeleitet und mit </table> geschlossen. Innerhalb der Tabellen wird mittels <tr> die Reihe und mit <td> die Spalte bzw. Zelle der Reihe definiert. Mich interessiert nur der erste Song-Eintrag in der Tabelle. Dieser ist innterhalb der zweiten Reihe der Tabelle zu finden. In der ersten Reihe stehen die Überschriften der Spalte.
<tr class="top44_table_line1">
		<td class="top44_table_zelle left bottom">14:23</td>
		<td class="top44_table_zelle  bottom">Smith,Sam</td>
		<td class="top44_table_zelle right bottom">I'm not the only one</td>
	</tr>

An dem Quellcode-Ausschnitt wird deutlich, dass die interessierende Tabellenreihe das Attribut class=“top44_table_line1“ enhält. Alle Informationen die innerhalb dieses <tr>-Tags sind, will ich haben.

Python Code mit Beautifulsoup
Untenstehend ist der Python-Code, der genutzt wird, um die Radiosongs (d.h. Uhrzeit, Künstler, Titel) von der Website zu scrapen. Da die Uhrzeit von der Website kein Datum enthält, ergänze ich Jahr, Monat und Tag über ein Python-Package.
Ich beschreibe kurz, was der untere Code macht. Als erstes wird die Arbeitsumgebung geschaffen, indem die Module geladen werden. Dann wird ein leeres Objekt erstellt, dass ich später benötige.
Anschließend wird eine Endlosschleife gestartet, die alle zwei Minuten durchläuft. Zuerst wird die Website abgefragt und als Beautifulsoup-Objekt abgelegt. Beautifulsoup wird nun angewiesen in den festgelegten Zellen der HTML-Tabelle nach Informationen zu suchen und diese in eine Datei abzulegen. Jedoch nur, wenn der neue Eintrag nicht derselbe wie der alte ist. Anschließend wartet das Programm zwei Minuten, um anschließend von vorne loszulegen. Die „Else“ Bedingung tritt z.B. dann in Kraft, wenn keine Information auf der HTML-Website zu finden war. Dies ist – wie ich herausfand – Nachts von 0:00 bis 6:00 morgens der Fall, da Bremenvier um diese Zeit keine eigenen Lieder sendet, sondern von einem anderen Sender der RadioBremen-Gruppe übernimmt.

Den Code habe ich übrigens während einer „ONOC – Open Night of Coding“ an der Uni-Bremen geschrieben. Wie ihr seht, ist der Code allerdings nicht besonders lang, weshalb meine ONOC eher eine „OHOC – Open Hour of Coding“ war 😉

#Importieren der Module
import requests
import time
import datetime
from bs4 import BeautifulSoup

# leeren Wert fuer den Titelvergleich erstellen, da dieser beim ersten Durchlauf noch nicht existiert
last=""

#Endlosschleife starten
while (True):
    try:
        #Website aufrufen und als soup-objekt nutzbar machen
        fobj = open("daten.dat","a")
        r  = requests.get("http://www.radiobremen.de/bremenvier/musik/titelsuche/index.html")
        data = r.text
        soup = BeautifulSoup(data)
        
        #Gesuchte Informationen werden als Objekt gespeichert          
        line1= soup.select('tr.top44_table_line1 > td')[0]
        line2= soup.select('tr.top44_table_line1 > td')[1]
        line3= soup.select('tr.top44_table_line1 > td')[2]
        now = datetime.datetime.now().strftime("%y-%m-%d")            
        entry="{Artist:s}\t{Song:s}\t{Zeit:s}\t{Datum}".format(Zeit=line1.text,Artist=line2.text,Song=line3.text,Datum=now)
        print(entry)
        
        #Abgleich, ob bei der Abfrage noch dasselbe Lied läuft wie bei der letzten Abfrage, wenn nicht wird das neue Lied eingetragen und ein neuer Wert für den Titelvergleich definiert
        if last != entry:
                fobj.write(entry + "\n")
                last=entry
                fobj.close()      
        time.sleep(120)
               
    except:
        print("Did not find any data")

Auf dem Raspberry Pi findet sich anschließend eine Datei namens „daten.dat“, in der eine Tabulatorseparierte Liste von Einträgen steht.

Twitter-mining mit R – Teil 6 – Tweets im Zeitverlauf visualisieren – in Überarbeitung

Der letzte Blogeintrag (Teil 5) zeigte ein einfaches Rezept, um eine Followermap zu erstellen.

Hier möchte ich ein Beispiel zeigen, in dem ich die Tweethäufigkeit zu einem Hashtag über einen Zeitraum visualisiere. Konkret möchte ich die Tweets zum Fußballspiel von Werder-Bremen gegen Hannover96 nutzen. Wann wird am meisten getwittert? Wenn etwas spannendes auf dem Spielfeld passiert (Tor, gelbe Karte, Freistoß)? Oder überwiegend vor und nach dem Spiel beziehungsweise in der Halbzeitpause, wenn das Spiel keine Aufmerksamkeit erfordert?

Folgende Schritte sind nötig:

  • Verbindung zur Twitter-(Streaming)-API herstellen und relevante Tweets speichern.
  • Spielereignisse und ihre Zeitpunkte von Liveticker abgreifen und als Tabelle abspeichern
  • Daten formatieren/bereinigen, aggregieren und für Grafik vorbereiten
  • Daten mit ggplot2 plotten

 
Verbindung zur Streaming-API
Wie immer: Zunächst eine Verbindung mit der Twitter-API herstellen. In diesem Fall mit der Streaming-API, was einen etwas anderen Code erfordert, als die Rest-API

#-----------------------------------------------------
# --- Mit Twitter verbinden ---
#-----------------------------------------------------
library(RCurl)
library(ROAuth)
library(twitteR)
library(streamR)

# Authentifizierungsschlüssel eingeben
api_key <- "**************************"
api_secret <- "***************************"
access_token <- "*****************************"
access_token_secret <- "******************************"
options(RCurlOptions = list(cainfo = system.file("CurlSSL", "cacert.pem", package = "RCurl")))#nicht für MAC/Linux-User

my_oauth consumerSecret=api_secret,
requestURL='https://api.twitter.com/oauth/request_token',
accessURL='https://api.twitter.com/oauth/access_token',
authURL='https://api.twitter.com/oauth/authorize')

my_oauth$handshake(cainfo = system.file("CurlSSL", "cacert.pem", package = "RCurl"))

Mit dem Ausführen der letzten Codezeile öffnet sich ein Browserfenster und zeigt eine PIN an. Diese PIN muss nun in die R-console eingegeben werden, um die Verbindung herzustellen.

Tweets während des Fußballspiels speichern
Nun soll der Twitter-Datenstream zu den Suchbegriffen „Werder“,“H96″ und „SVWH96“ abgespeichert werden. Die ersten beiden Suchbegriffe stehen natürlich für die Mannschaften. Der dritte Begriff ist das Hashtag für das Bundeligaspiel. Es hat sich eingebürgert, beide Kürzel der Mannschaften in einem Hashtag zu kombinieren, wobei die Heimatmannschaft zuerst genannt wird. Die Suchbegriffe werden im untenstehenden Code mit dem Argument „track“ als Komma-separierte Liste festgelegt. So werden Tweets gespeichert, die mindestens einen der Begriffe enthalten. Das Komma entspricht dem logischen „OR“. Lässt man es weg, sind die Begriffe wie beim logischen „AND“ verknüpft.

Ich habe mich für die Streaming-API entschieden, da ich hierdurch wesentlich mehr Tweets erhalte. Die Streaming-API greift die Tweets fortlaufend ab. Deshalb muss ich den Zeitraum festlegen, für den ich Tweets haben möchte. Die Tweets sollen 6 Stunden lang aufgezeichnet werden. Dies lege ich mit dem Wert 21600 im timeout-Argument fest (21600 = 6 x 60min x 60sek).

#Tweets abgreifen
filterStream(file.name="C:/Beispieldaten/svwh96b.json", track=c("svwh96","Werder","H96"), timeout=21600, oauth=my_oauth)

#Tweets in Dataframe packen
tweets_svw <- parseTweets("C:/Beispieldaten/svwh96b.json", verbose = TRUE)

 

Spielereignisse in Tabelle speichern
Nachdem wir die Tweets haben, möchten wir nun eine Tabelle mit den Spielereignissen erstellen. Am einfachsten ist es, eine Liveticker-Tabelle von einer Sport-Homepage zu kopieren, in eine Tabellenkalkulation (Excel, LibreOfficeCalc, Numbers) einzufügen und als .csv-Datei zu speichern.

Es geht natürlich auch komplizierter. Der untenstehende Code zeigt, wie „rvest“ (ein Webscraping-tool von Hadley Wickham) genutzt werden kann, um Daten von Websiten zu holen. Der Code zeigt nur den Ansatz, das Data-cleaning habe ich nicht eingefügt.

#--------------------------------------------------------------------------
# --- Webscraping der Spielverlauf-Tabelle
#--------------------------------------------------------------------------
install.packages("rvest")
library(rvest)

#Website parsen und anschließend, den interessierenden TEXT extrahieren
spielbericht<- html("http://www.weser-kurier.de/werder/werder-bundesliga_artikel,-Unentschieden-im-Weserstadion-_arid,1013156.html")
Spielverlauf1<-as.character(spielbericht %>% html_nodes("p") %>% html_text())
Spielverlauf2<-as.character(spielbericht %>% html_nodes("strong") %>% html_text())

#Vector in Minute und Ereignis trennen und als dataframe speichern
library(reshape2)
Spielverlauf<-colsplit(Spielverlauf," ",c("Minute","Ereignis"))
#hier beginnt dann das Datacleaning. 

 

Daten für Visualisierung vorbereiten
Ich habe oben zwei dataframes erzeugt: Einen dataframe mit den tweets über 6 Stunden, den anderen dataframe mit den wichtigsten Spielereignissen und den entsprechenden Spielminuten.

tweets_svw<-read.csv(file='c:/Beispieldaten/svwh96_streamR.csv', header=T)
Spielverlauf<-read.csv2(file="C:/Beispieldaten/Spielereignisse.csv",sep=";",header=FALSE,col.names=c("Minute","Ereignis"))

Eine schöne Sache von R ist, dass Zeit- und Datumsangaben eine eigene Klasse (class) besitzen. Die Klasse legt fest, wie mit den Daten umgegangen werden soll. Sehr vereinfacht, wissen Pakete wie GGPlot2 durch die Klasse „ah, wenn ich auf 15:30 Uhr 40 Minuten addiere, ist es 16:10 Uhr und nicht 15:70Uhr.“
Konkret nutze ich die Klasse POSIXct. Sie ist hilfreich, wenn Zeiten in dataframes gespeichert werden sollen. POSIXlt wäre die Alternative. Doch sie nutzt man eher, wenn es um Dinge wie Wochentage oder ähnliches geht.
Der Befehl „strptime“ ist nützlich, wenn die Daten noch nicht im Format der entsprechenden Klasse vorliegen (sondern z.B. als character vector) und noch umformatiert werden müssen.

Zeitformat der Tweets anpassen
Im tweets_svw dataframe gibt es eine Spalte namens „created_at“, welche den Zeitpunkt enthält, an dem ein Tweet geschrieben wurde. Diese Einträge sehen so aus: „Sat Dec 13 13:04:34 +0000 2014“. Um diese Daten als Zeitangabe nutzbar zu machen, konvertiere ich sie ins POSIXct-Format.

#Datenformat (musste ich bei mir anpassen)
Sys.setlocale("LC_TIME", "English") 
## Zeitformat der created_at-Variable auf POSIXct ändern
tweets_svw$Zeitformat <- as.POSIXct(tweets_svw$created_at, format = "%a %b %d %H:%M:%S %z %Y", tz="")
head(tweets_svw$Zeitformat)
[/code]
<strong>Zeitformat der Spielereignisse anpassen</strong>
Der Liveticker enthielt nur die Spielereignisse und die entsprechende Spielminute (0-92). Die Zeitangaben sollen aber in beiden dataframes im gleichen Format sein, daher muss ich die Spielminuten in Uhrzeiten umwandeln. Mit der POSIXct Klasse ist dies möglich. Der Anstoß war um 15:30. Hier müssen nun noch die Spielminuten aufaddiert werden. 
[code language="r"]
Spielverlauf$Uhrzeit<-c("2014-12-13 15:30:00")
Spielverlauf$Uhrzeit<- as.POSIXct(Spielverlauf$Uhrzeit,tz="")
Spielverlauf$Minute<- (Spielverlauf$Minute*60)
Spielverlauf$Uhrzeit<-Spielverlauf$Uhrzeit + Spielverlauf$Minute
head(Spielverlauf$Uhrzeit)

Anmerkung: Bei der Berechnung der Zeitpunkte für die Spielereignisse habe ich einen Fehler gemacht, da ich nicht berücksichtigt habe, dass die Halbzeit ebenfalls Zeit verbraucht.

Tweets pro Minute aggregieren
Mich interessiert nicht der Inhalt der Tweets, sondern ihre Häufigkeit vor, während und nach dem Spiel. Daher habe ich mich entschieden, zu zählen, wieviele Tweets pro Minute geschrieben wurden.
Der Code hierfür ist nicht wirklich schön und eventuell werde ich ihn nochmal aktualisieren, aber für den Moment macht er erstmal das, was ich möchte.
In Teil 1.) erzeuge ich einen neuen dataframe, der nur die Uhrzeit des abgesetzten Tweets enthält, sowie eine zweite Spalte mit einer „1“ als Zähler, um im nächsten Schritt die Tweets je Minute zu zählen.
In Teil 2.) zerstückele ich den dataframe nach Minuten, zähle die Tweets je Minute und bekomme am Ende einen neuen dataframe namens plot.df. In plot.df ist eine minutengenaue Zeitangabe (Uhrzeit) und die Anzahl der Tweets (Freq) in dieser Minute enthalten.

# 1.) 
tweets.df<-as.data.frame(tweets_svw$Zeitformat) #neuer dataframe
tweets.df$freq<-1 #jeder Tweet erhält einen Häufigkeitszähler
colnames(tweets.df)<-c("time","freq")
tweets.df$time <- as.POSIXct(tweets.df$time)

# 2.)
#Tweets pro Minute aggregieren und als Dataframe abspeichern
by.mins <- cut.POSIXt(tweets.df$time,"mins")
tweets.mins <- split(tweets.df, by.mins)
plot.df<-sapply(tweets.mins,function(x)sum(as.integer(x$freq)))
plot.df<-as.data.frame(plot.df)
plot.df<-cbind(Uhrzeit = rownames(plot.df), plot.df)
plot.df$Uhrzeit <- as.POSIXct(plot.df$Uhrzeit)
plot.df<-as.data.frame(plot.df)
names(plot.df)[names(plot.df) == 'plot.df'] <- 'Freq'

Die Vorbereitungen sind jetzt fast abgeschlossen. Wir müssen nur noch eine unnütze Spalte aus den dataframes loswerden und vorsichtshalber nochmal das Datumsformat festlegen.

library(dplyr)
plot.df<- plot.df %>% select(Uhrzeit,Freq)
Spielverlauf<- Spielverlauf %>% select(Uhrzeit,Ereignis)
plot.df$Uhrzeit<-as.POSIXct(plot.df$Uhrzeit)
Spielverlauf$Uhrzeit<-as.POSIXct(Spielverlauf$Uhrzeit)

Tweet-Daten und Spielereignis-Daten zusammenfügen
Mittels „plyr“ werden die dataframes anhand der gemeinsamen Uhrzeiten in einen gemeinsamen dataframe überführt. Das all.x=TRUE Argument ist wichtig, da somit alle Einträge aus dem plot.df dataframe übernommen werden. Dies entspricht einem „left join“. Das Bild hinter diesem Link verdeutlicht das.

library(plyr)
daten<- merge(plot.df,Spielverlauf,by="Uhrzeit", all.x=TRUE) 

Wenn wir den Text zu jedem Spielereignis plotten würden, würde man von der eigentlichen Grafik nichts mehr sehen. Daher filtere ich alle Spielereignisse raus, die nicht während der 10% der stärksten Tweetminuten geschrieben wurden.

daten$Ereignis<-as.character(daten$Ereignis)
daten3 <- daten %>% mutate(cumdist = cume_dist(desc(Freq)), 
                         top = as.character(ifelse(cumdist < 0.1, Ereignis, NA))) %>%
                         select(Uhrzeit,Freq, Ereignis, top)

Visualisieren
Endlich können wir die Daten visualisieren. Der dataframe „daten“ enthält die Spalten „Uhrzeit“, „Freq“,“Ereignis“ und „Top“. „Top“ enthält dasselbe wie „Ereignis“, allerdings nur für die 10% der Minuten mit der größten Tweetproduktion.

Grafik 1
Grafik1

ggplot(daten3,aes(Uhrzeit,Freq)) +geom_line()+ geom_area(fill="darkgreen",alpha=.8)+theme_bw()

Grafik 2
Grafik2

ggplot(daten3) + geom_bar(aes(Uhrzeit,Freq),stat="identity",colour="darkgreen") + geom_text(aes(Uhrzeit,Freq,label=top),size=4)+ theme_bw()

Grafik 3
Grafik3

xmin<-as.POSIXct(strptime("2014-12-13 15:30:00", "%Y-%m-%d %H:%M:%S"))
xmax<-as.POSIXct(strptime("2014-12-13 17:02:00", "%Y-%m-%d %H:%M:%S"))
Halbzeit<-as.POSIXct(strptime("2014-12-13 16:15:00", "%Y-%m-%d %H:%M:%S"))
ggplot(daten3,aes(Uhrzeit,Freq))+geom_rect(data=NULL,aes(xmin=xmin,xmax=xmax,ymin=0,ymax=Inf),alpha=0.9,fill="lightgrey")+geom_rect(data=NULL,aes(xmin=Halbzeit,xmax=Halbzeit,ymin=0,ymax=Inf),fill="black")+geom_line(colour="black") + geom_area(fill="darkgreen",alpha=.8) +theme_bw() 

Twitter-mining mit R – Teil 5 – Followermap

Mit nur wenigen Zeilen R-Code ist es möglich, die globale Verteilung der Follower eines Accounts zu Visualisieren. Dies geht besonders einfach, wenn man eine Funktion zur Hilfe nimmt, die auf folgender Homepage veröffentlicht wurde http://simplystatistics.org/2011/12/21/an-r-function-to-map-your-twitter-followers/.

Zwei Dinge sollten bedacht werden:

  • Viele Twitter-User tragen aus Datenschutzgründen keinen, oder einen falschen Standort ein
  • Visualisierungen wie diese Followermap bilden häufig nur die variiernde Bevölkerungsdichte bzw. die variierende Dichte des Internetzugangs über die Weltregionen ab

Randell Munroe von XKCD bringt das Problem mit einem Comic treffend auf den Punkt:
XKCD.com

#-----------------------------------------------------
# --- Mit Twitter verbinden ---
#-----------------------------------------------------
library(twitteR)
# Authentifizierungsschlüssel eingeben
api_key <- "**************************"
api_secret <- "***************************"
access_token <- "*****************************"
access_token_secret <- "******************************"
setup_twitter_oauth(api_key,api_secret,access_token,access_token_secret)

#Followermap-Funktion laden
source("http://biostat.jhsph.edu/~jleek/code/twitterMap.R")

#Followermap-Funktion ausführen (erstellt ein PDF)
twitterMap("OfficialSanta",nMax=2000,plotType="followers",userLocation="Anchorage",fileName="Weihnachtsmann-Follower.pdf")

Das Ausführen dieser Funktion kann etwas länger dauern (durchaus einige Minuten). Es müssen schließlich die Koordinaten von bis zu 2000 Accounts abgefragt werden. Nach einiger Wartezeit erhält man eine solche Grafik:

Als Wohnsitz von Santa Claus wurde der North Pole in Alaska bei Fairbanks gewählt bzw. die nächstgrößte Stadt Anchorage.

Als Wohnsitz von Santa Claus wurde der North Pole in Alaska bei Fairbanks gewählt bzw. die nächstgrößte Stadt Anchorage.

Twitter-mining mit R – Teil 3 – Erweiterte Wordclouds

Im letzten Blogeintrag (Teil 2) wurden die Ergebnisse einer Twitter-Suche als Wordcloud visualisiert. Man kann das Potential von Wordclouds noch etwas weiter ausschöpfen, wenn man Wordclouds miteinander vergleicht. Das  folgende Beispiel orientiert sich an dem code von dieser Seite.

Wie immer, brauchen wir zuerst die Verbindung zwischen R und der Twitter-API.

#-----------------------------------------------------
# --- Mit Twitter verbinden ---
#-----------------------------------------------------
library(twitteR)
# Authentifizierungsschlüssel eingeben
api_key <- "**************************"
api_secret <- "***************************"
access_token <- "*****************************"
access_token_secret <- "******************************"
setup_twitter_oauth(api_key,api_secret,access_token,access_token_secret)

#--- Suchabfrage: 450 tweets mit dem Hashtag #rstats ---
tweets<-searchTwitter("christmas",n=450))
tweets

Comparison Wordcloud
Als Beispiel werde ich die Tweets der Bundestagsfraktionen von CDU, SPD und den Grünen abgreifen und jeweils einen Textkorpus erstellen, der dann zusammengefügt in einer einzelnen Wordcloud visualisiert wird. Jede Partei wird eine Wordcloud ihrer Farbe erhalten. Dadurch kann vielleicht ein Einblick erhalten werden, über welche inhaltlichen Schwerpunkte die Parteien jeweils am häufigsten twittern.

#Twitter Accounts der Bundestagsfraktionen CDU/CSU, SPD, Gruene(wordcloud)

library(tm)
library(wordcloud)
cdu.tweets<-userTimeline("CDUCSUBT",n=500)
spd.tweets<-userTimeline("SPDBT",n=500)
gruene.tweets<-userTimeline("GrueneBundestag",n=500)

# get text
cdu_txt = sapply(cdu.tweets, function(x) x$getText())
spd_txt = sapply(spd.tweets, function(x) x$getText())
gruene_txt = sapply(gruene.tweets, function(x) x$getText())

#text cleanings
clean.text = function(x)
{
# tolower
x = tolower(x)
# remove rt
x = gsub("rt", "", x)
# remove at
x = gsub("@\\w+", "", x)
# remove punctuation
x = gsub("[[:punct:]]", "", x)
# remove numbers
x = gsub("[[:digit:]]", "", x)
# remove links http
x = gsub("http\\w+", "", x)
# remove tabs
x = gsub("[ |\t]{2,}", "", x)
# remove blank spaces at the beginning
x = gsub("^ ", "", x)
# remove blank spaces at the end
x = gsub(" $", "", x)
return(x)
}

cdu_clean = clean.text(cdu_txt)
spd_clean = clean.text(spd_txt)
gruene_clean = clean.text(gruene_txt)

#join text
cdu = paste(cdu_clean, collapse=" ")
spd = paste(spd_clean, collapse=" ")
gruen = paste(gruene_clean, collapse=" ")
# put everything in a single vector
all = c(cdu, spd, gruen)

Die Daten sind nun vorbereitet und können geplottet werden.
An dem Punkt, an dem die TermDocumentMatrix erstellt wird, werden mittels „stopwords(„german“) häufig genannte deutsche Wörter („der“, „die“, „das“, „und“…) von der Auswertung ausgeklammert.

#WORDCLOUD COMPARISON
# create corpus
corpus = Corpus(VectorSource(all))
# create term-document matrix 
tdm = TermDocumentMatrix(corpus, control = list(removePunctuation = TRUE, stopwords = stopwords("german"),removeNumbers = TRUE))
# convert as matrix
tdm = as.matrix(tdm)
# add column names
colnames(tdm) = c("CDU", "SPD", "Grüne")

#PLOTTING
# comparison cloud
comparison.cloud(tdm, random.order=FALSE, colors = c("black", "red", "green"),max.words=100)

Die untenstehende Grafik ist das Ergebnis. Während bei der CDU/CSU-Fraktion häufig der Fraktionsvorsitzende Kauder erwähnt wird sowie der Nachruf auf „Schockenhoff“ viel Raum einnimmt, werden auch „Europa“, „Erbschaftssteuer“, „Ukraine“ und „Europa“ genannt. Auch bei den Grünen wird der Fraktionsvorsitzende (Hofreiter) am häufigsten genannt. Ansonsten ist die Klimakonferenz in Lima scheinbar ein Hauptthema. Der Twitteraccount der SPD-Bundestagsfraktion scheint ein breites Themenrepertoire zu haben, da sich keine einzelnen thematischen Schwerpunkte abheben. Jedoch zeigt sich wie bei der CDU und den Grünen, dass die Fraktionsvorsitzende Lambrecht häufig genannt wird.

Comparison Wordcloud. Tweets der Accounts der CDU, SPD und Grünen Bundestagsfraktion (je 450 tweets)

Comparison Wordcloud. Tweets der Accounts der CDU, SPD und Grünen Bundestagsfraktion (je 450 tweets)

Commonality Cloud
Eine Alternative zur Comparison Wordcloud ist die Commonality Cloud. Sie wird ebenfalls mit der wordcloud-library erstellt.

# commonality cloud
commonality.cloud(tdm, random.order=FALSE, colors = brewer.pal(8, "Dark2"),title.size=1.5)

Leider kristalliert sich in der Commonality Cloud kaum Übereinstimmung ab. Eventuell sollten mehr Tweets abgegriffen und häufige Wörter wie „die, der, das“ aus der Analyse ausgeschlossen werden.

Jeweils 450 letzte Tweets der Accounts der CDU, SPD und Grünen Bundestagsfraktion als Commonality Cloud

Jeweils 450 letzte Tweets der Accounts der CDU, SPD und Grünen Bundestagsfraktion als Commonality Cloud

Im nächsten Blogeintrag (Teil 4) zeige ich, wie eine einfache Sentiment Analysis mit Tweets durchgeführt werden kann.