Preface
這個小節延續輕鬆學習 Python:透過 API 擷取網站資料、輕鬆學習 Python:透過解析 HTML 擷取網站資料 討論如何使用 Python 從第三種來源:網頁透過 selenium 套件 操控瀏覽器來擷取 HTML(全名為 HyperText Markup Language)格式的資料源,selenium 除了具備操控瀏覽器的功能還內建有以 XPath(提供在 XML/HTML 資料中以 XML 節點找尋特定資料位置的定位方法)或 CSS Selector(提供在 HTML 資料中以層疊樣式表找尋特定資料位置的定位方法)為基礎的資料解析函數。
完整的 Jupyter Notebook 內容可以參考 這裡.
關於 Selenium
我們打算求助可以操控瀏覽器的 Selenium,這是一個瀏覽器自動化的解決方案,主要應用於網頁程式測試目的;在資料科學團隊中被運用於解決擷取網站資料所碰到的問題,例如面對到需要登入、填寫表單或者點選按鈕後才會顯示出資料的網站。Python 可以透過 Selenium WebDriver 呼叫 瀏覽器驅動程式,再由 瀏覽器驅動程式 去呼叫 瀏覽器。Selenium WebDriver 對 Google Chrome 與 Mozilla Firefox 兩個主流瀏覽器的支援最好,為了確保使用上不會碰到問題,建議都使用最新版的瀏覽器、瀏覽器驅動程式與模組。
下載瀏覽器
前往官方網站下載最新版的瀏覽器。
安裝 瀏覽器驅動程式 與 Selenium
前往官方網站下載最新版的 瀏覽器驅動程式,Chrome 瀏覽器的驅動程式名稱為 ChromeDriver,Firefox 瀏覽器的驅動程式名稱為 geckodriver。
下載完成以後解壓縮在熟悉的路徑讓後續的指派較為方便, 以我自身為範例, 下載並解壓縮後, 執行檔的位置在 "C:\tmp\chromedriver.exe".
接著在終端機安裝 Selenium 模組:
接著底下測試用程式碼透過 ChromeDriver 操控 Chrome 瀏覽器前往 IMDB.com 並將首頁的網址印出再關閉瀏覽器:
- from selenium import webdriver
- imdb_home = "https://www.imdb.com/"
- driver = webdriver.Chrome(executable_path="C:/tmp/chromedriver.exe") # Use Chrome
- driver.get(imdb_home)
- print(driver.current_url)
- driver.close()
- from selenium import webdriver
- imdb_home = "https://www.imdb.com/"
- driver = webdriver.Firefox(executable_path="YOURGECKODRIVERPATH") # Use Firefox
- driver.get(imdb_home)
- print(driver.current_url)
- driver.close()
測試完畢確認可以利用 Python 啟動 Chrome 以及 Firefox 瀏覽器之後,接著是盤點從 IMDB.com 前往指定電影資訊頁面過程中,手動用滑鼠、鍵盤所操控的動作:
前往 IMDB.com
在搜尋欄位輸入電影名稱
點選搜尋按鈕
將搜尋結果限縮在「電影」
點選搜尋符合度最高的連結
然後盤點會使用到的 Selenium WebDriver 方法:
安裝與使用 Chrome 瀏覽器外掛:XPath Helper
Selenium WebDriver 除了與 BeautifulSoup4、PyQuery 一樣支援以 CSS Selector 定位資料位址,亦支援 XPath,利用這個機會,我們簡介如何安裝與使用 Chrome 瀏覽器外掛 XPath Helper:
依照下列步驟使用 Chrome 瀏覽器外掛:XPath Helper:
使用 Selenium 擷取多部電影資訊
接著寫作 get_movies() 函數,這個函數接受輸入電影名稱,會利用 Selenium 瀏覽到指定電影頁面,再呼叫一開始寫好的 get_movie_info() 函數,最後將多部電影的結果儲存到 Python 的 dict 中並以電影名稱作為 dict 的 key:
- from pyquery import PyQuery as pq
- from selenium import webdriver
- from random import randint
- import time
- def get_movie_info(movie_url):
- """
- Get movie info from certain IMDB url
- """
- d = pq(movie_url)
- movie_rating = float(d("strong span").text())
- movie_genre = [x.text() for x in d(".subtext a").items()]
- movie_release_date = movie_genre.pop()
- movie_poster = d(".poster img").attr('src')
- movie_cast = [x.text() for x in d(".primary_photo+ td a").items()]
- # 回傳資訊
- movie_info = {
- "movieRating": movie_rating,
- "movieReleaseDate": movie_release_date,
- "movieGenre": movie_genre,
- "moviePosterLink": movie_poster,
- "movieCast": movie_cast
- }
- return movie_info
- def get_movies(*args):
- """
- Get multiple movies' info from movie titles
- """
- imdb_home = "https://www.imdb.com/"
- driver = webdriver.Chrome(executable_path="C:/tmp/chromedriver.exe") # Use Chrome
- # driver = webdriver.Firefox(executable_path="PATHTOYOURGECKODRIVER") # Use Firefox
- movies = dict()
- for movie_title in args:
- # 前往 IMDB 首頁
- driver.get(imdb_home)
- # 定位搜尋欄位
- search_elem = driver.find_element_by_id("suggestion-search")
- # 輸入電影名稱
- search_elem.send_keys(movie_title)
- # 定位搜尋按鈕
- submit_elem = driver.find_element_by_id("suggestion-search-button")
- # 按下搜尋按鈕
- submit_elem.click()
- # 限縮搜尋結果為「電影」類
- category_movie_elem = driver.find_element_by_xpath("//ul[@class='findTitleSubfilterList']/li[1]/a")
- # 按下限縮搜尋結果
- category_movie_elem.click()
- # 定位搜尋結果連結
- first_result_elem = driver.find_element_by_xpath("//div[@class='findSection'][1]/table[@class='findList']/tbody/tr[@class='findResult odd'][1]/td[@class='result_text']/a")
- # 按下搜尋結果連結
- first_result_elem.click()
- # 呼叫 get_movie_info()
- current_url = driver.current_url
- movie_info = get_movie_info(current_url)
- movies[movie_title] = movie_info
- time.sleep(randint(3, 8))
- driver.close()
- return movies
- get_movies("Avengers: Endgame", "Captain Marvel")
- {'Avengers: Endgame': {'movieRating': 8.5,
- 'movieReleaseDate': '24 April 2019 (Taiwan)',
- 'movieGenre': ['Action', 'Adventure', 'Drama'],
- 'moviePosterLink': 'https://m.media-amazon.com/images/M/MV5BMTc5MDE2ODcwNV5BMl5BanBnXkFtZTgwMzI2NzQ2NzM@._V1_UX182_CR0,0,182,268_AL_.jpg',
- 'movieCast': ['Robert Downey Jr.',
- 'Chris Evans',
- 'Mark Ruffalo',
- 'Chris Hemsworth',
- 'Scarlett Johansson',
- 'Jeremy Renner',
- 'Don Cheadle',
- 'Paul Rudd',
- 'Benedict Cumberbatch',
- 'Chadwick Boseman',
- 'Brie Larson',
- 'Tom Holland',
- 'Karen Gillan',
- 'Zoe Saldana',
- 'Evangeline Lilly']},
- 'Captain Marvel': {'movieRating': 6.9,
- 'movieReleaseDate': '6 March 2019 (Taiwan)',
- 'movieGenre': ['Action', 'Adventure', 'Sci-Fi'],
- 'moviePosterLink': 'https://m.media-amazon.com/images/M/MV5BMTE0YWFmOTMtYTU2ZS00ZTIxLWE3OTEtYTNiYzBkZjViZThiXkEyXkFqcGdeQXVyODMzMzQ4OTI@._V1_UX182_CR0,0,182,268_AL_.jpg',
- 'movieCast': ['Brie Larson',
- 'Samuel L. Jackson',
- 'Ben Mendelsohn',
- 'Jude Law',
- 'Annette Bening',
- 'Djimon Hounsou',
- 'Lee Pace',
- 'Lashana Lynch',
- 'Gemma Chan',
- 'Clark Gregg',
- 'Rune Temte',
- 'Algenis Perez Soto',
- 'Mckenna Grace',
- 'Akira Akbar',
- 'Matthew Maher']}}
* Selenium WebDriver API
沒有留言:
張貼留言