本篇介紹如何在 R 中透過 RSelenium 模組使用 Selenium 自動操控瀏覽器,下載並擷取網頁中的資料。

RSelenium 是一個可以讓 R 程式透過 Selenium 操控瀏覽器的套件,它的功能非常強大,幾乎可以讓程式對瀏覽器進行任何操作,以下我們要介紹如何在 R 中使用 RSelenium,透過標準的 Google Chrome 或 Firefox 瀏覽器,擷取到最標準的網頁內容。

安裝 RSelenium 套件

RSelenium 可以從 R 官方的 CRAN 套件庫下載安裝:

# 從 CRAN 安裝 RSelenium
install.packages("RSelenium")

或是從 GitHub 上面下載最新的版本來安裝:

# 從 GitHub 安裝 RSelenium
install.packages("devtools")
devtools::install_github("ropensci/RSelenium")

安裝好之後,載入 RSelenium 套件:

# 載入 RSelenium 套件
library("RSelenium")

若沒有錯誤訊息,就表示安裝完成了。

WebDriver

Selenium 是透過 WebDriver API 來操控瀏覽器的,所以在使用時必須先安裝對應瀏覽器的 WebDriver,目前支援 WebDriver 的瀏覽器有 Google Chrome 與 Firefox:

ChromeDriver 與 geckodriver 這兩種 WebDriver 的使用方式都相同,首先從它們的官方網站依據自己的作業系統下載編譯好的 WebDriver 執行檔,以 Windows 作業系統來說,ChromeDriver 的執行檔為 chromedriver.exe,而 geckodriver 的執行檔則為 geckodriver.exe

下載 WebDriver 的執行檔之後,放在自己喜歡的固定位置,然後記下 WebDriver 的放置路徑,在下面的 Selenium 伺服器啟動時,指定這個路徑即可。

Selenium 伺服器

Selenium 官方網站下載最新版的 Selenium JAR 檔,然後在 Windows 命令提示字元中,以 Java 執行此 JAR 檔,並且指定 chromedriver.exegeckodriver.exe 的放置路徑,假設這兩個 WebDriver 都放在 D:\ 底下,則執行:

java -Dwebdriver.chrome.driver=D:\chromedriver.exe -Dwebdriver.gecko.driver=D:\geckodriver.exe -jar selenium-server-standalone-3.12.0.jar

啟動 Selenium 伺服器

這裡我們同時指定 Google Chrome 與 Firefox 兩個瀏覽器的 WebDriver 位置,這樣的話在程式中就可以使用兩種瀏覽器,如果只使用一種瀏覽器的話,可以將沒用到的 WebDriver 省略。

操控瀏覽器

安裝好並啟動了 Selenium 伺服器之後,接著就可以回到 R 中開始使用 RSelenium 套件操控瀏覽器了。

首先載入 RSelenium 套件:

# 載入 RSelenium 套件
library("RSelenium")

接著使用 remoteDriver 函數連接 Selenium 伺服器:

# 連接 Selenium 伺服器,選用 Firefox 瀏覽器
remDr <- remoteDriver(
  remoteServerAddr = "localhost",
  port = 4444,
  browserName = "firefox")

預設的 Selenium 伺服器會開在 localhost4444 連接埠,分別可用 remoteServerAddrport 參數來指定,而 browserName 則是指定要使用的瀏覽器,如果慣用 Google Chrome 的人,可以指定為 chrome

連接 Selenium 伺服器之後,即可用 RSelenium 開啟瀏覽器:

# 開啟瀏覽器
remDr$open()

以 RSelenium 開啟的 Firefox 瀏覽器,在外觀上看起來會跟正常開啟的 Firefox 瀏覽器有一些差異,網址列會有一個機器人的圖示,標示這個瀏覽器是由程式在控制的。

以 RSelenium 開啟的 Firefox 瀏覽器

若要瀏覽指定的網址,可以呼叫 navigate 函數:

# 瀏覽 Google 首頁
remDr$navigate("https://www.google.com.tw/")

Google 首頁

接著我們讓瀏覽器開啟另外一個網頁:

# 瀏覽 Yahoo 首頁
remDr$navigate("https://tw.yahoo.com/")

若要讓瀏覽器回到上一頁,可以呼叫 goBack 函數:

# 回到上一頁
remDr$goBack()

若要前往下一頁,則可以呼叫 goForward 函數:

# 前往下一頁
remDr$goForward()

若要取得目前瀏覽器網址列中的網址,可以使用 getCurrentUrl 函數:

# 取的目前網頁的網址
remDr$getCurrentUrl()
[[1]]
[1] "https://tw.yahoo.com/"

呼叫 refresh可以讓瀏覽器重新整理網頁:

# 重新整理
remDr$refresh()

擷取網頁元素

在控制瀏覽器瀏覽網頁之後,我們可以進一步透過 RSelenium 擷取網頁中的元素與資料。這裡我們以 Google 搜尋 office 這個關鍵字的結果網頁作為示範:

# 用 Google 搜尋 office 關鍵字
remDr$navigate("https://www.google.com.tw/search?q=office")

在擷取網頁元素時,主要的方法就是使用 findElement 這個函數,配合各種條件篩選出自己想要的結果,以下是常見的一些應用實例。

id 擷取網頁元素

若要以指定的 id 擷取網頁元素,可以在呼叫 findElement 時,將 using 指定為 id,並將 value 指定為網頁元素的 id 屬性值。

例如若要將網頁中 id 屬性值為 ires 的元素取出來,可以這樣寫:

# 依據 ID 取得網頁元素
web.elem <- remDr$findElement(using = "id", value = "ires")

將網頁元素擷取出來之後,就可以對該元素進行各項操作,例如取得網頁元素的各種屬性值:

# 取得網頁元素的 id 屬性值
web.elem$getElementAttribute("id")
[[1]]
[1] "ires"
# 取得網頁元素的 data-async-context 屬性值
web.elem$getElementAttribute("data-async-context")
[[1]]
[1] "query:office"

或是取得網頁元素的文字內容:

# 取得網頁元素的文字內容
web.elem$getElementText()
[[1]]
[1] "Office 365 Login | Microsoft Office\nhttps://www.office.com/\n[略]

class 擷取網頁元素

若要使用網頁元素的 class 名稱來擷取網頁元素,可將 using 設定為 class name,並在 value 指定網頁元素的 class 名稱:

# 依據 Class 取得網頁元素
web.elem <- remDr$findElement(using = 'class name', value = "r")

取出的網頁元素,操作方式都是相同的:

# 取得網頁元素的文字內容
web.elem$getElementText()
[[1]]
[1] "Office 365 Login | Microsoft Office"

尋找子元素

如果想在指定的元素中,繼續尋找更下層的子元素,可以使用 findChildElement。假設我們想以現有的 web.elem 為起始點,往下尋找超連結的元素(HTML 節點名稱為 a 的元素),可以這樣寫:

# 從 web.elem 往下尋找超連結的元素
a.elem <- web.elem$findChildElement(using = "tag name", value = "a")

找到之後,傳回的元素也是用相同的方式操作:

# 取出超連結的網址
a.elem$getElementAttribute("href")
[[1]]
[1] "https://www.office.com/"

擷取多個元素

在網頁中可能會有多個元素都傭有相同的 class 名稱,若要一次取出所有符合的元素,可以改用 findElements 函數,其法相同,但可以一次將所有的符合條件的結果以 list 的方式傳回:

# 依據 Class 取得多個網頁元素
web.elem.list <- remDr$findElements(using = 'class name', value = "r")

得到所有符合條件的元素之後,即可使用一般 list 處理方式,取出想要的資料:

# 取出每個網頁元素的文字內容
unlist(lapply(web.elem.list, function(e) { e$getElementText() }))
 [1] "Office 365 Login | Microsoft Office"
 [2] "Microsoft Office | 家用和辦公室用生產力工具"
 [3] "Office 產品- Microsoft Office"
 [4] "Office 下載 - Microsoft"
 [5] "微軟Office 2016 專業增強版(繁體中文下載+免費永久序號註冊) @ 呂 ..."
 [6] "UGAMail – Office 365"  
 [7] "Office - Home | Facebook"
 [8] "My Account - Outlook.com"
 [9] "Office"
[10] "The Office (U.S. TV series) - Wikipedia"

以 CSS 選擇器擷取網頁元素

在實務上擷取網頁,最常使用的方式應該就是 CSS 選擇器,只要將 using 設定為 css selector,並在 value 中指定 CSS 選擇器的規則,即可快速篩選出指定的網頁元素:

# 依據 CSS 選擇器取得網頁元素
web.elem.list <- remDr$findElements(using = 'css selector', value = "div.rc h3.r a")

這我們取出每一個 Google 搜尋結果的超連結,並將實際的網址列出來:

# 取出每個超連結的網址
unlist(lapply(web.elem.list, function(e) { e$getElementAttribute("href") }))
 [1] "https://www.office.com/"
 [2] "https://products.office.com/zh-tw/home"
 [3] "https://products.office.com/zh-tw/products"
 [4] "https://www.microsoft.com/zh-tw/download/office.aspx"
 [5] "http://a4287604.pixnet.net/blog/post/201669981-%E5%BE%AE%E8%BB%9F-office-2016-%E5%B0%88%E6%A5%AD%E5%A2%9E%E5%BC%B7%E7%89%88-%28%E7%B9%81%E9%AB%94%E4%B8%AD%E6%96%87%E4%B8%8B%E8%BC%89%2B%E5%85%8D"
 [6] "https://ugamail.uga.edu/"
 [7] "https://www.facebook.com/Office/"
 [8] "https://office.live.com/start/MyAccount.aspx"
 [9] "https://visitoffice.com/"
[10] "https://en.wikipedia.org/wiki/The_Office_(U.S._TV_series)"

以 XPath 擷取網頁元素

# 依據 XPath 取得網頁元素
web.elem <- remDr$findElement(using = 'xpath', value = "//a[@href='https://www.office.com/']")

找到之後,用相同的方式操作傳回的元素:

# 取得網頁元素的文字內容
web.elem$getElementText()
[[1]]
[1] "Office 365 Login | Microsoft Office"

參考資料:rOpenSciYao-Jen KuoYao-Jen Kuo張郎生活的筆記RPubs