見出し画像

【R/Shiny】「ツイート収集webアプリ」を作る2 ~timeline収集機能追加~

こちらの続きです。

●やること

本記事では過去記事で作ったツイートを収集するwebアプリに特定のアカウントのツイートを収集する機能を追加します。

For English speakers.
In this article, I'm going to add a function to collect tweets from a specified account to the web app that collects tweets created in the previous article.
The program code is in English only, but the text is in Japanese, so please use a translation site to read it.
If you want to contact me, you can DM me on Twitter in English.

●前書き

こんなのを作ります。

前回記事で作成した部分に加えて、get_timelineタブを作り、指定されたアカウントの3200ツイートを収集できる機能を追加します。
自分のツイートでどんなツイートが人気があったのか?
競合はどんなツイートをしているのか?
ウォッチング対象の過去ツイート保存やツイートを消していないか監視
ストーキング
に便利です。
もちろん検索された側にはインプレッションが1増える以外にバレないのでやりたい放題。

●作り方

基本的な作り方は前回記事をご参考ください。
すでに前回記事で基本形ができているのならば、今回やることはRstudioでコードを書き換えるだけです。
コードを以下のように書き換えます。全コードですのでこのままRstudioのapp.Rに貼り付けてください。
過去記事と同様、 app = "YOUR APP NAME"のところはTwitter API keyで設定したアプリの名前に書き換えてください。

# tweet_collection

# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
#    http://shiny.rstudio.com/
#

# cache clear
rm(list=ls())
gc();  gc();

library("shiny")
library("shinymanager")
library("shinythemes")
library("shinybusy")
library("rtweet")
library("tidyverse")
library("DT")
library("lubridate")
library("jsonlite")

# set credentials
credentials <- data.frame(
  user = c("shiny123", "user1234"), # 
  password = c("azerty123", "pass1234"), # 
  start = c("2019-04-15"), # optinal (all others)
  expire = c(NA, NA),
  admin = c(TRUE, TRUE),
  comment = "",
  stringsAsFactors = FALSE
)

# read API key from json
keys <- read_json("config.json", simplifyVector = TRUE)
twitter_token <- create_token(
  app = "YOUR APP NAME", # ← Enter your app name 
  consumer_key = keys$consumer_key,
  consumer_secret = keys$consumer_secret,
  access_token = keys$access_token,
  access_secret = keys$access_secret
)

# Define UI for app
ui <- navbarPage(
  theme = shinytheme("spacelab"),

  # Application title
  br(),
  tabPanel(
    title = "search_tweets",
    h2("Search tweets"),
    "You can only download a total of ",
    span("18,000 tweets per 15 minutes and tweets within the last seven days. ", style = "color:blue"),
    "If it does not work, please try again later.",

    # Sidebar
    sidebarLayout(
      sidebarPanel(
        width = 2,
        numericInput("num_tweets_to_download",
          label = "Number of tweets to download",
          min = 100,
          max = 18000,
          value = 100,
          step = 100
        ),
        textInput("word_to_search",
          label = "word to search",
          value = "#cat"
        ),
        actionButton("get_data", "Get data", class = "btn-primary"),
        br(), br(),
        "Download",
        br(),
        downloadButton("download_data", ".csv")
      ),

      # Show results
      mainPanel(
        width = 10,
        DT::dataTableOutput("tweet_table")
      )
    )
  ),
  tabPanel(
    title = "get_timeline",
    h2("Get user timeline"),
    "You can get ",
    span("3,200 tweets from a user's timeline. Up to 18,000 per hour. ", style = "color:blue"),
    "If it does not work, please try again later.",

    # Sidebar
    sidebarLayout(
      sidebarPanel(
        width = 2,
        numericInput("num_tweets_to_download2",
          label = "Number of tweets to download",
          min = 100,
          max = 3200,
          value = 100,
          step = 100
        ),
        textInput("user_name",
          label = "user ID to search",
          value = "@DLsite"
        ),
        actionButton("get_data2", "Get data", class = "btn-primary"),
        br(), br(),
        "Download",
        br(),
        downloadButton("download_data2", ".csv")
      ),

      # Show user timeline results
      mainPanel(
        width = 10,
        DT::dataTableOutput("ut_table")
      )
    )
  )
)


# Wrap your UI with secure_app
ui <- secure_app(ui)

# Define server logic
server <- function(input, output, session) {
  # check_credentials returns a function to authenticate users
  res_auth <- secure_server(
    check_credentials = check_credentials(credentials)
  )

  # tweet collection
  tweet_df <- eventReactive(
    input$get_data,
    {
      show_modal_spinner()
      x <- search_tweets(input$word_to_search, n = input$num_tweets_to_download, include_rts = FALSE)
      x
    }
  )

  # tweet_data cleansing
  tweet_table_data <- reactive({
    req(tweet_df())
    x2 <- tweet_df() %>%
      mutate(created_at = ymd_hms(created_at) + 32400) %>% # UST to JST
      select(
        created_at,
        screen_name, text, favorite_count, retweet_count,
        status_url, description, followers_count, friends_count,
        source
      )
    x2 <- x2 %>% mutate(
      url = status_url,
      status_url = paste0("<a href='", x2$status_url, "'>", x2$status_url, "</a>")
    )
    remove_modal_spinner()
    x2
  })

  # output
  output$tweet_table <- DT::renderDataTable(
    tweet_table_data() %>%
      select(-url),
    escape = FALSE,
    options = list(
      lengthMenu = c(10, 30, 100),
      autoWidth = TRUE,
      pageLength = 30,
      scrollY = "800px",
      scrollX = TRUE,
      scrollCollapse = TRUE
    )
  )

  # download button server logic
  output$download_data <- downloadHandler(
    filename = function() {
      paste(Sys.Date(), input$word_to_search, "_", ".csv", sep = "")
    },
    content = function(file) {
      rtweet::write_as_csv(tweet_table_data() %>%
        select(-status_url),
      file,
      na = "NA", fileEncoding = "CP932"
      )
    }
  )

  # user timeline collection
  ut_df <- eventReactive(
    input$get_data2,
    {
      show_modal_spinner()
      x <- rtweet::get_timeline(input$user_name, n = input$num_tweets_to_download2)
      x
    }
  )

  # user_timeline cleansing
  ut_table_data <- reactive({
    req(ut_df())
    x3 <- ut_df() %>%
      mutate(created_at = ymd_hms(created_at) + 32400) %>% # UST to JST
      select(
        created_at,
        screen_name, text, favorite_count, retweet_count,
        status_url, followers_count, friends_count,
        source
      )
    x3 <- x3 %>% mutate(
      url = status_url,
      status_url = paste0("<a href='", x3$status_url, "'>", x3$status_url, "</a>")
    )
    remove_modal_spinner()
    x3
  })
  # output user timeline
  output$ut_table <- DT::renderDataTable(
    ut_table_data() %>%
      select(-url),
    escape = FALSE,
    options = list(
      lengthMenu = c(10, 30, 100),
      autoWidth = TRUE,
      pageLength = 30,
      scrollY = "800px",
      scrollX = TRUE,
      scrollCollapse = TRUE
    )
  )

  # download button server logic
  output$download_data2 <- downloadHandler(
    filename = function() {
      paste(Sys.Date(), input$user_name, "_", ".csv", sep = "")
    },
    content = function(file) {
      rtweet::write_as_csv(ut_table_data() %>%
        select(-status_url),
      file,
      na = "NA", fileEncoding = "CP932"
      )
    }
  )
}

# Run the application
shinyApp(ui = ui, server = server)


# shinyapps
# tmp.enc <- options()$encoding
# options(encoding = "UTF-8")
# library(rsconnect)
# rsconnect::deployApp()
# options(encoding = tmp.enc)

上の様にコードを書き換え、前回記事と同じようにRun Appから起動、またはshinyappsにdeployしてやればOKです。

●主な変更点

・UI部分

# Define UI for app
ui <- navbarPage(
  theme = shinytheme("spacelab"),

  # Application title
  br(),
  tabPanel(
    title = "search_tweets",
    h2("Search tweets"),

...(中略)...

 tabPanel(
    title = "get_timeline",
    h2("Get user timeline"),
...

前回の記事ではui <- fluidPage( theme = …としていたところを、navbarPageとtabPanelを利用してツイート検索機能とユーザーツイート収集機能をタブで切り替えられるようにしました。
これにより


ここの部分で機能を切り替えられるようになっています。

またUI部分ではアカウントのツイート を収集するget_timeline部分で、search_tweetと同様に以下を追加しています。

  tabPanel(
    title = "get_timeline",
    h2("Get user timeline"),
    "You can get ",
    span("3,200 tweets from a user's timeline. Up to 18,000 per hour. ", style = "color:blue"),
    "If it does not work, please try again later.",

    # Sidebar
    sidebarLayout(
      sidebarPanel(
        width = 2,
        numericInput("num_tweets_to_download2",
          label = "Number of tweets to download",
          min = 100,
          max = 3200,
          value = 100,
          step = 100
        ),
        textInput("user_name",
          label = "user ID to search",
          value = "@DLsite"
        ),
        actionButton("get_data2", "Get data", class = "btn-primary"),
        br(), br(),
        "Download",
        br(),
        downloadButton("download_data2", ".csv")
      ),

      # Show user timeline results
      mainPanel(
        width = 10,
        DT::dataTableOutput("ut_table")
      )
    )
  )

基本的な構成は全くsearch_tweetと一緒ですが、rtweetのget_timeline関数の最大取得数は3200ですのでnumericInputのmaxを3200にしています。
検索ワードのtextInputはTwitter IDの@を含むID(screen_name)を引数とするので、その旨も表示してやります。

・server部分

以下を追加してやります。

  # user timeline collection
  ut_df <- eventReactive(
    input$get_data2,
    {
      show_modal_spinner()
      x <- rtweet::get_timeline(input$user_name, n = input$num_tweets_to_download2)
      x
    }
  )
 
 # user_timeline cleansing
  ut_table_data <- reactive({
    req(ut_df())
    x3 <- ut_df() %>%
      mutate(created_at = ymd_hms(created_at) + 32400) %>% # UST to JST
      select(
        created_at,
        screen_name, text, favorite_count, retweet_count,
        status_url, followers_count, friends_count,
        source
      )
    x3 <- x3 %>% mutate(
      url = status_url,
      status_url = paste0("<a href='", x3$status_url, "'>", x3$status_url, "</a>")
    )
    remove_modal_spinner()
    x3
  })
  # output user timeline
  output$ut_table <- DT::renderDataTable(
    ut_table_data() %>%
      select(-url),
    escape = FALSE,
    options = list(
      lengthMenu = c(10, 30, 100),
      autoWidth = TRUE,
      pageLength = 30,
      scrollY = "800px",
      scrollX = TRUE,
      scrollCollapse = TRUE
    )
  )

  # download button server logic
  output$download_data2 <- downloadHandler(
    filename = function() {
      paste(Sys.Date(), input$user_name, "_", ".csv", sep = "")
    },
    content = function(file) {
      rtweet::write_as_csv(ut_table_data() %>%
        select(-status_url),
      file,
      na = "NA", fileEncoding = "CP932"
      )
    }
  )

こちらも前回のsearch_tweetとほぼ中身は一緒。
関数としてユーザーのタイムラインを収集するrtweet::get_timelineを入れてやっただけです。
各場所の引数はui側で新たに設定したオブジェクトを使う様に設定してやります。

●できあがり

@nhk_newsのデータ取得例

特定のユーザーの3200ツイートを収集できます。
こちらはsearch_tweetと違って最新7日間の制限はなく、3200ツイートきっちり遡って取得することができます。
このため、自分または特定のアカウントについてツイートを取得し、
・RT数降順にソートしてどんなツイートが人気があったかを分析
・どんな話題やタグが多いのかを分析
・sourceを見てどんな端末を使っているかを分析
・いつくらいにツイートしているのかを分析
・過去に過去3200ツイートをログのように保存
・Twitterの検索ではなかなか出てこないツイートを検索
というような監視またはストーキングことに使用することができます。

●終わりに

今回はrtweetパッケージのget_timeline関数の機能を追加してみました。
このようにrtweetの関数であれば簡単に機能を追加することが可能です。
このため、アカウントのフォローを取得するget_followers、フォロワーを取得するget_friends、ツイート頻度を取得するts_plot、複数のアカウント間の交流データを取得するlookup_friendships…などなど、rtweetにはたくさんの関数があります。
これら関数、またその他パッケージと組み合わせればかなり高度なTwitter分析アプリケーションを自作することも可能です。
興味があればCRANの説明書をご覧ください。

本記事について分からない点、何かお気づきの点がございましたらTwitterのDMまでご連絡ください。
何かリクエストがあれば作ったり対応するかもしれません。

以上です。
ありがとうございました。

万が一サポート、感想、コメント、分析等のご相談などございましたらお気軽に。