Selenium 網路爬蟲的王者

Selenium 網路爬蟲的王者

前言

在前面我們有介紹有些網頁伺服器會阻擋網路爬蟲讀取網頁內容,我們可以使用 headers 的宣告,將爬蟲程式偽裝成瀏覽器,這樣我們克服了讀取網頁內容的障礙。

Selenium 功能可以控制瀏覽器,所以當使用 Selenium 當爬蟲工具時,網路伺服器會認為來讀取資料的是瀏覽器,所以不會有被阻擋無法讀取網頁 HTML 原始檔的問題。當然則 Selenium 不只如此,可以使用它按連結、填寫登入資訊、甚至訂票系統、搶購系統.....等。

大綱

順利使用 Selenium 工具前的安裝工作

如果想要在 Windows 系統內順利使用 Selenium 執行工作,必須安裝下列三項工具以及一個設定。

  1. Selenium 工具
  2. 瀏覽器 : 使用 Selenium 市面上最常使用的是安裝 Firefox,也可以是Chrome。本文將以 Firefox 為主要說明,另外也會說明安裝 Chrome方式。
  3. 驅動程式 : 這是指 Selenium 驅動瀏覽器的程式,其實這部分資訊很重要,卻是目前極少文件有說明,因此常造成讀者學習上的障礙,因為依照一般說明結果,是錯誤訊息。

安裝 Selenium

安裝

pip install selenium

導入

from selenium import webdriver

安裝驅動程式

Selenium 需要一個驅動程序來與所選瀏覽器互動。 例如 : Firefox,需要 geckodriver,它需要在運行以下範例之前安裝。 確保它在您的 PATH 中,例如 : 將它放在 /usr/bin 或 /usr/local/bin 中。

若少安裝這一步會給你一個錯誤 "error selenium.common.exceptions.WebDriverException: Message: ‘geckodriver’ executable needs to be in PATH."

其他受支持的瀏覽器將有自己的驅動程序可用。 一些更流行的瀏覽器驅動程序的鏈接如下:

驅動程式的安裝需要幾個步驟

  1. 下載與解壓縮。
  2. 將驅動程式放在 PATH 路徑內。
  3. 將驅動程式路徑放在 Python 程式內。

Firefox 安裝驅動程式為例

點選 Firefox驅動程式的連結 => 選擇 geckodriver-v0.30.0-win64.zip 的版本 => 下載存放路徑, 這裡我們的驅動程式路徑為 C:\driver ,未來這個檔案路徑配合參數設定,放在 webdriver.Firefox() 內,就可正確執行了。

Chrome 安裝驅動程式為例

點選 Chrome 驅動程式的連結 => 選擇 ChromeDriver 95.0.4638.17 的版本 => 下載存放路徑, 這裡我們的驅動程式路徑為 C:\driver ,未來這個檔案路徑配合參數設定,放在 webdriver.Chrome() 內,就可正確執行了。


獲得webdriver 的物件型態

使用 Selenium 的第一步是獲得 webdriver 物件。

Firefox 瀏覽器為例

範例 pythonSelenium-02.py : 列出 webdriver 物件型態。

# pythonSelenium-02.py
from selenium import webdriver

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
print(type(browser))

執行結果

<class 'selenium.webdriver.firefox.webdriver.WebDriver'>

這個程式在執行時,螢幕將出現 'c:\driver\geckodriver.exe' 視窗,讀者可以不用理會。接著,會啟動 Firefox 視窗,因為我們沒有設定找尋任何網頁,所以視窗是空白的,不過在 Python Shell 視窗可以看到程式的執行結果。

上述程式的重點是第5行,我們將參數 “executable_path=driverPath” 當作參數設在 webdriver.Firefox() 內,driverPath 主要是設定驅動程式的位置在第4行設定,如果不這樣,也可以將 c:\driver 路徑設在系統 PATH 內,此時可以省略第4行和 webdriver.Firefox() 參數設定。最後程式印出來變數 browser 的物件類別

Chrome 瀏覽器為例

範例 pythonSelenium-03.py : 列出 webdriver 物件型態。

# pythonSelenium-03.py
from selenium import webdriver

dirverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Chrome(dirverPath)
print(type(browser))

執行結果

<class 'selenium.webdriver.chrome.webdriver.WebDriver'>

這個程式在執行時,螢幕將出現 'c:\driver\chromedriver.exe' (這是筆者放置 chromedriver.exe 檔案路徑)的視窗,讀者可以不必理會,接著會啟動 Chrome 視窗,因為我們沒有設定找尋任何網頁,所以視窗是空白的,不過 Python Shell 視窗可以看到程式的執行結果。

上述程式的重點是第5行,我們將參數 "driverPath" 當作參數設在 webdrive.Chrome() 內,driverPath 主要是設定驅動程式的檔案路徑在第4行設定。最後程式印出來變數 browser 的物件類別。

擷取網頁

獲得 browser 物件後,可以使用 get() 讓瀏覽器連上網頁。

範例 pythonSelenium-04.py : 讓瀏覽器連上網頁與列印網頁標題。

# pythonSelenium-04.py
from selenium import webdriver

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'https://icook.tw/'
browser.get(url)                # 網頁下載至瀏覽器

執行結果

由於上述程式沒有輸出任何資料,所以 Python Shell 視窗沒有任何結果,另外,由 webbrowser 物件啟動的 Firefox 視窗將可以看到所載入的網頁。下列是 webdriver 常見的屬性 :

  • name : 瀏覽器名稱
  • title : 網頁標題
  • page_source : 網頁的原始碼
  • current_url : 目前的網址
  • session_id : 網頁連線ID
  • capabilities : 瀏覽器功能設定

下列是常見的方法,對於所取得的瀏覽器位置與大小皆是以字典方式顯示結果 ,(x, y) 是座標, (width, height) 是大小。

  • get_window_position() : 取得瀏覽器視窗左上角位置
  • set_window_position() : 設定(x, y) 為瀏覽器視窗左上角位置
  • get_window_size() : 取得瀏覽器視窗大小
  • set_window_size(x, y) : 設定 (x, y) 為瀏覽器視窗大小
  • maximize_window() : 設定瀏覽器視窗最大化

範例 pythonSelenium-04-1.py : 列出網頁的 HTML 原始碼 。

# pythonSelenium-04-1.py
from selenium import webdriver

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'https://icook.tw/'
browser.get(url)                # 網頁下載至瀏覽器
print(browser.page_source)      # 列印網頁原始碼

執行結果

Firefox 瀏覽器會打開網頁 https://icook.tw/ ,並且列印網頁原始碼

範例 pythonSelenium-04-2.py : 列出 name、current_url、session_id 和 capabilities 屬性。

# pythonSelenium-04-1.py
from selenium import webdriver

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'https://icook.tw/'
browser.get(url)                # 網頁下載至瀏覽器
print(browser.page_source)      # 列印網頁原始碼

執行結果

瀏覽器名稱 =  firefox
網頁url    =  https://icook.tw/
網頁連線id =  ff973e9c-212b-4f1f-8a0a-c73aa97b0f33
瀏覽器功能 =
 {'acceptInsecureCerts': True, 'browserName': 'firefox', 'browserVersion': '93.0', 'moz:accessibilityChecks': False, 'moz:buildID': '20210927210923', 'moz:debuggerAddress': 'localhost:52392', 'moz:geckodriverVersion': '0.30.0', 'moz:headless': False, 'moz:processID': 7704, 'moz:profile': 'C:\\Users\\Administrator\\AppData\\Local\\Temp\\rust_mozprofileDZ6B33', 'moz:shutdownTimeout': 60000, 'moz:useNonSpecCompliantPointerOrigin': False, 'moz:webdriverClick': True, 'pageLoadStrategy': 'normal', 'platformName': 'windows', 'platformVersion': '10.0', 'proxy': {}, 'setWindowRect': True, 'strictFileInteractability': False, 'timeouts': {'implicit': 0, 'pageLoad': 300000, 'script': 30000}, 'unhandledPromptBehavior': 'dismiss and notify'}

範例 pythonSelenium-04-3.py : 每隔5秒瀏覽一個網站。

# pythonSelenium-04-3.py
from selenium import webdriver
import time

urls = ['https://icook.tw/',
        'http://www.mcut.edu.tw',
        'http://www.siliconstone.com']

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)

for url in urls:
    browser.get(url)                # 網頁下載至瀏覽器
    time.sleep(5)

browser.quit()

尋找 HTML 文件的元素

使用 Selenium 建立 browser 物件時,可以使用下列方法獲得 HTML 文件的元素(WebElement),在下列方法中 find_element_* 可以找到一個符合的元素,find_elements_* 則可以找到所有相符的元素同時用串列傳回。

  • find_element_by_id(id) : 傳回第一個相符ID的元素。
  • find_elements_by_id(id) : 傳回所有相符的ID的元素,以串列的方式傳回。
  • find_element_by_class_name(name) : 傳回第一個相符 Class 的元素。
  • find_elements_by_class_name(name) : 傳回所有相符的 Class 的元素,以串列方式傳回。
  • find_element_by_name(name) : 傳回第一個相符 name 屬性的元素。
  • find_elements_by_name(name) : 傳回所有相符的 name 屬性的元素,以串列方式傳回。
  • find_element_by_css_selector(selector) : 傳回第一個相符 CSS Selector 的元素。
  • find_elements_by_css_selector(selector) : 傳回所有相符的 CSS Selector 的元素,以串列方式傳回。
  • find_element_by_partial_link_text(text) : 傳回第一個內含有 text 的 <a> 元素。
  • find_elements_by_partial_link_text(text) : 傳回所有內含相符 text 的 <a> 元素,以串列方式傳回。
  • find_element_by_link_text(text) : 傳回第一個完全相同 text  的  <a> 元素。
  • find_elements_by_link_text(text) : 傳回所有完全相同 text  的  <a> 元素,以串列方式傳回。
  • find_element_by_tag_name(name) : 不區分大小寫,傳回第一個相符name 的元素,例如 : <p> 與 <P> 是一樣的。
  • find_elements_by_tag_name(name) : 不區分大小寫,傳回所有相符的name 的元素,以串列方式傳回,例如 : <p> 與 <P> 是一樣的。

上述方法如果沒有找到相符的,會產生 NoSuchElement 異常,如果我們期待沒有找到時,程式不要列出錯誤而結束,可以使用 try…..except 執行例外處理。

找到 HTML 元素物件後,可以使用下列方式方法或屬性獲得 HTML 元素物件的內容。

  • tag_name : 元素名稱。
  • text : 元素內容。
  • location : 這是字典,內含有 x 和 y 鍵值,表示元素在頁面上的座標。
  • clear() : 可以刪除在文字 (text) 欄位和文字區域 (textarea) 欄位的文字。
  • get_attribute(name) : 可以獲得這個元素 name 屬性的值。
  • is_displayed() : 如果元素可以看到傳回 True,否則傳回 False。
  • is_enabled() : 如果元素是可以立即使用則傳回 True,否則傳回 False。
  • is_selected() : 如果元素的核取方塊有勾選則傳回 True,否則傳回 False。

 範例 pythonSelenium-05.py : 找不到符合條件的元素,造成程式結束的範例 。

# pythonSelenium-05.py
from selenium import webdriver

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'https://icook.tw/'
browser.get(url)                # 網頁下載至瀏覽器

tag = browser.find_element_by_id('main')
print(tag.tag_name)

可以使用下列方式處理。

範例 pythonSelenium-06.py : 找不到符合條件的元素,執行例外處理  。

# pythonSelenium-06.py
from selenium import webdriver

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'https://icook.tw/'
browser.get(url)                # 網頁下載至瀏覽器

try:
    tag = browser.find_element_by_id('main')
    print(tag.tag_name)
except:
    print("沒有找到相符的元素")

執行結果

沒有找到相符的元素

範例 pythonSelenium-07.py : 抓取不同元素的應用  。

# pythonSelenium-07.py
from selenium import webdriver

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'http://127.0.0.1:5500/htmlExampleBS4-02.html'
browser.get(url)                # 網頁下載至瀏覽器

print("網頁標題內容是 = ", browser.title)

tag2 = browser.find_element_by_id('header')             # 傳回<h1 id='header'>
print("\n標籤名稱 = %s, 內容是 = %s " % (tag2.tag_name, tag2.text))

tag4 = browser.find_elements_by_tag_name('p')           # 傳回<p>
for t4 in tag4:
    print("標籤名稱 = %s, 內容是 = %s " % (t4.tag_name, t4.text))

tag5 = browser.find_elements_by_tag_name('img')         # 傳回<img>
for t5 in tag5:
    print("標籤名稱 = %s, 內容是 = %s " % (t5.tag_name, t5.get_attribute('src')))

執行結果

tag2 = browser.find_element_by_id('header')  # 傳回<h1 id='header'>
標籤名稱 = h1, 內容是 = 這是表頭區塊
tag4 = browser.find_elements_by_tag_name('p')  # 傳回<p>
標籤名稱 = p, 內容是 = 人物介紹-1
標籤名稱 = p, 內容是 = 人物介紹-2
標籤名稱 = p, 內容是 = 人物介紹-2
tag5 = browser.find_elements_by_tag_name('img')    # 傳回<img>
標籤名稱 = img, 內容是 = http://127.0.0.1:5500/media/components/user-1.jpg
標籤名稱 = img, 內容是 = http://127.0.0.1:5500/media/components/user-2.jpg
標籤名稱 = img, 內容是 = http://127.0.0.1:5500/media/components/user-3.jpg

XPath 語法

在前一小節,我們使用了 find_element_by 系列指令,應用在搜尋網頁的節點,其實可以發現我們搜尋了 id、name、class,如果 HTML 文件沒有這些則搜尋會有困難。這一節,我們將介紹相對路徑方式搜尋網頁的節點 find_element_by_xpath() 的方法。

XPath 全名是 XML Path,主要是可以用來查詢 HTML/XML 文件的所有元素,在 XPath 中所有的 HTML/XML 元素皆是一個節點,整個文件是一個樹狀結構,本書所述是使用 XPath 查詢 HTML 文件的節點,這個觀念也可以應用在 XML 文件。

認識HTML的架構 

範例 htmlExampleBS4-02.html : 簡單的 HTML 文件。

<!doctype html>
<html lang="zh-Hant-TW">
  <head>
    <meta charset="utf-8">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <title>htmlExampleBS4-02.html</title>
    <style>
      header {
        background-color: rgb(218, 180, 231);
        min-height: 100px;      }
      main {
        background-color: rgb(239, 238, 238);
        min-height: 300px;      }
      footer {
        min-height: 80px;      }
    </style>
  </head>
  <body>
    <header class="d-flex justify-content-center align-items-center">
      <h1 class="text-center" id='header'>這是表頭區塊</h1>
    </header>
    <main>
      <h1 class="text-center">這是主要內容區</h1>
      <section class="d-flex justify-content-center align-items-center">
        <div class="card" style="width: 18rem;">
          <img src="media/components/user-1.jpg" class="card-img-top" alt="card-1">
          <div class="card-body">
            <p class="card-text text-center" data-author='author1'>人物介紹-1</p>
          </div>
        </div>
        <div class="card" style="width: 18rem;">
          <img src="media/components/user-2.jpg" class="card-img-top" alt="card-2">
          <div class="card-body">
            <p class="card-text text-center" data-author='author2'>人物介紹-2</p>
          </div>
        </div>
        <div class="card" style="width: 18rem;">
          <img src="media/components/user-3.jpg" class="card-img-top" alt="card-2">
          <div class="card-body">
            <p class="card-text text-center" data-author='author3'>人物介紹-2</p>
          </div>
        </div>
      </section>
    </main>
    <footer class="bg-dark text-white d-flex justify-content-center align-items-center">
      <h3 class="text-center">這是表尾</h3>
    </footer>
    <!-- Bootstrap Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
      crossorigin="anonymous"></script>
  </body>
</html>

執行結果

此範例所有節點的樹狀結構如下:

往們將使用上圖來講解各節點的觀念。

  • 根節點 ( Root Node ) : <html> 就是根結點。
  • 父節點 ( Parent Node ) : <html> 是 <head> 和 <body> 的父節點。
  • 子節點 ( Children Node ) : <head> 和 <body> 是 <html> 的子節點。
  • 相鄰節點 ( Sibling Node ) : <head> 和 <body> 有共同的 <html> 父節點,所以彼此為相鄰節點。 <header> 和 <main> 有共同的 <body> 父節點,所以他們也是為相鄰節點。

絕對路徑與相對路徑

XPath 用絕對路徑與相對路徑標示各節點,其中 "/" 標示為絕對路徑, "//" 標示為相對路徑,這兩個標示可以交互使用。一般來說 "//" 相對路徑較常使用。我們以下圖示說明絕對路徑與相對路徑 : 

範例 pythonSelenium-08.py : 使用 htmlExampleBS4-02.html 驗證相對路徑節點 。

# pythonSelenium-08.py
from selenium import webdriver

driverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Chrome(executable_path=driverPath)
url = 'F:\GoogleDrive\Coding\CodeIndex\Example-Python\Examples\htmlExampleBS4-02.html'
browser.get(url)                # 網頁下載至瀏覽器

n1 = browser.find_element_by_xpath('//h1')
print(n1.text)
n2 = browser.find_element_by_xpath('//body/header/h1')
print(n2.text)
n3 = browser.find_element_by_xpath('//header/h1')
print(n3.text)
n4 = browser.find_element_by_xpath('//body/*/h1')
print(n4.text) 

執行結果

  n1 = browser.find_element_by_xpath('//h1')
這是表頭區塊
  n2 = browser.find_element_by_xpath('//body/*/h1')
這是表頭區塊
  n3 = browser.find_element_by_xpath('//header/h1')
這是表頭區塊
  n4 = browser.find_element_by_xpath('//body/*/h1')
這是表頭區塊

範例 pythonSelenium-08-1.py : 使用 htmlExampleBS4-02.html 找出第一個 <p> ,<p>元素共有3個,第一個是 "人物介紹-1" 。

# pythonSelenium-08-1.py
from selenium import webdriver

driverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Chrome(executable_path=driverPath)
url = 'F:\GoogleDrive\Coding\CodeIndex\Example-Python\Examples\htmlExampleBS4-02.html'
browser.get(url)                # 網頁下載至瀏覽器

n1 = browser.find_element_by_xpath('//p')
print(n1.text)

執行結果

  n1 = browser.find_element_by_xpath('//p')
人物介紹-1

索引爬取重複的元素

在 htmlExampleBS4-02.html  文件中,我們有3個 <p> 元素,上面我們只列出第一個元素,如果我們想列出不同的元素,可以使用索引方式處理。

範例 pythonSelenium-08-2.py : 在 htmlExampleBS4-02.html 文件中<p>元素共有3個,找出第1、2個 <p> 元素 。

# pythonSelenium-08-2.py
from selenium import webdriver

driverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Chrome(executable_path=driverPath)
url = 'F:\GoogleDrive\Coding\CodeIndex\Example-Python\Examples\htmlExampleBS4-02.html'
browser.get(url)                # 網頁下載至瀏覽器

n1 = browser.find_element_by_xpath("//section/div[1]/div/p")
print(n1.text)
n2 = browser.find_element_by_xpath("//section/div[2]/div/p")
print(n2.text)

執行結果

  n1 = browser.find_element_by_xpath("//section/div[1]/div/p")
人物介紹-1
  n2 = browser.find_element_by_xpath("//section/div[2]/div/p")
人物介紹-2

元素的屬性

在 htmlExampleBS4-02.html  文件中,有3個 <p> 元素,第2個 <p> 元素內有 data-author='author2' ,我們稱 "data-author" 為屬性 attribute, 第3個 <p> 元素內有 data-author='author3',我們可以使用屬性列印出我們想要的節點。

範例 pythonSelenium-08-3.py : 在 htmlExampleBS4-02.html 文件中<p>元素共有3個,找出第2、3個 <p> 元素 。

# pythonSelenium-08-3.py
from selenium import webdriver

driverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Chrome(executable_path=driverPath)
url = 'F:\GoogleDrive\Coding\CodeIndex\Example-Python\Examples\htmlExampleBS4-02.html'
browser.get(url)                # 網頁下載至瀏覽器

n1 = browser.find_element_by_xpath("//section/*/*/p[@data-author='author2']")
print(n1.text)
n1 = browser.find_element_by_xpath("//section/*/*/p[@data-author='author3']")
print(n1.text)

執行結果

  n1 = browser.find_element_by_xpath("//section/*/*/p[@data-author='author2']")
人物介紹-2
  n1 = browser.find_element_by_xpath("//section/*/*/p[@data-author='author3']")
人物介紹-3

列出屬性值

範例 pythonSelenium-08-4.py : 在 htmlExampleBS4-02.html 文件中,列出圖片的完整路徑。

# pythonSelenium-08-4.py
from selenium import webdriver

driverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Chrome(executable_path=driverPath)
url = 'F:\GoogleDrive\Coding\CodeIndex\Example-Python\Examples\htmlExampleBS4-02.html'
browser.get(url)                # 網頁下載至瀏覽器

pict = browser.find_element_by_xpath("//section/div/img")
print(pict.get_attribute('src'))

執行結果

  pict = browser.find_element_by_xpath("//section/div/img")
file:///F:/GoogleDrive/Coding/CodeIndex/Example-Python/Examples/media/components/user-1.jpg

範例 pythonSelenium-08-5.py : 以 htmlExampleBS4-02.html 為例,說明屬性的用法。

# pythonSelenium-08-5.py
from selenium import webdriver

driverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Chrome(executable_path=driverPath)
url = 'F:\GoogleDrive\Coding\CodeIndex\Example-Python\Examples\htmlExampleBS4-02.html'
browser.get(url)                # 網頁下載至瀏覽器

n1 = browser.find_element_by_xpath("//h1/em")
print('em          : ', n1.text)
n2 = browser.find_element_by_xpath("//h1")
print('h1          : ', n2.text)
n3 = browser.find_element_by_xpath("//h1")
print('textContent : ', n3.get_attribute('textContent'))
n4 = browser.find_element_by_xpath("//h1")
print('innerHTML : ', n4.get_attribute('innerHTML'))
n5 = browser.find_element_by_xpath("//h1")
print('outerHTML : ', n5.get_attribute('outerHTML'))

執行結果

  n1 = browser.find_element_by_xpath("//h1/em")
em :  表頭區塊
  n2 = browser.find_element_by_xpath("//h1")
h1 :  這是表頭區塊
  n3 = browser.find_element_by_xpath("//h1")
textContent :  這是表頭區塊
  n4 = browser.find_element_by_xpath("//h1")
innerHTML :  這是<em>表頭區塊</em>
  n5 = browser.find_element_by_xpath("//h1")
outerHTML :  <h1 class="text-center" id="header">這是<em>表頭區塊</em></h1>

contains()

當網頁有多個元素重複時,XPath 也包含 contains() 方法讓我們找出包含某個字串的元素節點。

範例 pythonSelenium-08-6.py : 以 htmlExampleBS4-02.html 為例,使用 contains() 方法,找出節點的 outerHTML 和 data-author 屬性。

# pythonSelenium-08-6.py
from selenium import webdriver

driverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Chrome(executable_path=driverPath)
url = 'F:\GoogleDrive\Coding\CodeIndex\Example-Python\Examples\htmlExampleBS4-02.html'
browser.get(url)                # 網頁下載至瀏覽器

n = browser.find_element_by_xpath("//div[@class='card-body']//p[contains(text(),'人物介紹-2')]")
print(n.get_attribute('outerHTML'))
print(n.get_attribute('data-author'))

執行結果

<p class="card-text text-center" data-author="author2">人物介紹-2</p>
author2

隱藏參數與等待網頁載入

在先前程式設計時,每次就會開啓 Chrome 瀏覽器,顯示所開啟的網頁,若是不喜歡看到開啟網頁,可以增加設定 “headless” 隱藏參數,未來程式執行時,可以不用再開啟瀏覽器。相關實作可以參考下列範例第5-7行。

另外有的程式設計師喜歡在 get(url) 前先使用 implicitly_wait(seconds) 指令,等待網頁載入,再執行 get(url) 。如果參數設定等待5秒,實際只需3秒即可載入網頁,程式也可以在網頁下載完成立即執行 get(url) 指令,相關實作可以參考下列範例第9行。

範例 pythonSelenium-08-7.py : 以重新設計 範例 pythonSelenium-08-6.py ,增加隱藏參數與等待網頁載入設定。

# pythonSelenium-08-7.py
from selenium import webdriver

driverPath = 'c:\driver\chromedriver.exe'
headless = webdriver.ChromeOptions()
headless.add_argument('headless')   # 隱藏參數
browser = webdriver.Chrome(executable_path=driverPath, options=headless)
url = 'F:\GoogleDrive\Coding\CodeIndex\Example-Python\Examples\htmlExampleBS4-02.html'
browser.implicitly_wait(5)          # 等待網頁載入
browser.get(url)                    # 網頁下載至瀏覽器

n = browser.find_element_by_xpath("//div[@class='card-body']//p[contains(text(),'人物介紹-2')]")
print(n.get_attribute('outerHTML'))
print(n.get_attribute('data-author'))

執行結果

結果同範例 pythonSelenium-08-6.py,並且不再出現瀏覽器。

進入Chrome 開發工具觀察 XPath 運作

使用 Chrome 開啟 htmlExampleBS4-02.html 網頁,按 F12 進入開發模式 => 1.點選 Elements => 同時按下 Ctrl + F 鍵,會出現搜尋欄位 => 2. 輸入 : "//section/div[2]",會發現搜尋欄右邊出現搜尋結果的數量,並會指到所蒐尋的第一個元素 => 3.並在左邊有灰階區域顯示、右邊綠色區塊為元素代碼。

Chrome 外掛套件 ChroPath

有了 ChroPath ,當你再開發環境點選一個元素,就會自動產生 XPath,請 Google 搜尋 "chropath extension"

點選他就可以進入chrome 線上應用程式商店 =>按 "加到 Chrome" =>  按 "新增擴充功能"


當你在左方網頁或右上方點選元素時,ChroPath 區域框就會出現以下資訊 :

  • Rel XPath : 點選元素的相對路徑。
  • Abx XPath : 點選元素的絕對路徑。
  • CSS Selectors :  CSS Selectors 路徑

用 Python 控制點選超連結

使用 尋找 HTML 文件的元素  方式傳回 Web Element 元素時,可以使用 click() 方法,如果執行此方法,相當於我們點選這個傳回的元素,如果傳回的元素是超連結的文字,這樣可以產生按此超連結的結果。

範例 pythonSelenium-09.py : 進入深智數位網頁,經過5秒後(第12行),程式設計自動點選 "深智數位緣起" 超連結,我們設計程式暫停5秒,主要是讓讀者可以體會網頁的變化。

# pythonSelenium-09.py
from selenium import webdriver
import time

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'https://deepmind.com.tw/'
browser.get(url)                # 網頁下載至瀏覽器

eleLink = browser.find_element_by_link_text('深智數位緣起')
print(type(eleLink))            # 列印eleLink資料類別
time.sleep(5)                   # 暫停5秒
eleLink.click()

執行結果

<class 'selenium.webdriver.remote.webelement.WebElement'>

使用 Python 填寫表單和送出

使用 HTML 原始碼

許多網頁有填寫表單欄位,碰上這類問題,我們可以使用 HTML 原始碼分析輸入欄位的相關資訊。

我們可以開啟網頁 HTML 原始碼找尋 <input> 元素 id 元素 或是 name,然後使用 send_keys() 方法,就可以填寫表單。填寫完表單後可以使用 submit() 方法,將表單送出

以下是以台灣證券交易所網頁 HTML 檔案為實例解說。

從以上可以看到 <input> 元素 name 屬性,是上市公司整合資訊的搜尋的輸入欄位,所以可以用下列方式設計。

範例 pythonSelenium-10.py : 用 Python 填寫表單,所填寫的表單是搜尋 "2330",本程式會經過5秒自動送出,我們在執行結果中印出來填寫表單以及送出的結果 。

# pythonSelenium-10.py
from selenium import webdriver
import time

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'https://www.twse.com.tw/zh/'
browser.get(url)                    # 網頁下載至瀏覽器

txtBox = browser.find_element_by_name('stockNo')
txtBox.send_keys('2330')          # 輸入表單資料
time.sleep(5)                       # 暫停5秒
txtBox.submit()                     # 送出表單

執行結果 : 上述表單是自動填寫,經過5秒後可以得到下列結果 :

了解上述自動填寫表單和送出功能,未來熱門的演唱會門票、過年搶破頭的高鐵車票,就讓 Python 處理吧 !

用 Python 處理使用網頁的特殊按鍵

在欣賞、閱讀網頁時,有時往往需要捲動網頁或是使用特殊鍵,這些特殊鍵無法用 Python 輸入,不過 Python 有提供下列模組,可以方便我們操作。

selenium.webdriver.common.keys

使用時的導入語法如下

from selenium.webdriver.common.keys import Keys

經過上述宣告,未來可以使用 Keys 呼叫相關屬性,下列是常見的屬性內容 :

  • ENTER/RETURN : 相當於鍵盤的 Enter 和 Return。
  • PAGE_DOWN/PAGE_UP/HOME/END : 相當於鍵盤的 PAGE_DOWN,PAGE_UP、HOME、END。
  • UP/DOWN/LEFT/RIGHT : 相當於鍵盤的 上、下、左、右 方向鍵。

使用方式是在前面加上 "Keys" ,例如 : Keys.HOME

範例 pythonSelenium-11.py : 這個程式在執行時,首先顯示最上方的網頁內容,經過3秒後會往下捲動一頁,再過3秒會捲動到最下方,經過3秒可以往上捲動,再過3秒可以將網頁捲動到最上方。程式第10行,先搜尋 “body” ,這是網頁設計主體的開始標籤,相當於在網頁的最上方 。

# pythonSelenium-11.py
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'https://www.twse.com.tw/zh/'
browser.get(url)                    # 網頁下載至瀏覽器
ele = browser.find_element_by_tag_name('body')
time.sleep(3)
ele.send_keys(Keys.PAGE_DOWN)       # 網頁捲動到下一頁
time.sleep(3)
ele.send_keys(Keys.END)             # 網頁捲動到最底端
time.sleep(3)
ele.send_keys(Keys.PAGE_UP)         # 網頁捲動到上一頁
time.sleep(3)
ele.send_keys(Keys.HOME)            # 網頁捲動到最上端

執行結果 : 每次間隔3秒,讀者可以觀察頁面內容的捲動方式。

用 Python 處理瀏覽器運作

常見的運作有下列方法

  • forward() : 往前一頁。
  • back() : 往後一頁。
  • refresh() : 更新網頁。
  • quit() : 關閉網頁,相當於關閉瀏覽器。

上述必須用 Firefox 瀏覽器物件啟動,也就是我們本文的變數 browser,例如 : browser.refresh() 可更新網頁,browser.quit() 可以關閉網頁。

範例 pythonSelenium-12.py : 更新網頁與關閉網頁的應用。

# pythonSelenium-12.py
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
url = 'https://www.twse.com.tw/zh/'
browser.get(url)                    # 網頁下載至瀏覽器

time.sleep(3)
browser.refresh()                   # 更新網頁
time.sleep(3)
browser.quit()                      # 關閉網頁

執行結果 : 網頁下載後3秒可以更新網頁內容,再過3秒後關閉瀏覽器。

自動進入 Google 系統

Google 有提供免費 Email 與一系列的服務,我們可以藉由輸入帳號與密碼方式進入系統,然後使用 Google 的所有服務,我們將一步一步引導讀者設計程式可以自動進入 Google 的系統,其實這一節主要觀念是我們將帶領讀者練習和網站做互動。

首先請輸入Google 網址 : https://www.google.com/

這時在視窗右上方可以看到 登入 按鈕 => 請將滑鼠移至按鈕再按滑鼠右鍵,可以看到下拉視窗 => 請點選 Inspect 檢查 => 可以看到下列視窗畫面 :

範例 pythonSelenium-13.py : 設計自動登入 Google 的系統。

from selenium import webdriver
import time

url = 'https://www.google.com'
email = input('請輸入你的Google Email的帳號 : ')
pwd = input('請輸入你的Google Email的密碼 : ')

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
browser.get(url)                    # 網頁下載至瀏覽器

browser.find_element_by_link_text('登入').click()

執行結果 : 網頁出現以下畫面。

以上,我們已經進入登入 Google 帳號畫面,接下來需要解析輸入郵件地址 => 將滑鼠移至輸入郵件地址欄位 => 請按滑鼠右鍵,可以看到下拉視窗 => 請點選 Inspect 檢查 => 可以看到下列視窗畫面 如下 :

我們可以看到 <input> 元素內的 id 是 "identifierId"。

範例 pythonSelenium-14.py : 設計自動登入 Google 的系統,繼續 範例 pythonSelenium-13.py ,增加可以輸入Email的帳號。

# pythonSelenium-14.py
from selenium import webdriver
import time

url = 'https://www.google.com'
email = input('請輸入你的Google Email的帳號 : ')
pwd = input('請輸入你的Google Email的密碼 : ')

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
browser.get(url)                    # 網頁下載至瀏覽器

browser.find_element_by_link_text('登入').click()
browser.find_element_by_id('identifierId').send_keys(email) # 輸入帳號
time.sleep(3)

執行結果 : 網頁出現以下面。

接下來,我們必須解析 "繼續" 按鈕,重複類似以上步驟。

範例 pythonSelenium-15.py : 設計自動登入 Google 的系統,繼續 範例 pythonSelenium-14.py ,增加 "繼續" 按鈕。

# pythonSelenium-15.py
from selenium import webdriver
import time

url = 'https://www.google.com'
email = input('請輸入你的Google Email的帳號 : ')
pwd = input('請輸入你的Google Email的密碼 : ')

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
browser.get(url)                    # 網頁下載至瀏覽器

browser.find_element_by_link_text('登入').click()
browser.find_element_by_id('identifierId').send_keys(email) # 輸入帳號
time.sleep(3)

# 按繼續鈕
browser.find_element_by_xpath("//span[@class='VfPpkd-vQzf8d']").click()
time.sleep(3)
time.sleep(3)

接下來,我們必須解析輸入 "密碼" 欄位,重複類似以上步驟。

範例 pythonSelenium-16.py : 設計自動登入 Google 的系統,繼續 範例 pythonSelenium-15.py ,增加 輸入 "密碼" 欄位。

# pythonSelenium-16.py
from selenium import webdriver
import time

url = 'https://www.google.com'
email = input('請輸入你的Google Email的帳號 : ')
pwd = input('請輸入你的Google Email的密碼 : ')

driverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
browser.get(url)                    # 網頁下載至瀏覽器

browser.find_element_by_link_text('登入').click()
browser.find_element_by_id('identifierId').send_keys(email) # 輸入帳號
time.sleep(3)

# 按繼續鈕
browser.find_element_by_xpath("//span[@class='VfPpkd-vQzf8d']").click()
time.sleep(3)

# 輸入密碼
browser.find_element_by_xpath("//input[@type='password']").send_keys(pwd)
time.sleep(3)

接下來,我們必須解析輸入 "密碼" 後的 "繼續" 按紐,重複類似以上步驟。

範例 pythonSelenium-17.py : 設計自動登入 Google 的系統,繼續 範例 pythonSelenium-16.py ,增加  "繼續" 按鈕。

# pythonSelenium-17.py
from selenium import webdriver
import time

url = 'https://www.google.com'
email = input('請輸入你的Google Email的帳號 : ')
pwd = input('請輸入你的Google Email的密碼 : ')

driverPath = 'c:\driver\chromedriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
browser.get(url)                    # 網頁下載至瀏覽器

browser.find_element_by_link_text('登入').click()
browser.find_element_by_id('identifierId').send_keys(email) # 輸入帳號
time.sleep(3)

# 按繼續鈕
browser.find_element_by_xpath("//span[@class='VfPpkd-vQzf8d']").click()
time.sleep(3)

# 輸入密碼
browser.find_element_by_xpath("//input[@type='password']").send_keys(pwd)
time.sleep(3)

# 按繼續鈕
browser.find_element_by_xpath("//span[@class='RveJvd snByac']").click()
time.sleep(3)

們就會看到登入 Google 的網頁如下,右上方已經改為我們的名字與圖示。

自動化下載環保署空氣品質資料

範例 pythonSelenium-18.py : 自動化下載環保署空氣品質資料。

# pythonSelenium-18.py
from selenium import webdriver
import time

url = 'https://data.epa.gov.tw/dataset/aqx_p_434/resource/b50c2109-6b1b-4413-9037-658760f7c969'

driverPath = 'c:\driver\geckodriver.exe'
browser = webdriver.Firefox(executable_path=driverPath)
browser.get(url)                    # 網頁下載至瀏覽器

browser.find_element_by_link_text('JSON').click()      # 按JSON鈕
time.sleep(5)

browser.find_element_by_link_text('XML').click()        # 按XML鈕
time.sleep(5)

browser.find_element_by_link_text('CSV').click()        # 按CSV鈕
time.sleep(5)


參考資料

特色、摘要,Feature、Summary:

關鍵字、標籤,Keyword、Tag:

  • Web-Crawler,Data-Mining,Data-Science,Python,

留言

這個網誌中的熱門文章

Ubuntu 常用指令、分類與簡介

iptables的觀念與使用

網路設定必要參數IP、netmask(遮罩)、Gateway(閘道)、DNS

了解、分析登錄檔 - log

Python 與SQLite 資料庫

Blogger文章排版範本

Pandas 模組

如何撰寫Shell Script

查詢指令或設定 -Linux 線上手冊 - man

下載網頁使用 requests 模組