RSeleniumを使ってログインにcaptcha認証の必要なページをスクレイピングする


Project EulerのStatisticsページ(Statistics - Project Euler)から使用言語別の統計情報を得ることを目標とする。このページはログインしないと閲覧することができず、またログインには画像認証が必要である。今回、画像認証を(半手動で)突破するためにRSeleniumパッケージを用いる。

準備

Selenium Serverの起動

まず、何らかの方法でSelenium Serverを起動しておく必要がある。現在、RSeleniumのVignette(RSelenium: Basics)ではDockerコンテナの利用が推奨されており、Dockerを使用する場合のためのvignettも別途用意されている(RSelenium: Docker)。

今回は推奨の手順に従ってSeleniumを導入し、ローカルでSelenium Sereverが起動されているものとする。

ターミナル
% docker run -d -p 4445:4444 selenium/standalone-firefox

使用するパッケージ

下記のパッケージを利用する。

# install.packages("RSelenium", "rvest") # インストールしていなければインストール
library(RSelenium)
library(rvest)

rvestは今回取得したhtmlの取扱いに用いた。データの取得自体はRSeleniumから行った。

スクレイピング

Webサイトへ接続する

まず、remoteDriverクラスのインスタンスを作成する。このとき、Selenium Server起動時に指定した情報を入力する。

remDr <- remoteDriver(remoteServerAddr = "localhost",
                      port = 4445L,
                      browserName = "firefox")

Serverへの接続はopenメソッドを用いる。

remDr$open()

正常に接続できていれば、getStatus()メソッドでステータスが取得できるはずだ。

> remDr$getStatus()
$ready
[1] TRUE

(...以下略)

また、screenshot()メソッドを用いれば現在の画面のスクリーンショットを取得できる。display=TRUEを指定すればRStudioならViewerペインで内容を確認できるので、現在の状況を確認したくなったら適宜実行すると良い。また、今回captchaの確認にも使用する。

スクリーンショットをViewerペインに表示
remDr$maxWindowSize()
remDr$screenshot(display=TRUE)

ログイン情報を入力する

フォームへの情報入力はwebElementクラスのsendKeysToElementメソッドを用いる。

## usernameとpassword入力
webElem <- remDr$findElement(using="id", value = "username")
webElem$sendKeysToElement(list("your_username")) # 自身のユーザー名に置き換える
webElem <- remDr$findElement(using="id", value = "password")
webElem$sendKeysToElement(list("your_password")) # 自身のパスワードに置き換える

captcha部分は見ないと分からないので、見て入力する。screenshotを使おう。

## captcha情報確認のためスクリーンショットをとる
remDr$maxWindowSize()
remDr$screenshot(display=TRUE)

## captcha入力
webElem <- remDr$findElement(using="name", value = "captcha")
webElem$sendKeysToElement(list(readline("confirmation code: "))) # 確認した値を入力

情報を入力したらサインインしよう。

## サインインボタンクリック
webElem <- remDr$findElement(using="name", value = "sign_in")
webElem$clickElement()

データの取得

getPageSourceメソッドでhtmlを取得できるので、後はrvestを使うなり何なり。

## 目的のページへ移動
remDr$navigate("https://projecteuler.net/languages")

## rvestでテーブル取得
df <- remDr$getPageSource()[[1]] %>% read_html %>% html_table %>% `[[`(1)

おまけ

せっかくなのでplotしてみた。

library(ggplot2)

plotdf <- function(target){
  df %>%
    filter(rank(eval(target)) > nrow(df)/2) %>%
    ggplot(aes(x = reorder(Language, eval(target)), y = eval(target))) +
    geom_col() +
    coord_flip() +
    xlab("Language") +
    ylab(target) +
    theme_classic()
}

plotdf(quote(`Total Number of Members`))
plotdf(quote(`Mean Percentage of Problems Solved`))

言語別のランク。Project Eulerは速度を求められないので、Rでも十分戦えるぞ。

解答率でソートすると様相が一変して面白い。ユーザー数が10名に満たないような言語が多く入ってくる(だからこそ上位に入りやすい、という見方もできるだろうが)。トップのStataは現時点でユーザー数16名だ。

参考