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

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

## 準備

### Selenium Serverの起動

まず、何らかの方法でSelenium Serverを起動しておく必要がある。現在、RSeleniumのVignette([RSelenium: Basics](https://cran.r-project.org/web/packages/RSelenium/vignettes/RSelenium-basics.html))ではDockerコンテナの利用が推奨されており、Dockerを使用する場合のためのvignettも別途用意されている([RSelenium: Docker](https://cran.r-project.org/web/packages/RSelenium/vignettes/RSelenium-docker.html))。

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

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

### 使用するパッケージ

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

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

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

## スクレイピング

### Webサイトへ接続する

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

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

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

```r
remDr$open()
```

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

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

(...以下略)
```

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

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

### ログイン情報を入力する

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

```r
## 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`を使おう。

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

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

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

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

### データの取得

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

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

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

## おまけ

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

```r 
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でも十分戦えるぞ。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/109348/099a6867-f2dc-0078-3801-d3b9ede8d07f.png)

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/109348/52b64429-b84d-2f57-a8aa-39093b5ee697.png)


## 参考

- [rvest でログインしてスクレイピング #rstatsj - Qiita](https://qiita.com/hoxo_m/items/59bedbf7e4c7c99cbf5a)
- [[翻訳] RSelenium vignette: RSeleniumの基本 - Qiita](https://qiita.com/nakamichi/items/ad186633ae249b26d032)
- [RSelenium: Basics](https://cran.r-project.org/web/packages/RSelenium/vignettes/RSelenium-basics.html)
- [RSelenium: Docker](https://cran.r-project.org/web/packages/RSelenium/vignettes/RSelenium-docker.html)