Rtweet: baixando e salvando todos os tweets de um usuário

Vivemos em um mundo onde, desde sempre, informação significa poder. Possuir determinados livros, dominar certos conhecimentos, frequentar cursos ou universidades específica, tudo isso leva a um certo status na sociedade. Na obra 1984, de George Orwell, temos o clássico exemplo do que isso representa: o partido IngSoc, a partir do Ministério da Verdade, controla informações e altera a história, ao apagar notícias, livros e acontecimentos do passado.

Uma outro exemplo, agora na vida real, foi o da Biblioteca de Alexandria, no Egito Ptolemaico. Fundada no século III a.C., possuía as grandes obras da humanidade em cerca de 700 mil papiros. Com o tempo, foi sendo alvo de ataques diretos e de descuido do poder público, com diversos saques e incêndios, sobrevivendo até 390 d.C., quando o Patriarca Teófilo I de Alexandria ordenou a sua destruição, junto com diversos outros templos pagãos. É possível, em parte, relacionar a entrada da Europa na Idade das Trevas à destruição da biblioteca: sem o conhecimento, não haverá questionamento ou críticas por parte de opositores ao regime dominante.

O mesmot ambém aconteceu no Renascimento, quando Girolamo Savonarola, padre florentino, criou as chamadas “fogueiras das vaidades”, onde queimava de obras de arte a livros, afirmando serem vaidades humanas. Adolf Hitler fez o mesmo, com o “Bücherverbrennung”, ou “queima de livros”, em alemão. Toda obra que foi considerada um conteúdo em desacordo com a ideologia nazifascista, foi queimada em 1933 em praça pública. Cerca de 20 mil livros foram perdidos.

Atualmente, parece fácil apagar informações: deletar perfis de Instagram, Facebook e Twitter é fácil como (literalmente) apertar um botão. Assim, parece que provas provas podem ser completamente deletadas com pouco esforço. Todavia, não é bem assim: no caso de crimes cometidos, por exemplo, as empresas podem ser solicitadas pela justiça a entregarem os dados que, mesmo deletados pelo usuário, devem permanecer nos bancos de dados da rede responsável. É possível também utilizar o próprio cache do Google (que é temporário).

Uma outra forma de manter esses dados é fazendo o download dos mesmos com certa frequência, e salvando em um arquivo no computador, sendo esse o foco do artigo de hoje, que será dedicado ao Twitter. Assim, será possível acompanhar seu político favorito, por exemplo, e salvar absolutamente tudo que ele posta. Dessa forma, caso ele delete suas redes sociais, você possui um backup dos dados.

Para fins de testes, vamos utilizar a conta do twitter de Hadley Wickham, criador do RStudio. Utilizaremos também os pacotes tidyverse, como sempre, rio, que serve para importar/exportar dados em todos os formatos possíveis, e o rtweet, que nos permite baixar dados do Twitter através da API.

O rtweet pode ser baixado usando a função install.packages("rtweet") ou então carregado pelo pacman::p_load(), como costumo fazer. Lembrando, quando o pacote não estiver instalado, o pacman instala automaticamente e o carrega, possibilitando o uso na hora.

O primeiro passo, então, é carregar os pacotes que usaremos:

pacman::p_load(tidyverse, rtweet, rio)

O rtweet possui, dentre suas muitas funções, a get_timeline(), que baixa os tweets de uma determinada conta. Contudo, existe uma limitação: o get_timeline() só consegue baixar no máximo os 3200 tweets mais recentes. Todos os outros não podem ser baixados. Por isso, é necessário que haja um acompanhamento da conta, de tempos em tempos, para que todos os tweets passem a ser salvos. É fácil entender isso como um backup do próprio computador: não é possível salvar muito para trás, mas é possível fazer backups constantemente para sempre ter uma versão atualizada.

Para baixar os tweets da conta do Hadley Wickham, fazemos o seguinte:

wickham <- get_timeline("hadleywickham", n = 3200)

Explicando o código acima, a função baixou os tweets da conta do Hadley (para identificar, basta o usuário, sem a @!), e n = 3200 significa: baixe todos os últimos 3200 tweets. Números acima disso não funcionarão.

Obs: é importante dizer que, se é a primeira vez que você usa o rtweet, uma página se abrirá, pedindo para fazer login e autorizar a aplicação no seu Twitter. Esse passo é necessário para que os dados possam ser acessados e baixados. A autorização funcionará somente em sua conta no seu computador, mantendo seus dados do twitter em segurança.

Vamos dar uma olhada no banco:

glimpse(wickham)
## Rows: 3,196
## Columns: 90
## $ user_id                 <chr> "69133574", "69133574", "69133574", "69133574"…
## $ status_id               <chr> "1366844045029486595", "1366843610516369409", …
## $ created_at              <dttm> 2021-03-02 20:13:09, 2021-03-02 20:11:26, 202…
## $ screen_name             <chr> "hadleywickham", "hadleywickham", "hadleywickh…
## $ text                    <chr> "Watch Sharla's 💯 talk about reproducible exam…
## $ source                  <chr> "Twitter Web App", "Twitter Web App", "Twitter…
## $ display_text_width      <dbl> 116, 35, 3, 140, 57, 143, 17, 84, 121, 140, 14…
## $ reply_to_status_id      <chr> NA, "1366835687593508875", "136684052474168934…
## $ reply_to_user_id        <chr> NA, "43186378", "2440258777", NA, "45565875", …
## $ reply_to_screen_name    <chr> NA, "CMastication", "kierisi", NA, "emilmalta"…
## $ is_quote                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALS…
## $ is_retweet              <lgl> TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, …
## $ favorite_count          <int> 0, 2, 5, 0, 2, 0, 19, 234, 0, 0, 0, 0, 0, 0, 1…
## $ retweet_count           <int> 6, 0, 0, 19, 0, 23, 0, 5, 653, 30, 59, 23, 65,…
## $ quote_count             <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ reply_count             <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ hashtags                <list> [NA, NA, NA, NA, NA, "rstats", NA, "rstats", …
## $ symbols                 <list> [NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
## $ urls_url                <list> ["twitter.com/sharlagelfand/…", NA, NA, NA, N…
## $ urls_t.co               <list> ["https://t.co/D1iUlXKyTX", NA, NA, NA, NA, N…
## $ urls_expanded_url       <list> ["https://twitter.com/sharlagelfand/status/13…
## $ media_url               <list> ["http://pbs.twimg.com/media/EvcWmixU4AEgX3v.…
## $ media_t.co              <list> ["https://t.co/XLeHCJ0kIV", NA, NA, NA, NA, N…
## $ media_expanded_url      <list> ["https://twitter.com/juliasilge/status/13665…
## $ media_type              <list> ["photo", NA, NA, NA, NA, NA, "photo", "photo…
## $ ext_media_url           <list> ["http://pbs.twimg.com/media/EvcWmixU4AEgX3v.…
## $ ext_media_t.co          <list> ["https://t.co/XLeHCJ0kIV", NA, NA, NA, NA, N…
## $ ext_media_expanded_url  <list> ["https://twitter.com/juliasilge/status/13665…
## $ ext_media_type          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ mentions_user_id        <list> ["13074042", "43186378", "2440258777", "97395…
## $ mentions_screen_name    <list> ["juliasilge", "CMastication", "kierisi", "al…
## $ lang                    <chr> "en", "en", "und", "en", "en", "en", "en", "en…
## $ quoted_status_id        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_text             <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_created_at       <dttm> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ quoted_source           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_favorite_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_retweet_count    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_user_id          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_screen_name      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_name             <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_followers_count  <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_friends_count    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_statuses_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_location         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_description      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ quoted_verified         <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ retweet_status_id       <chr> "1366586155387342849", NA, NA, "13668152182616…
## $ retweet_text            <chr> "Watch Sharla's 💯 talk about reproducible exam…
## $ retweet_created_at      <dttm> 2021-03-02 03:08:24, NA, NA, 2021-03-02 18:18…
## $ retweet_source          <chr> "Twitter Web App", NA, NA, "Twitter Web App", …
## $ retweet_favorite_count  <int> 66, NA, NA, 99, NA, 96, NA, NA, 4680, 113, 148…
## $ retweet_retweet_count   <int> 6, NA, NA, 19, NA, 23, NA, NA, 653, 30, 59, 23…
## $ retweet_user_id         <chr> "13074042", NA, NA, "973955318987210752", NA, …
## $ retweet_screen_name     <chr> "juliasilge", NA, NA, "allison_horst", NA, "ju…
## $ retweet_name            <chr> "Julia Silge", NA, NA, "Allison Horst", NA, "J…
## $ retweet_followers_count <int> 37825, NA, NA, 18580, NA, 991, NA, NA, 2841, 2…
## $ retweet_friends_count   <int> 705, NA, NA, 2773, NA, 430, NA, NA, 745, 1275,…
## $ retweet_statuses_count  <int> 22380, NA, NA, 3026, NA, 1546, NA, NA, 27524, …
## $ retweet_location        <chr> "Salt Lake City, UT", NA, NA, "Chumash & Paiut…
## $ retweet_description     <chr> "Data science and modeling at @rstudio, #rstat…
## $ retweet_verified        <lgl> FALSE, NA, NA, FALSE, NA, FALSE, NA, NA, FALSE…
## $ place_url               <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ place_name              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ place_full_name         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ place_type              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ country                 <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ country_code            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ geo_coords              <list> [<NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA,…
## $ coords_coords           <list> [<NA, NA>, <NA, NA>, <NA, NA>, <NA, NA>, <NA,…
## $ bbox_coords             <list> [<NA, NA, NA, NA, NA, NA, NA, NA>, <NA, NA, N…
## $ status_url              <chr> "https://twitter.com/hadleywickham/status/1366…
## $ name                    <chr> "Hadley Wickham", "Hadley Wickham", "Hadley Wi…
## $ location                <chr> "Houston, TX", "Houston, TX", "Houston, TX", "…
## $ description             <chr> "R, data, visualisation, 🐕, 🍸, 🌈. He/him", "R,…
## $ url                     <chr> "https://t.co/DWqWlxbOKK", "https://t.co/DWqWl…
## $ protected               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALS…
## $ followers_count         <int> 118051, 118051, 118051, 118051, 118051, 118051…
## $ friends_count           <int> 269, 269, 269, 269, 269, 269, 269, 269, 269, 2…
## $ listed_count            <int> 2969, 2969, 2969, 2969, 2969, 2969, 2969, 2969…
## $ statuses_count          <int> 40635, 40635, 40635, 40635, 40635, 40635, 4063…
## $ favourites_count        <int> 24169, 24169, 24169, 24169, 24169, 24169, 2416…
## $ account_created_at      <dttm> 2009-08-26 23:34:46, 2009-08-26 23:34:46, 200…
## $ verified                <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE…
## $ profile_url             <chr> "https://t.co/DWqWlxbOKK", "https://t.co/DWqWl…
## $ profile_expanded_url    <chr> "http://hadley.nz", "http://hadley.nz", "http:…
## $ account_lang            <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ profile_banner_url      <chr> "https://pbs.twimg.com/profile_banners/6913357…
## $ profile_background_url  <chr> "http://abs.twimg.com/images/themes/theme15/bg…
## $ profile_image_url       <chr> "http://pbs.twimg.com/profile_images/905186381…

Temos 90 variáveis e 3198 variáveis. Às vezes, o rtweet não baixa completamente os 3200, por erros de internet, erros do próprio Twitter ou então pelo fato de não existirem. No caso do Hadley, ele possui bem mais de 3200, logo, ou foi a internet ou foi a API. De qualquer forma, apenas 4 tweets não foram baixados, o que representa apenas 0,125% do total.

Para salvar esse arquivo em outros formatos, basta executar:

export(wickham, "wickham.xlsx") # Para salvar em Excel
export(wickham, "wickham.csv") # Para  salvar em CSV
export(wickham, "wickham.sav") # Para salvar no formato do SPSS
export(wickham, "wickham.dta") # Para salvar no formato do Stata
export(wickham, "wickham.json") # Para salvar em JSON
# Dentre outros formatos...

# E para salvar em .RData, o formato padrão do R
save(wickham, "wickham.RData")

Ok, mas e agora? Se a conta que acompanho posta tweets constantemente, como juntar esses ao banco?

É preciso executar a função get_timelines(), de tempos em tempos (a depender da frequência de tweets), e depois somar ao banco original usando a função bind_rows() do * dplyr*. Lembre-se: salve em um objeto com um nome diferente do original, para que os dados não sejam sobreescritos. É importante também lembrar que a função só funciona se todas as colunas (variáveis) forem as mesmas, ou seja, não é interessante remover variáveis

wickham_update <- get_timeline("hadleywickham", n = 3200)

wickham <- bind_rows(wickham, wickham_update)

Entretanto, se a conta tem uma frequência irregular e, por isso, é importante baixar em um intervalo pequeno, muitos tweets virão repetidos, pois já foram baixados antes. Para evitar isso, utilizamos a função distinct(), também do dplyr, que filtra apenas pelos valores únicos, removendo o que for repetido. A variável que representa valores únicos de tweets no banco é a status_id, que é o número único de todo tweet. Logo, se existirem dois números iguais no status_id, se referem à tweets iguais. Adicionando, com o pipe %>% a função distinct(), conseguimos manter somente os tweets únicos:

wickham <- bind_rows(wickham, wickham_update) %>% 
  distinct(status_id, .keep_all = T) 

O argumento .keep_all = T é essencial na função: ela mantém todas as variáveis do banco. Sem ela, todas as variáveis seriam removidas, menos status_id.

No final, o código completo que deve ser executado fica bem simples, da seguinte forma:

# Para baixar os tweets pela primeira vez
wickham <- get_timeline("hadleywickham", n = 3200)

# Para atualizar esse banco
wickham_update <- get_timeline("hadleywickham", n = 3200)

# Juntando o novo e o antigo, selecionando os tweets únicos e exportando para Excel
wickham <- bind_rows(wickham, wickham_update) %>% 
  distinct(status_id, .keep_all = T) 

# Salvando em Excel (ou qualquer outro formato)
export(wickham, "wickham.xlsx")

Bom, por hoje é só! Assim, você pode salvar os tweets de uma determinada conta. Mesmo que deletada, os tweets continuarão acessíveis do seu computador, e podem (e devem) ser compartilhados, no caso de pessoas públicas com responsabilidade política e social. Futuramente, utilizarei o rtweet para explorar mais os dados do Twitter, como hashtags, usuários e frequência de tweets. Qualquer dúvida, correção ou sugestão pode ser encaminhada para

Mateus Cavalcanti Pestana
Mateus Cavalcanti Pestana
Doutorando e Mestre em Ciência Política

Interessado em ciência de dados, ciência política, política russa, impressão 3D, redes neurais e aprendizado de máquina.

Relacionados