3  Twitter Mining

Text als Datenbasis prädiktiver Modelle

Bild von mcmurryjulie auf Pixabay

3.1 Vorab

3.1.1 Lernziele

  • Twitterdaten via API von Twitter auslesen

3.1.2 Begleitliteratur

Lesen Sie in Hvitfeldt und Silge (2021) Kap. 1 als Vorbereitung.

3.1.3 Vorbereitung

Legen Sie sich ein Konto bei Github an. Sie werden es mehrfach in diesem Kurs benötigen (und in der Praxis der Datenanalyse). Legen Sie sich ein Konto bei Twitter an. Lesen Sie diesen Artikel zur Anmeldung bei der Twitter API1

3.1.4 Benötigte R-Pakete

R-Paket {rtweet}

Einen Überblick über die Funktionen des Pakets (function reference) findet sich hier.

3.2 Anmelden bei Twitter

3.2.1 Welche Accounts interessieren uns?

Hier ist eine (subjektive) Auswahl von deutschen Politikern2, die einen Startpunkt gibt zur Analyse von Art und Ausmaß von Hate Speech gerichtet an deutsche Politiker:innen.

d_path <- "data/twitter-german-politicians.csv"

politicians <- import(d_path)
politicians

3.2.2 Twitter App erstellen

Tutorial

Auf der Twitter Developer Seite können Sie sich ein Konto erstellen und dann anmelden.

3.2.3 Intro

Die Seite von rtweet gibt eine gute Starthilfe in die Benutzung des Pakets.

3.2.4 Zugangsdaten

Zugangsdaten sollte man geschützt speichern, also z.B. nicht in einem geteilten Ordner.

source("/Users/sebastiansaueruser/credentials/hate-speech-analysis-v01-twitter.R")

Anmelden:

auth <- rtweet_bot(api_key = API_Key,
                   api_secret = API_Key_Secret,
                   access_token = Access_Token,
                   access_secret = Access_Token_Secret)

Alternativ kann man sich auch als App anmelden, damit kann man z.B. nicht posten, aber dafür mehr herunterladen Quelle.

auth <- rtweet_app(bearer_token = Bearer_Token)

Jetzt haben wir ein Anmeldeobjekt, das wir für die weiteren Anfragen dieser Session nutzen können. Das sagen wir jetzt der Twitter-API:

auth_as(auth)

Infos über Ihre aktuellen Raten kann man sich mittels rate_limit() ausgeben lassen.

3.2.5 Schützen Sie Ihre Zugangsdaten

Achtung, Sicherheitshinweis … Passwörter und andere sensitive (Anmelde-)Informationen muss man schützen, das weiß jeder. Konkret bedeutet es, dass Sie diese Daten nicht in einem öffentlichen oder geteilten Repo herumliegen lassen. Achten Sie auch darauf, dass, wenn Sie diese Information sourceen, so wie ich gerade, diese dann ungeschützt in Ihrem RStudio Environment Fenster zu sehen sind. Falls Sie also den Bildschirm teilen, oder Ihnen jemand über die Schulter schaut, sind Ihre Zugangsdaten nicht geschützt.

Ein ähnlicher Fehler wäre, die History-Dateien von R in ein öffentliches Repo einzustellen (z.B. via Git). In der Datei .gitignore sollten daher folgende Dateien aufgeführt sein:

.Rhistory
.Rapp.history

Ein Rat von rtweet dazu:

It’s good practice to only provide secrets interactively, because that makes it harder to accidentally share them in either your .Rhistory or an .R file.

Einen alternativen, sichereren Zugang bietet z.B. das Paket keyring. Dieses Paket bietet eine Anbindung zur Schlüsselbundverwaltung Ihres Betriebssystems:

Platform independent API to access the operating systems credential store.

Im MacOS wird die zentrale Schlüsselbundverwaltung genutzt, in Windows und Linux die analoge Vorrichtungen.

Wir erstellen uns einen Schlüsselbund:

keyring_create(keyring = "hate-speech-twitter")

Dann können wir einen Eintrag im Schlüsselbund erstellen. Es öffnet sich eine Maske, die nach einem Passwort fragt. Geben Sie dort die sensitiven Informationen ein, etwa die Client-ID. Ggf. werden Sie noch nach dem Passwort des Schlüsselbunds an sich gefragt3

key_set(service = "client_id",
        keyring = "hate-speech-twitter")
key_set(service = "client_secret",
        keyring = "hate-speech-twitter")
key_set(service = "bearer_token",
        keyring = "hate-speech-twitter")

Künftig können wir dann die Passwörter aus dem Schlüsselbund abrufen:

key_get(service = "client_id",
        keyring = "hate-speech-twitter")
key_get(service = "bearer_token",
        keyring = "hate-speech-twitter")

3.3 Tweets einlesen

Zu beachten ist, dass es Limits gibt, wie viele Informationen (pro Zeiteinheit) man über die Twitter-API auslesen darf. Informationen dazu findet man z.B. hier oder auch mit rate_limit().

Ein gängiges Limit der Twitter-API sind 900 Anfragen (z.B. Tweets auslesen) pro 15 Minuten.

3.3.1 Timeline einlesen einzelner Accounts

Mal ein paar Tweets zur Probe:

sesa_test <- get_timeline(user = "sauer_sebastian", n = 3) %>% 
  select(full_text)
## RT @pia_lamberty: Ein Ansatz, der sich beim Debunking wissenschaftlich als erfolgreich herausgestellt hat, ist das sog. Faktensandwich: htt…
## RT @ianbremmer: sure, it’s the hottest summer europe has ever had in history 
## 
## but look at the upside
## 
## it’s one of the coolest summers euro…
## RT @twisteddoodles: Balanced news reporting https://t.co/O1iiItEQrs
tweets <- get_timeline(user = politicians$screenname)
saveRDS(tweets, file = "data/tweets/tweets01.rds")

Michael Kearney rät uns:

PRO TIP #4: (for developer accounts only) Use bearer_token() to increase rate limit to 45,000 per fifteen minutes.

3.3.2 Retweets einlesen

tweets01_retweets <- 
  tweets$id_str %>% 
  head(3) %>% 
  map_dfr( ~ get_retweets(status_id = .x, retryonratelimit = TRUE))

Da die meisten Retweets aber nix sagen, sondern nur auf das einen Tweet wiederholen, ist das Sammeln der Retweets ziemlich langweilig.

Möchte man retry on rate limit im Standard auf TRUE setzen, so kann man das über die Optionen von R tun.

options(rtweet.retryonratelimit = TRUE)

3.3.3 EPINetz Twitter Politicians 2021

König u. a. (2022) Volltext hier haben einen Datensatz mit knapp 2500 Twitter Accounts deutscher Politiker zusammengestellt, zum Stand 2021.

Der Datensatz kann über Gesis bezogen werden.

Auf der gleichen Seite findet sich auch eine Dokumentation des Vorgehens.

Nachdem wir den Datensatz heruntergeladen haben, können wir ihn einlesen:

politicians_path <- "data/tweets/EPINetz_TwitterPoliticians_2021.RDs"
politicians_twitter <- read_rds(politicians_path)

head(politicians_twitter)

Dann lesen wir die Timelines (die Tweets) dieser Konten aus; in diesem Beispiel nur 10 Tweets pro Account:

epi_tweets <- get_timeline(user = head(politicians_twitter$twitter_name), n = 10)
head(epi_tweets)

Natürlich könnte man auch mehr als 10 Tweets pro Konto einsammeln, braucht nur seine Zeit.

3.3.4 Followers suchen

followers01 <-
  politicians$screenname %>% 
 map_dfr( ~ get_followers(user = .x, retryonratelimit = TRUE))

Da es dauern kann, Daten auszulesen (wir dürfen pro 15 Min. nur eine begrenzte Zahl an Information abrufen), kann es Sinn machen, die Daten lokal zu speichern.

saveRDS(followers01, file = "data/tweets/followers01.rds")

Und ggf. wieder importieren:

followers01 <- read_rds(file = "data/tweets/followers01.rds")

Wie viele unique Followers haben wir identifiziert?

followers02 <- 
  followers01 %>% 
  distinct(from_id)

Die Screennames wären noch nützlich:

lookup_users(users = "1690868335")

Die Anzahl der Users, die man nachschauen kann, ist begrenzt auf 180 pro 15 Minuten.

followers03 <-
  followers02 %>% 
  mutate(screenname = 
           list(lookup_users(users = from_id, retryonratelimit = TRUE,verbose = TRUE)))

Entsprechend kann man wieder einlesen:

Damit haben wir eine Liste an Followers, deren Tweets wir einlesen und analysieren können, z.B. nach Hate Speech.

Im Gegensatz zu Followers heißen bei Twitter die Accounts, denen ei Nutzi folgt “Friends”.

Lesen wir mal die Followers von karl_lauterbach ein:

karl_followers <- get_followers(user = "karl_lauterbach", verbose = TRUE)

Um nicht jedes Mal aufs Neue die Daten herunterzuladen, bietet es sich an, die Daten lokal zu speichern:

write_rds(karl_followers, file = "data/tweets/karl_followers.rds",
          compress = "gz")

Entsprechend kann man die Daten dann auch wieder einlesen:

karl_followers <- read_rds(file = "data/tweets/karl_followers.rds")

3.3.5 Follower Tweets einlesen

followers_tweets <- get_timeline(user = head(followers01$from_id), n = 10)

3.3.6 Tweets nach Stichwort suchen

Um nach einem Stichwort, allgemeiner nach einem bestimmten Text, in einem Tweet zu suchen, kann man die Funktion search_tweets nutzen:

my_tweet <- search_tweets("Sebastian Sauer", n = 1)
my_tweet$full_text

Schaut man sich das zurückgelieferte Objekt (einen Tibble) näher an, entdeckt man eine Fülle an Informationen. Satte 43 Spalten (teilweise Listenspalten) finden sich dort:

names(my_tweet)
##  [1] "created_at"                    "id"                           
##  [3] "id_str"                        "full_text"                    
##  [5] "truncated"                     "display_text_range"           
##  [7] "entities"                      "metadata"                     
##  [9] "source"                        "in_reply_to_status_id"        
## [11] "in_reply_to_status_id_str"     "in_reply_to_user_id"          
## [13] "in_reply_to_user_id_str"       "in_reply_to_screen_name"      
## [15] "geo"                           "coordinates"                  
## [17] "place"                         "contributors"                 
## [19] "retweeted_status"              "is_quote_status"              
## [21] "quoted_status_id"              "quoted_status_id_str"         
## [23] "retweet_count"                 "favorite_count"               
## [25] "favorited"                     "retweeted"                    
## [27] "lang"                          "possibly_sensitive"           
## [29] "quoted_status"                 "text"                         
## [31] "favorited_by"                  "scopes"                       
## [33] "display_text_width"            "quoted_status_permalink"      
## [35] "quote_count"                   "timestamp_ms"                 
## [37] "reply_count"                   "filter_level"                 
## [39] "query"                         "withheld_scope"               
## [41] "withheld_copyright"            "withheld_in_countries"        
## [43] "possibly_sensitive_appealable"

Die Tweet-ID dieses Tweets bekommen Sie, wenn Sie die Variable id_str auslesen:

my_tweet$id_str
## [1] "1593598440675500032"
my_tweet$source
## [1] "<a href=\"https://mobile.twitter.com\" rel=\"nofollow\">Twitter Web App</a>"

Dabei ist source nicht etwa die Person, die tweetet, wie man vielleicht meinen könnte, sondern das Frontend, das dabei verwendet wurde, also z.B. die iphone-App oder die Twitter-Webseite.

Leider sucht man den screenname zu einen Tweet vergeblich in my_tweet.

Gegeben eines Dataframes mit Tweets kann man sich aber wie folgt den Nutzernamen (screen_name) ausgeben lassen.

users_data(my_tweet)

Außerdem gibt es einen “Trick” laut dieser Quelle, vgl. auch diesen SO-Post: Gibt man in die URL eines Tweets einen beliebigen Nutzernamen - das kann ein Fantasiename sein - so wird man automatisch zum richtigen Nutzer geleitet.

Die Rohform der URL sieht also so aus:

https://twitter.com/irgendeinnutzer/status/<id_str>

Geben Sie also z.B. Folgende URL in Ihren Browser sein:

https://twitter.com/irgendeinnutzer/status/1593598440675500032

Und Sie werden zum Nutzer sauer_sebastian weitergeleitet bzw. zu seinem Tweet mit obiger ID.

3.3.7 Der Volltext ist manchmal abgeschniten

Manchmal ist der Volltext abgeschnitten

my_tweet$full_text
Hier steht der Beginn des Tweet-Textes, aber dann endet der Text abrup..."

Glücklicherweise - sofern man bei einer so umständlichen Darstellung von Glück reden kann - findet man den kompletten Text andernorts Quelle.

Dazu schreibt in diesem SO-Post der Nutzer Jonas:

You will need to check if the tweet is a retweet. If it is, use the retweet’s full_text. If it is not, use the tweet’s full_text. – Jonas, Nov 13, 2017 at 15:00

my_tweet$retweeted_status[[1]][["full_text"]]
Hier steht der Beginn des Tweet-Textes, aber dann endet der Text abrupt? 
Nein,er geht weiter und irgendwann ist der dann wirklich aus."

3.3.8 Tweets nach ID suchen

Mit lookup_tweets(id_des_tweets) können Sie sich die Informationen zu einen Tweet ausgeben lassen. Das ist natürlich primär der Volltext:

tweet_example <- lookup_tweets("1593598440675500032")
tweet_example$full_text

Aber auch die übrigen Informationen können interessant sein.

3.4 Tweets verarbeiten

3.4.1 Grundlegende Verarbeitung

Sind die Tweets eingelesen, kann man z.B. eine Sentimentanalyse, s. sec-sentimentanalyse, durchführen, oder schlicht vergleichen, welche Personen welche Wörter häufig verwenden, s. sec-woerterzaehlen.

3.4.2 Bot or not?

Eine interessante Methode, Tweets zu verarbeiten, bietet das R-Paket tweetbotornot von M. Kearney.

Aus der Readme:

Due to Twitter’s REST API rate limits, users are limited to only 180 estimates per every 15 minutes. To maximize the number of estimates per 15 minutes (at the cost of being less accurate), use the fast = TRUE argument. This method uses only users-level data, which increases the maximum number of estimates per 15 minutes to 90,000! Due to losses in accuracy, this method should be used with caution!

users <- c("sauer_sebastian")
bot01 <-
  tweetbotornot(users)
Wichtig

Ich habe ein Fehlermeldung bekommen bei tweetbotornot. Da könnte ein technisches Problem in der Funktion vorliegen.

3.5 Cron Jobs

3.5.1 Was ist ein Cron Job?

Cron ist ein Programm auf Unix-artigen Betriebssystemen, das Skripte zu eingestellten Zeiten (wiederholt) ausführt, das sind dann “Cron Jobs”. Auf Windows gibt es aber analoge Funktionen. Cron Jobs sind praktisch, da man nicht jedes Mal selber z.B. Tweets, die heute zu einem Thema getweetet wurden, herunterladen muss. Das wird dann vom Cron Job übernommen.

In R gibt es eine API zum Programm Cron mit dem Paket cronR, s. Anleitung hier.

Das analoge R-Paket für Windows heißt {taskscheduleR}.

3.5.2 Beispiel für einen Cron Job

library(cronR)

scrape_script <- cron_rscript("scrape_tweets.R")

# Cron Job hinzufügen:
cron_add(command = scrape_script, 
         frequency = 'daily', 
         at = "10AM",
         id = 'Hate Speech')  # Name des Cron Jobs

cron_clear(ask = FALSE)  # Alle Cron Jobs löschen
cron_ls()  # Liste aller Cron Jobs

Im obigen Beispiel wird das R-Skript scrape_tweets.R täglich um 10h ausgeführt.

Der Inhalt von scrape_tweets.R könnte dann, in Grundzügen, so aussehen:

library(tidyverse)
library(lubridate)
library(rtweet)
followers_lauterbach <-
  followers01 %>% 
  filter(to_id == "Karl_Lauterbach")

followers_lauterbach_tweets <- 
  get_timeline(user = followers_lauterbach$from_id[1:10], n = 10, retryonratelimit = TRUE, verbose = FALSE)


path_output <- "/Users/sebastiansaueruser/Google Drive/RRRing/Scrape-Tweets/tweets/"

write_csv(x = followers_lauterbach_tweets,
          file = paste0(path_output, "followers_lauterbach_tweets.csv"),
          append = TRUE)

Wir schreiben nicht jedes Mal (jeden Tag) eine neue CSV-Datei, sondern wir hängen hier die neu ausgelesenen Daten an die Datei an.

Leider ist es mit rtweet nicht möglich, ein Datum anzugeben, ab dem man Tweets auslesen möchte4

3.6 Datenbank an Tweets aufbauen

3.6.1 Stamm an bisherigen Tweets

In diesem Abschnitt kümmern wir uns in größerem Detail um das Aufbauen einer Tweets-Datenbank.

Diese Pakete benötigen wir:

library(rtweet)
library(tidyverse)
library(rio)  # R Data import/export

Dann melden wir uns an:

source("/Users/sebastiansaueruser/credentials/hate-speech-analysis-v01-twitter.R")
auth <- rtweet_app(bearer_token = Bearer_Token)

Dann brauchen wir eine Liste an Twitterkonten, die uns interessieren. Im Kontext von Hate Speech soll uns hier interessieren, welche Tweets an deutsche Spitzenpolitikis5 gesendet werden. Wir suchen also nach Tweets mit dem Text @karl_lauterbach, um ein Beispiel für einen Spitzenpolitiker zu nennen, der vermutlich von Hate Speech in höherem Maße betroffen ist.

politicians_twitter_path <- "/Users/sebastiansaueruser/github-repos/datascience-text/data/twitter-german-politicians.csv"

politicians_twitter <- rio::import(file = politicians_twitter_path)

In der Liste befinden sich 13 Politiker. Es macht die Sache vielleicht einfacher, wenn wir die Rate nicht überziehen. Bleiben wir daher bei 1000 Tweets pro Politiki:

n_tweets_per_politician <- 1e3

Die R-Syntax, die die Arbeit leistet, ist in Funktionen ausgelagert, der Übersichtlichkeit halber.

source("funs/filter_recent_tweets.R")
source("funs/download_recent_tweets.R")
source("funs/add_tweets_to_tweet_db.R")
source("funs/sanitize_tweets.R")

Jetzt laden wir einfach die aktuellsten 1000 Tweets pro Konto herunter, daher brauchen wir keine Tweet-ID angeben, die ein Mindest- oder Maximum-Datum (bzw. ID) für einen Tweet angibt:

tweets_older <-
  download_recent_tweets(screenname = politicians_twitter$screenname,
                         max_or_since_id_str = NULL,
                         n = n_tweets_per_politician,
                         strip_columns = TRUE,
                         reverse = TRUE)

Wie weit in die Vergangenheit reicht unsere Tweet-Sammlung?

oldest_tweets <- filter_recent_tweets(tweets_older, max_or_min_id_str = is_min_id_str)
oldest_tweets

Was sind die neuesten Tweets, die wir habven?

most_recent_tweets <- filter_recent_tweets(oldest_tweets)
most_recent_tweets

Jetzt laden wir die neueren Tweets herunter, also mit einer ID größer als die größte in unserer Sammlung:

tweets_new <- 
  download_recent_tweets(screenname = most_recent_tweets$screenname,
                         max_or_since_id_str = most_recent_tweets$id_str)

tweets_new %>% 
  select(screenname, created_at, id_str) %>% 
  head()

Jetzt - und jedes Mal, wenn wir Tweets herunterladen - fügen wir diese einer Datenbank (oder zumindest einer “Gesamt-Tabelle”) hinzu:

tweets_db <- add_tweets_to_tweets_db(tweets_new, tweets_older)

nrow(tweets_db)
## [1] 10969

Schließlich sollten wir nicht vergessen diese in einer Datei zu speichern:

write_rds(tweets_db, file = "~/datasets/Twitter/tweets-db-2022-11-11.rds")

… … So, einige Zeit ist vergangen. Laden wir noch ältere Tweets herunter und fügen Sie unserer Datenbank hinzu:

tweets_older2 <-
  download_recent_tweets(screenname = politicians_twitter$screenname,
                         max_or_since_id_str = oldest_tweets$id_str,
                         n = 1e3,
                         strip_columns = TRUE,
                         reverse = TRUE)
tweets_db <- add_tweets_to_tweets_db(tweets_new, tweets_older2)

nrow(tweets_db)
## [1] 10011

Und wieder speichern wir die vergrößerte Datenbasis auf der Festplatte:

write_rds(tweets_db, file = "~/datasets/Twitter/hate-speech-twitter.rds")

Leider ist die Datenbasis nicht mehr deutlich gewachsen. Eine plausible Ursache ist, dass Twitter den Zugriff auf alte Tweets einschränkt.

Aus der Hilfe von search_tweets:

Returns Twitter statuses matching a user provided search query. ONLY RETURNS DATA FROM THE PAST 6-9 DAYS.

Mit Hilfe des Academic Research Access sind deutlich höhere Raten möglich.

3.6.2 Neue Tweets per Cron Job

Wie oben schon ausprobiert, legen wir uns einen Cron Job an.

Das ist übrigens auch eine komfortable Lösung.

library(cronR)

scrape_script <- cron_rscript("/Users/sebastiansaueruser/github-repos/datascience-text/funs/get_tweets_politicians.R")

# Cron Job hinzufügen:
cron_add(command = scrape_script, 
         frequency = 'daily', 
         at = "10AM",
         id = 'Hate Speech')  # Name des Cron Jobs

Das Skript get_tweets_politicians.R birgt die Schritte, die wir in diesem Abschnitt ausprobiert haben, hier liegt es. Kurz gesagt sucht es nach neuen Tweets, die also noch nicht in Ihrer “Datenbank” vorhanden sind, und lädt diese herunter. Dabei werden maximal 1000 Tweets pro Konto (derer sind es 13) heruntergeladen.

Bei einem Cronjob sollten absolute Pfade angegeben werden, da der Cronjob nicht aus dem aktuellen Projekt-Repo startet.

Die Ergebnisse eines Cronjob-Durchlaufs werden in einer Log-Datei abgelegt, und zwar in dem Ordner, in dem auch das Skript liegt, das im Rahmen des Cronjobs durchgeführt wird.

Hinweis

Schauen Sie sich die Funktionen im Ordner /funs einmal in Ruhe an. Hier geht es zu dem Ordner im Github-Repo. Es ist alles keine Zauberei, aber im Detail gibt es immer wieder Schwierigkeiten. Am meisten lernt man, wenn man selber Hand anlegt.

Möchte man den Cron Job wieder löschen, so kann man das so tun:

cron_clear(ask = FALSE)  # Alle Cron Jobs löschen
cron_ls()  # Liste aller Cron Jobs

Um die Tweets “händisch” herunterzuladen, kann man get_tweets_politicians() aufrufen:

source("funs/get_tweets_politicians.R")
get_tweets_politicians()

3.6.3 Tweets in Excel exportieren

Um prädiktive Modelle zu erstellen, braucht man ein Trainingsset, Tweets also, die schon vorklassifiziert sind, z.B. im Hinblick auf Hassrede mit ja oder nein. Technisch bietet sich ein 1 vs. 0 an.

Dazu laden wir einen Datensatz mit Tweets, z.B. diesen hier:

tweets_to_kl <- import("/Users/sebastiansaueruser/datasets/Twitter/tweets_to_karl_lauterbach.rds")

Da es viele Spalten gibt, die teilweise Listenspalten sind, also komplex, begrenzen wir uns auf das Wesentliche, den Tweet-Text und die ID des Tweets.

tweets_to_kl2 <-
  tweets_to_kl %>% 
  select(id_str, full_text) 
Hinweis

Die Tweet-ID wird einmal als String und einmal als Integer gespeichert. Allerdings übersteigt die Anzahl der Ziffern die Speichergröße von (normalen) Integer-Formaten in R. Daher ist die Twitter-ID als Integer nicht zuverlässig; als Text hingegen schon.

Und schließlich können wir die Excel-Datei importieren.

export(tweets_to_kl2, file = "~/datasets/Twitter/tweets_to_kl.xlsx")

Die Excel-Tabelle können wir dann bequem hernehmen, um Tweets manuell zu klassifizieren.

3.6.4 Twitterkonten für Wissenschaftler

Twitter stellt spezielle Konten für Wissentschaftlis bereit, die über höhere Raten und mehr Funktionen verfügen, also mehr Tweets herunterladen können, z.B. 10 Millionen Tweets pro Monat pro Projekt.

auth_academic <- rtweet_app(bearer_token = askpass::askpass("bearer token"))
auth_academic

Das R-Paket askpass stellt eine weitere Möglichkeit bereit, um Zugangsdaten zu schützen. Es öffnet eine Maske, die interaktiv und als Punkte geschützte Buchstaben nach einem Passwort fragt, in diesem Fall nach dem Bearer-Token.

Aus der Hilfe:

Prompt the user for a password to authenticate or read a protected key. By default, this function automatically uses the most appropriate method based on the user platform and front-end. Users or IDEs can override this and set a custom password entry function via the askpass option.

Twitter bietet in diesem Repo einen nützlichen Kurs an, um sich mit der API vertraut zu machen.

tweets_to__FriedrichMerz_2022 <-
  get_all_tweets(query = "to:_FriedrichMerz -is:retweet",
                 start_tweets = "2022-01-01T00:00:00Z",
                 end_tweets = "2022-11-23T23:59:59Z",
                 bearer_token = askpass("Bearer token"),
                 file = "~/datasets/Twitter/tweets-to-_FriedrichMerz_2022.rds",
                 n = 1e5)

Oder als Funktion, das ist praktischer, wenn man die Syntax mehrfach verwendet:

get_all_tweets_politicians <- function(screenname, bearer_token, n = 1e5) {
  get_all_tweets(query = paste0("to:", screenname, " -is:retweet"),
                 start_tweets = "2021-01-01T00:00:00Z",
                 end_tweets = "2021-12-31T23:59:59Z",
                 bearer_token = bearer_token,
                 file = glue::glue("~/datasets/Twitter/tweets_to_{screenname}_2021.rds"),
                 data_path = glue::glue("~/datasets/Twitter/{screenname}"),
                 n = n)
}
#debug(get_all_tweets_politicians)
get_all_tweets_politicians(screenname = politicians$screenname[5],
                           bearer_token = askpass("Bearer token"),
                           n = 1e05)

Dann kann man die Objekte abespeichern, etwas als RDS-Datei oder als Feather-Datei.

Den Datensatz politicians hatten wir oben angelegt, s. sec-politicians-accounts. Er beinhaltet die Kontonamen (screennames) einiger deutscher Politikis.

Wichtig ist, mit den Lizenzregeln in Einklang zu bleiben.

Zentral ist dabei sicherlich die Frage, ob und wie man Tweets weitergeben darf. Dazu:

Academic researchers are permitted to distribute an unlimited number of Tweet IDs and/or User IDs if they are doing so for the purposes of non-commercial research and to validate or replicate previous academic studies. You should not share the entire Tweet text directly. Instead, you can build a list of Tweet IDs and share those. The researchers who you share this set of Tweet IDs with, can then use the Twitter API to hydrate and get the full Tweet objects from the Tweet IDs.

Quelle

Mehr Details finden sich den Entwicklerrichtlinien von Twitter.

Twitter stellt eine Reihe von Lehrmaterialien für die wissenschaftliche Nutzung von Tweets bereit.

3.7 Aufgaben

  1. Überlegen Sie, wie Sie das Ausmaß an Hate Speech, dem deutsche Politikerinnen und Politiker konfrontiert sind, messen können.
  2. Argumentieren Sie die Vorteile und Nachteile Ihres Ansatzes. Außerdem, auf welches Ergebnis dieser Analyse sie gespannt sind bzw. wären.
  3. Überlegen Sie Korrelate, oder besser noch: (mögliche) Ursachen, des Hasses in den Tweets, gerichtet auf Polikter:innen. Sie können auch Gruppen von Ursachen bilden, etwas personengebundene Variablen der Politiker:innen (z.B. Alter? Geschlecht? Migrationshintergrund?).
  4. Erstellen Sie sich eine Liste an Personen, deren Tweets sich lohnen (könnten), auf Hate Speech hin analysiert zu werden. Laden Sie deren Tweets (ggf. in Auszügen) herunter.
  5. Das Skript zu scrape_tweets.R könnte man noch verbessern, in dem man jeden Tag nur die neuesten Tweets herunterlädt. Dazu kann man bei get_timeline() mit dem Argument since_id eine Untergrenze der ID festlegen, so dass nur neuere Tweets (d.h. mit größerem Wert bei ID) ausgelesen werden. Ändern Sie das Skript entsprechend, so dass nur neuerer Tweets gelesen werden.
  6. Erarbeiten Sie die Folien zu diesem rtweet-Workshop. Eine Menge guter Tipps!
Hvitfeldt, Emil, und Julia Silge. 2021. Supervised Machine Learning for Text Analysis in R. 1. Aufl. Boca Raton: Chapman and Hall/CRC. https://doi.org/10.1201/9781003093459.
König, Tim, Wolf J. Schünemann, Alexander Brand, Julian Freyberg, und Michael Gertz. 2022. „The EPINetz Twitter Politicians Dataset 2021. A New Resource for the Study of the German Twittersphere and Its Application for the 2021 Federal Elections“. Politische Vierteljahresschrift 63 (3): 529–47. https://doi.org/10.1007/s11615-022-00405-7.