如何獲取資料:擷取網頁內容(下)

從 Python 與 R 語言使用 Selenium WebDriver 操控瀏覽器

Photo by rawpixel on Unsplash

The world’s most valuable resource is no longer oil, but data.
The Economist — May 6th, 2017

摘要

如何獲取資料:擷取網頁內容(上)一文中討論如何從常見資料來源(檔案、資料庫與網頁)中的第三種來源:網頁的 html 檔案中擷取資料,即為人耳熟能詳的爬蟲技巧,我們知道如何使用 CSS 選擇器與 XPath 定位網頁資料,然後分別使用 pyquery 模組與 rvest 套件分別解析至 Python 以及 R 語言中,不過在文末面對到一個問題:無法憑藉電影名稱對應到該電影資訊的頁面。

本文簡介如何使用 selenium 自動化網頁測試工具連接 Python 與 R 語言,協助兩者的程式碼操控瀏覽器,進而前往含有指定電影資訊的網頁。


修飾擷取電影資訊的函數

首先我們將如何獲取資料:擷取網頁內容(上)撰寫的函數再修飾一番,原文中擷取電影特定電影的評分(Rating)、劇情類型(Genre)、海報圖片連結(Poster)和演員名單(Cast)是分開的四個函數;修飾成 get_movie_info(movie_url) 函數,可以將四個電影資訊儲存在一個 Python 的 dict 中或一個 R 語言的 list 中。

## {'rating': 8.8,
'genre': ['Action', 'Adventure', 'Fantasy'],
'poster': 'https://m.media-amazon.com/images/M/MV5BMjMxNjY2MDU1OV5BMl5BanBnXkFtZTgwNzY1MTUwNTM@._V1_UX182_CR0,0,182,268_AL_.jpg',
'cast': ['Robert Downey Jr.',
'Chris Hemsworth',
'Mark Ruffalo',
'Chris Evans',
'Scarlett Johansson',
'Don Cheadle',
'Benedict Cumberbatch',
'Tom Holland',
'Chadwick Boseman',
'Zoe Saldana',
'Karen Gillan',
'Tom Hiddleston',
'Paul Bettany',
'Elizabeth Olsen',
'Anthony Mackie']}
## $rating
[1] 8.8
$genre
[1] "Action" "Adventure" "Fantasy"
$poster
[1] "https://m.media-amazon.com/images/M/MV5BMjMxNjY2MDU1OV5BMl5BanBnXkFtZTgwNzY1MTUwNTM@._V1_UX182_CR0,0,182,268_AL_.jpg"
$cast
[1] "Robert Downey Jr." "Chris Hemsworth" "Mark Ruffalo" "Chris Evans"
[5] "Scarlett Johansson" "Don Cheadle" "Benedict Cumberbatch" "Tom Holland"
[9] "Chadwick Boseman" "Zoe Saldana" "Karen Gillan" "Tom Hiddleston"
[13] "Paul Bettany" "Elizabeth Olsen" "Anthony Mackie"

遭遇到的問題

將函數修飾過後,我們想利用它一次擷取多部電影資訊,像是 Marvel 漫威系列的復仇者聯盟、黑豹或鋼鐵人,這時就會發現一個問題,每一部電影資訊頁面的網址皆是 IMDB 資料庫的一個 id,例如 Avengers: Infinity War 的 id 是 tt4154756;而 Black Panther 的 id 是 tt1825683,因此要取得網址,得先在 IMDB 的搜尋對話框中輸入電影名稱,再前往電影資訊頁面,假如要大量擷取電影資訊,手動做法會耗費太多的力氣,得想一個更好的方法才行:這時我們求助可以操控瀏覽器的 Selenium 解決方案。

什麼是 Selenium

Selenium 是瀏覽器自動化的解決方案,主要是網頁應用程式測試目的,在資料科學團隊中運用於解決擷取網頁資料所碰到的問題,例如面對到需要登入、填寫表單或者點選按鈕後才會顯示出資料的網站。Python 與 R 語言採用的是 Selenium 中的 WebDriver 元件,運作的方式略有不同。

Python 透過 Selenium WebDriver 呼叫瀏覽器驅動程式,再由瀏覽器驅動程式去呼叫瀏覽器;R 語言透過 Selenium Server 直接呼叫瀏覽器,本篇文章採用的是 Selenium Server Standalone 的 .jar 檔案,電腦必須安裝有 java 才能夠執行。如果使用者的電腦沒有安裝,請先前往 java 下載頁面下載後安裝。

Selenium WebDriver 對 Google Chrome 與 Mozilla Firefox 兩個主流瀏覽器的支援最好,為了確保使用上不會碰到問題,建議都使用最新版的瀏覽器、瀏覽器驅動程式與模組。

下載瀏覽器

前往官方網站下載最新版的瀏覽器。

前往官方網站下載最新版的 Google Chrome
前往官方網站下載最新版的 Mozilla Firefox

安裝 Selenium

Python

前往官方網站下載最新版的瀏覽器驅動程式,Chrome 瀏覽器的驅動程式名稱為 ChromeDriver,Firefox 瀏覽器的驅動程式名稱為 geckodriver。

前往官方網站下載最新版的 ChromeDriver
前往官方網站下載最新版的 geckodriver

下載完成以後解壓縮在熟悉的路徑讓後續的指派較為方便,我習慣放在使用者家目錄的下載資料夾,因此路徑會是 /Users/YOURUSERNAME/Downloads/chromedriver 以及 /Users/YOURUSERNAME/Downloads/geckodriver,相同路徑 Windows 的使用者應該是 C:/Users/YOURUSERNAME/Downloads/chromedriver.exe 以及 C:/Users/YOURUSERNAME/Downloads/geckodriver.exe

接著在終端機安裝 Selenium 模組。

接著測試用程式碼透過 ChromeDriver 與 geckodriver 分別操控 Chrome 瀏覽器以及 Firefox 瀏覽器前往 IMDB 首頁並將首頁的網址印出再關閉瀏覽器。

## https://www.imdb.com/
用程式碼透過 ChromeDriver 操控 Chrome 瀏覽器前往 IMDB 首頁
用程式碼透過 geckodriver 操控 Firefox 瀏覽器前往 IMDB 首頁

R 語言

前往官方網站下載最新版的 Selenium Standalone Driver,並且在終端機啟動。

接著測試用程式碼分別操控 Chrome 瀏覽器以及 Firefox 瀏覽器前往 IMDB 首頁並將首頁的網址印出再關閉瀏覽器,我們使用 RSelenium 套件從 R 語言操控 Selenium Driver。

## [[1]]
[1] "https://www.imdb.com/"
用程式碼透過 Selenium Server 操控 Chrome 瀏覽器前往 IMDB 首頁
用程式碼透過 Selenium Server 操控 Firefox 瀏覽器前往 IMDB 首頁

盤點手動操控的動作順序

測試完畢確認可以利用 Python 與 R 語言啟動 Chrome 以及 Firefox 瀏覽器之後,接著是盤點從 IMDB 首頁前往指定電影資訊頁面過程中,手動用滑鼠、鍵盤所操控的動作:

  1. 前往 IMDB 首頁
  2. 在搜尋欄位輸入電影名稱
  3. 點選搜尋按鈕
  4. 點選符合度最高的連結(最上方)
  5. 來到指定電影資訊頁面
前往 IMDB 首頁
在搜尋欄位輸入電影名稱
點選搜尋按鈕
點選符合度最高的連結(最上方)
來到指定電影資訊頁面

盤點要使用到的方法

Python 的 selenium 模組與 R 語言的 RSelenium 套件中對方法的命名不同,因此我們分開盤點。

Python 要使用的方法

  • driver.get() :前往 IMDB 首頁
  • driver.find_element_by_xpath()driver.find_element_by_css_selector() :定位搜尋欄位、搜尋按鈕與搜尋結果連結
  • driver.current_url :取得當下瀏覽器的網址
  • elem.send_keys() :輸入電影名稱
  • elem.click() :按下搜尋按鈕與連結

R 語言要使用的方法

  • remDr$navigate() :前往 IMDB 首頁
  • remDr$findElement(using = "css")remDr$findElement(using = "xpath"):定位搜尋欄位、搜尋按鈕與搜尋結果連結
  • remDr$getCurrentUrl() :取得當下瀏覽器的網址
  • elem$sendKeysToElement() :輸入電影名稱
  • elem$clickElement() :按下搜尋按鈕與連結

擷取多部電影資訊的函數

接著建立 get_movies() 函數,輸入電影名稱、利用 Selenium 瀏覽到指定電影頁面最後再呼叫一開始修飾過的 get_movie_info() 函數,然後將多部電影的結果儲存到 Python 的 dict 中或者 R 語言的 list 中並以電影名稱作為標籤。

Python

## {'rating': 8.8, 'genre': ['Action', 'Adventure', 'Fantasy'], 'poster': 'https://m.media-amazon.com/images/M/MV5BMjMxNjY2MDU1OV5BMl5BanBnXkFtZTgwNzY1MTUwNTM@._V1_UX182_CR0,0,182,268_AL_.jpg', 'cast': ['Robert Downey Jr.', 'Chris Hemsworth', 'Mark Ruffalo', 'Chris Evans', 'Scarlett Johansson', 'Don Cheadle', 'Benedict Cumberbatch', 'Tom Holland', 'Chadwick Boseman', 'Zoe Saldana', 'Karen Gillan', 'Tom Hiddleston', 'Paul Bettany', 'Elizabeth Olsen', 'Anthony Mackie']}


## {'rating': 7.5, 'genre': ['Action', 'Adventure', 'Sci-Fi'], 'poster': 'https://m.media-amazon.com/images/M/MV5BMTg1MTY2MjYzNV5BMl5BanBnXkFtZTgwMTc4NTMwNDI@._V1_UX182_CR0,0,182,268_AL_.jpg', 'cast': ['Chadwick Boseman', 'Michael B. Jordan', "Lupita Nyong'o", 'Danai Gurira', 'Martin Freeman', 'Daniel Kaluuya', 'Letitia Wright', 'Winston Duke', 'Sterling K. Brown', 'Angela Bassett', 'Forest Whitaker', 'Andy Serkis', 'Florence Kasumba', 'John Kani', 'David S. Lee']}

R 語言

## movies$`Avengers: Infinity War`
$rating
[1] 8.8
$genre
[1] "Action" "Adventure" "Fantasy"
$poster
[1] "https://m.media-amazon.com/images/M/MV5BMjMxNjY2MDU1OV5BMl5BanBnXkFtZTgwNzY1MTUwNTM@._V1_UX182_CR0,0,182,268_AL_.jpg"
$cast
[1] "Robert Downey Jr." "Chris Hemsworth" "Mark Ruffalo" "Chris Evans"
[5] "Scarlett Johansson" "Don Cheadle" "Benedict Cumberbatch" "Tom Holland"
[9] "Chadwick Boseman" "Zoe Saldana" "Karen Gillan" "Tom Hiddleston"
[13] "Paul Bettany" "Elizabeth Olsen" "Anthony Mackie"
## movies$`Black Panther`
$rating
[1] 7.5
$genre
[1] "Action" "Adventure" "Sci-Fi"
$poster
[1] "https://m.media-amazon.com/images/M/MV5BMTg1MTY2MjYzNV5BMl5BanBnXkFtZTgwMTc4NTMwNDI@._V1_UX182_CR0,0,182,268_AL_.jpg"
$cast
[1] "Chadwick Boseman" "Michael B. Jordan" "Lupita Nyong'o" "Danai Gurira"
[5] "Martin Freeman" "Daniel Kaluuya" "Letitia Wright" "Winston Duke"
[9] "Sterling K. Brown" "Angela Bassett" "Forest Whitaker" "Andy Serkis"
[13] "Florence Kasumba" "John Kani" "David S. Lee"

延伸閱讀

DataInPoint

DataInPoint 是一個超棒的資料科學專欄,主題涵蓋資料、程式、機器學習與高效能運算。

郭耀仁 Yao-Jen Kuo

Written by

Could that data be any tidier? It is always nice to meet a data enthusiast / 2:43 marathon runner.

DataInPoint

DataInPoint 是一個超棒的資料科學專欄,主題涵蓋資料、程式、機器學習與高效能運算。