京橋のバイオインフォマティシャンの日常

南国のビーチパラソルの下で、Rプログラムを打ってる日常を求めて、、Daily Life of Bioinformatician in Kyobashi of Osaka

R言語のRSeleniumを使って、ブラウザ(自動)操作とWebスクレイピングをやってみた件 〜Google検索でのトップヒットページ・ヒット件数・スクショの取得〜

はじめに

RでのWebスクレイピングのやり方の1つとして、rvestパッケージを使う方法がある。

詳細は、過去の記事を参照

skume.net

skume.net

ただ、このパッケージだと、Webスクレイピングがやや難解なケースがある。 実際、Google検索のヒット件数項目を取得するのを、小一時間ほどやったけど、rvestではうまくいかなかった。

この記事では、RSeleniumを使って、Webブラウザを介して、Webスクレイピングをやる方法を紹介する。RSeleniumは、Selenium*1 2.0 Remote WebDriver 用の R ラッパーである。使ってみたら、RSeleniumは、rvestがやや困難な部分を補完できそうである。また、htmlの扱い方は、rvestとの共通点もあり、rvestの経験も生かされる。

今回の内容は、基本的なRSeleniumの使い方が主で、「定期実行のプログラムを組んで、ブラウザを自動操作する」とかまでは含んでいない。

まずは、事前のセットアップを行う。

事前セットアップ

Webブラウザのダウンロード

FireFoxGoogle Chromeをダウンロード・インストールする。

Webブラウザ操作用ドライバーのインストール

ターミナルを起動して、以下を実行する。

#ドライバーのインストール
brew install geckodriver
brew install chromedriver
#実行時に許可が必要

Homebrewの詳細は、過去の記事を参照のこと。

skume.net

Javaのインストール

こういうのがでたら、javaのインストールも必要である。

ターミナルを起動して、以下を実行する。

#JAVA セットアップ
brew tap AdoptOpenJDK/openjdk
brew install --cask adoptopenjdk12
#インストールの確認
java -version

詳細は、過去の記事を参照のこと。

skume.net

selenium.jarのダウンロード・実行

RSeleniumの実行には、selenium-server-standalone が必要である。

R/RStudioを起動して、そのソフトウェアをダウンロードして、バックグラウンド実行する。

#ダウンロード実行: 2021年4月4日に、ダウンロード確認
download.file("https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar", 
              destfile = "selenium.jar")

#バックグラウンド実行
system("java -jar selenium.jar &")
#19:42:09.781 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358
#19:42:09.872 INFO [GridLauncherV3.lambda$buildLaunchers$3] - Launching a standalone Selenium Server on port 4444
#2021-04-04 19:42:09.977:INFO::main: Logging initialized @595ms to org.seleniumhq.jetty9.util.log.StdErrLog
#19:42:10.249 INFO [WebDriverServlet.<init>] - Initialising WebDriverServlet
#19:42:10.382 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 4444

#selenium.jarのジョブ確認
system("ps -A | grep 'java'")
# 3921 ??         0:01.50 /usr/bin/java -jar selenium.jar
# 3925 ??         0:00.00 sh -c ps -A | grep 'java'
# 3927 ??         0:00.00 grep java

#OR
#system("ps -A | grep 'selenium'")
# 3921 ??         0:01.52 /usr/bin/java -jar selenium.jar
# 3928 ??         0:00.01 sh -c ps -A | grep 'selenium'
# 3930 ??         0:00.00 grep selenium
 
#selenium.jarの止め方 - 終了時に実行すること
#上記コマンドで、プロセスのPIDを見つけて、そのIDをkillする
system("kill 3921")
system("ps -A | grep 'java'")
# 3939 ??         0:00.01 sh -c ps -A | grep 'java'
# 3941 ??         0:00.00 grep java

ここでは、selenium.jarのジョブが動いていれば、問題ない。

RSeleniumの使い方 - ブラウザ操作の基本

次に、RSeleniumパッケージをインストールする。

ここでは、Google Chromeを使ったやり方を紹介する。FireFoxでの実行例は、補足部分に記載している。

RSeleniumのポートは、「4444」を使用する。

#RSeleniumのインストール・ロード
install.packages("RSelenium")
library(RSelenium)

#Google Chrome 起動の場合
rsChr <- RSelenium::remoteDriver(port = 4444L, browserName = "chrome")
#[1] "Connecting to remote server"
#19:44:57.403 INFO [ActiveSessionFactory.apply] - Capabilities are: {
#  "browserName": "chrome",
#  "javascriptEnabled": true,
#  "nativeEvents": true,
#  "version": ""
#}
#(中略)

この状態で、Webブラウザ操作ができる。

以下が、基本操作のコマンドである。

#基本操作
#Webブラウザを開く
rsChr$open()

#Googleページへ移動する
rsChr$navigate("https://www.google.com")

#Rページへ移動する
rsChr$navigate("https://www.r-project.org/")

#1つ前のページに戻る(ex. Rページ => Googleページ)
rsChr$goBack()

#1つ先のページに戻る(ex. Googleページ => Rページ)
rsChr$goForward()

#現在のページのURL取得
rsChr$getCurrentUrl()
#[[1]]
#[1] "https://www.r-project.org/"

#ページの更新
rsChr$refresh()

#Webブラウザウインドの最大化
rsChr$maxWindowSize()

#ブラウザを閉じる
rsChr$close()

Google検索とか情報取得とかをやってみる

まずは、基本の「キ」ということで、RSeleniumを使った、Google検索をやってみる。

簡単なGoogle検索だけなら、以下の4ステップできる。

#ブラウザを開く
rsChr$open()

#Googleページに移動する
rsChr$navigate("https://www.google.co.jp/")

##Google検索の実行
#検索ウインドを選択
WebSearch <- rsChr$findElement(using = "css", "[name = 'q']")

#検索語「R Cran」を入力して、実行
WebSearch$sendKeysToElement(list("R Cran", key = "enter"))

このように、「R Cran」での検索結果が表示されたら、うまくいっている。

検索結果を取得する

次に、検索結果を取得する。重複とか関係ないところがあるので、以下のように不要部分を切って、出力させる。

#検索結果(全部)を取得する
ResultLink <- rsChr$findElements(using = "css", "[href]")
getResultLink <- unlist(lapply(ResultLink, function(x) {x$getElementAttribute("href")}))

#google関係を除外 + 重複除外
getResultLink <- unique(getResultLink[!grepl("[.]google|[.]gstatic[.]", getResultLink)])
getResultLink
# [1] "https://cran.r-project.org/"                                                                                        
# [2] "https://cran.r-project.org/bin/windows/"                                                                            
# [3] "https://cran.r-project.org/web/packages/available_packages_by_name.html"                                            
# [4] "https://cran.r-project.org/mirrors.html"                                                                            
# [5] "https://cran.r-project.org/web/packages/"                                                                           
# [6] "https://cran.r-project.org/bin/windows/base/"                                                                       
# [7] "https://cran.r-project.org/doc/contrib/manuals-jp/R-admin-jp.v15.pdf"                                               
# [8] "http://www.okadajp.org/RWiki/?CRAN%E5%9B%BD%E5%86%85%E3%83%9F%E3%83%A9%E3%83%BC%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9"
# [9] "https://cran.ism.ac.jp/"                                                                                            
#[10] "https://mjin.doshisha.ac.jp/iwanami/R/index.pdf"                                                                    
#[11] "https://staffblog.amelieff.jp/entry/2018/04/19/152233"                                                              
#[12] "https://qiita.com/hoxo_m/items/ce478bf0debe963d9e40"                                                                
#[13] "https://www.trifields.jp/statistical-analysis-r-cran-packages-341"                                                  
#[14] "https://en.wikipedia.org/wiki/Rnn_(software)" 

検索ヒット数を取得する

次に、検索ヒット数の結果である「約 23,300,000 件 ...」の項目を取得してみる。

#検索ヒット数を取得する
ResultStats <- rsChr$findElements(using = "id", "result-stats")

#テキストを取得
getResultStats <- unlist(lapply(ResultStats, function(x) {x$getElementText()}))
getResultStats
"約 23,300,000 件 (0.89 秒) "

#数値に変換(日本語の場合)
as.numeric(strsplit(gsub("[,]", "", sub("約 ", "", getResultStats)), split=" 件 ")[[1]][1])
#[1] 23300000

ヒットしたページに移動する

トップヒット、あるいはセカンド・ヒットのページに移動してみる。

今回、セカンド・ヒットは、トップヒットであるCRANのページ以外で最初に出てくるページを、セカンド・ヒットと定義したので、greplで少々整形してある。

#トップヒットのページに移動する
rsChr$navigate(getResultLink[1])

#セカンド・ヒットのページに移動する
rsChr$navigate(getResultLink[!grepl(getResultLink[1], getResultLink)][1])

ページ移動後に、ページのスクショを録る

#トップヒットのページに移動する
rsChr$navigate(getResultLink[1])

#スクショを録る => Rの作業ディレクトリに保存
rsChr$screenshot(file = 'test.png')

##同じ結果だが
#magick::image_writeで保存する場合
screenshot <- rsChr$screenshot(display = FALSE)
magick::image_write(screenshot, "test1.png")

#Rでスクショを表示させる
Img <- magick::image_read(base64enc::base64decode(toString(screenshot), output = NULL))
Img

CRAN HPのスクショが作業ディレクトリに保存されていたら、動作OKである。

まとめ

「動作が遅い」という評価が多かったものの、 思いの外、サクサク動いて、結構良かった。

実際、自動操作して、数万回とかアクセスするときには、 スリープ時間を乱数で入れるとかで、実行は遅くなるのかもと。

補足

Google 検索の有料APIについて

Google 検索の有料APIは、結構高い。 1万回検索するのに、50ドルかかるみたい。

RSeleniumを覚える方が経済的だよね。

RSelenium / FirefoxでのGoogle検索実行

#Firefox起動の場合
rsFox <- RSelenium::remoteDriver(port = 4444L, browserName = "firefox")

#ブラウザ起動
rsFox$open()

#Googleページに移動
rsFox$navigate("https://www.google.co.jp/")

#Google検索実行
WebSearch.Fox <- rsFox$findElement(using = "css", "[name = 'q']")
WebSearch.Fox$sendKeysToElement(list("R Cran", key = "enter"))

Rで、Python版Seleniumを実行してみる

#pipの確認
system("which pip")
#/usr/local/bin/pip
system("pip -V")
#pip 21.0.1 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)

#seleniumのインストール
system("pip install selenium")
#Collecting selenium
#  Downloading selenium-3.141.0-py2.py3-none-any.whl (904 kB)
#Requirement already satisfied: urllib3 in /usr/local/lib/python3.8/site-packages (from selenium) (1.26.3)
#Installing collected packages: selenium
#Successfully installed selenium-3.141.0

#Pythonの選択
reticulate::use_python("/usr/local/bin/python", required =T)

#ライブラリのロード
selenium <- reticulate::import(module = "selenium")

#Firefoxの起動
Firefox <- selenium$webdriver$Firefox()

#Googleページに移動
Firefox$get('http://google.com/')

#検索窓を選択
element <- Firefox$find_element_by_name("q")

#検索を実行する
element$send_keys('R Cran'); element$submit()

#Firefoxを閉じる
Firefox$quit()

Webスクレイピングについての関連図書

Webスクレイピングの関連図書を列挙しておきます。

参考資料

stackoverflow.com

docs.ropensci.org

qiita.com

*1:Webアプリケーションをテストするためのポータブルフレームワークである。