使用 Python 和 Selenium 進行現代 Web 抓取

已發表: 2022-03-11

幾乎從萬維網誕生之日起,Web 抓取就已用於從網站中提取數據。 在早期,抓取主要是在靜態頁面上完成的——那些具有已知元素、標籤和數據的頁面。

然而,最近,Web 開發中的先進技術使這項任務變得更加困難。 在本文中,我們將探討在新技術和其他因素阻止標準抓取的情況下如何進行數據抓取。

傳統數據抓取

由於大多數網站生成的頁面旨在供人類閱讀而不是自動閱讀,因此網頁抓取主要包括以編程方式消化網頁的標記數據(想想右鍵單擊,查看源代碼),然後檢測該數據中允許程序運行的靜態模式“讀取”各種信息並將其保存到文件或數據庫中。

數據抓取

如果要找到報告數據,通常可以通過使用 URL 傳遞表單變量或參數來訪問數據。 例如:

 https://www.myreportdata.com?month=12&year=2004&clientid=24823

Python 已成為最流行的網絡抓取語言之一,部分原因是為其創建了各種網絡庫。 一個流行的庫,Beautiful Soup,旨在通過允許搜索、導航和修改標籤(即解析樹)從 HTML 和 XML 文件中提取數據。

基於瀏覽器的抓取

最近,我有一個看起來很簡單的抓取項目,我已經做好了使用傳統抓取來處理它的充分準備。 但隨著深入,我發現了傳統方法無法克服的障礙。

三個主要問題使我無法使用標準的抓取方法:

  1. 證書。 需要安裝證書才能訪問數據所在的網站部分。 訪問初始頁面時,出現提示,要求我選擇計算機上安裝的證書的正確證書,然後單擊“確定”。
  2. 框架。 該網站使用了 iframe,這弄亂了我的正常抓取。 是的,我可以嘗試查找所有 iframe URL,然後構建站點地圖,但這似乎會變得笨拙。
  3. JavaScript。 在填寫帶有參數(例如,客戶 ID、日期範圍等)的表格後訪問數據。 通常,我會繞過表單並簡單地將表單變量(通過 URL 或作為隱藏表單變量)傳遞到結果頁面並查看結果。 但在這種情況下,表單包含 JavaScript,它不允許我以正常方式訪問表單變量。

所以,我決定放棄我的傳統方法,尋找一種可能的基於瀏覽器的抓取工具。 這將與正常工作不同——我不是直接進入頁面、下載解析樹並提取數據元素,而是“像人一樣”並使用瀏覽器訪問我需要的頁面,然後抓取數據 - 因此,繞過了處理提到的障礙的需要。

一般來說,Selenium 以 Web 應用程序的開源測試框架而聞名——使 QA 專家能夠執行自動化測試、執行回放和實現遠程控制功能(允許許多瀏覽器實例進行負載測試和多種瀏覽器類型)。 就我而言,這似乎很有用。

我的網絡抓取首選語言是 Python,因為它具有良好集成的庫,通常可以處理所需的所有功能。 果然,有一個用於 Python 的 Selenium 庫。 這將允許我實例化一個“瀏覽器”——Chrome、Firefox、IE 等——然後假裝我自己正在使用瀏覽器來訪問我正在尋找的數據。 如果我不希望瀏覽器實際出現,我可以在“無頭”模式下創建瀏覽器,使其對任何用戶都不可見。

項目設置

要開始試驗,我需要設置我的項目並獲得所需的一切。 我使用了一台 Windows 10 機器,並確保我有一個相對更新的 Python 版本(它是 v. 3.7.3)。 我創建了一個空白 Python 腳本,然後加載了我認為可能需要的庫,如果我還沒有加載庫,則使用 PIP(Python 的包安裝程序)。 這些是我開始使用的主要庫:

  1. 請求(用於發出 HTTP 請求)
  2. URLLib3(URL 處理)
  3. 美麗的湯(以防 Selenium 無法處理所有事情)
  4. Selenium(用於基於瀏覽器的導航)

我還向腳本添加了一些調用參數(使用 argparse 庫),以便我可以使用各種數據集,從命令行使用不同的選項調用腳本。 其中包括客戶 ID、從月/年和到月/年。

問題 1 – 證書

我需要做的第一個選擇是我要告訴 Selenium 使用哪個瀏覽器。 由於我通常使用 Chrome,並且它基於開源 Chromium 項目(Edge、Opera 和 Amazon Silk 瀏覽器也使用),所以我想我會先嘗試一下。

我可以通過添加我需要的庫組件在腳本中啟動 Chrome,然後發出幾個簡單的命令:

 # Load selenium components from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait, Select from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException # Establish chrome driver and go to report site URL url = "https://reportdata.mytestsite.com/transactionSearch.jsp" driver = webdriver.Chrome() driver.get(url)

由於我沒有以無頭模式啟動瀏覽器,因此瀏覽器實際上出現了,我可以看到它在做什麼。 它立即要求我選擇一個證書(我之前安裝的)。

首先要解決的問題是證書。 如何選擇合適的並接受它以進入網站? 在我對腳本的第一次測試中,我得到了這個提示:

數據抓取

這不好。 我不想在每次運行腳本時手動單擊“確定”按鈕。

事實證明,我能夠找到解決方法 - 無需編程。 雖然我希望 Chrome 能夠在啟動時傳遞證書名稱,但該功能並不存在。 但是,如果您的 Windows 註冊表中存在某個條目,Chrome 確實能夠自動選擇證書。 您可以將其設置為選擇它看到的第一個證書,或者更具體。 因為我只加載了一個證書,所以我使用了通用格式。

數據抓取

因此,使用該設置,當我告訴 Selenium 啟動 Chrome 並出現證書提示時,Chrome 將“自動選擇”證書並繼續。

問題 2 – iframe

好的,現在我在網站上,出現了一個表單,提示我輸入客戶 ID 和報告的日期範圍。

數據抓取

通過在開發人員工具 (F12) 中檢查表單,我注意到表單是在 iframe 中呈現的。 因此,在我開始填寫表單之前,我需要“切換”到表單所在的正確 iframe。 為此,我調用了 Selenium 的 switch-to 功能,如下所示:

 # Switch to iframe where form is frame_ref = driver.find_elements_by_tag_name("iframe")[0] iframe = driver.switch_to.frame(frame_ref)

很好,所以現在在正確的框架中,我能夠確定組件、填充客戶 ID 字段並選擇日期下拉列表:

 # Find the Customer ID field and populate it element = driver.find_element_by_name("custId") element.send_keys(custId) # send a test id # Find and select the date drop-downs select = Select(driver.find_element_by_name("fromMonth")) select.select_by_visible_text(from_month) select = Select(driver.find_element_by_name("fromYear")) select.select_by_visible_text(from_year) select = Select(driver.find_element_by_name("toMonth")) select.select_by_visible_text(to_month) select = Select(driver.find_element_by_name("toYear")) select.select_by_visible_text(to_year)

問題 3 – JavaScript

表單上唯一剩下的就是“單擊”“查找”按鈕,因此它將開始搜索。 這有點棘手,因為 Find 按鈕似乎是由 JavaScript 控制的,而不是普通的“提交”類型的按鈕。 在開發人員工具中檢查它,我找到了按鈕圖像,並且能夠通過右鍵單擊獲得它的 XPath。

數據抓取

然後,有了這些信息,我在頁面上找到了該元素,然後單擊它。

 # Find the 'Find' button, then click it driver.find_element_by_xpath("/html/body/table/tbody/tr[2]/td[1]/table[3]/tbody/tr[2]/td[2]/input").click()

瞧,表單提交了,數據出現了! 現在,我可以抓取結果頁面上的所有數據並根據需要保存。 或者我可以嗎?

獲取數據

首先,我必須處理搜索一無所獲的情況。 那很簡單。 它會在不離開的情況下在搜索表單上顯示一條消息,例如“未找到記錄”。 我只是搜索了那個字符串,如果找到它就停在那裡。

但如果結果確實出現了,數據會以加號 (+) 的形式顯示在 div 中,以打開交易並顯示其所有詳細信息。 已打開的事務顯示一個減號 (-),單擊該減號會關閉 div。 單擊加號將調用 URL 以打開其 div 並關閉任何打開的 div。

數據抓取

因此,有必要在頁面上找到任何加號,收集每個加號旁邊的 URL,然後遍歷每個加號以獲取每筆交易的所有數據。

 # Loop through transactions and count links = driver.find_elements_by_tag_name('a') link_urls = [link.get_attribute('href') for link in links] thisCount = 0 isFirst = 1 for url in link_urls: if (url.find("GetXas.do?processId") >= 0): # URL to link to transactions if isFirst == 1: # already expanded + isFirst = 0 else: driver.get(url) # collapsed +, so expand # Find closest element to URL element with correct class to get tran type tran_type=driver.find_element_by_xpath("//*[contains(@href,'/retail/transaction/results/GetXas.do?processId=-1')]/following::td[@class='txt_75b_lmnw_T1R10B1']").text # Get transaction status status = driver.find_element_by_class_name('txt_70b_lmnw_t1r10b1').text # Add to count if transaction found if (tran_type in ['Move In','Move Out','Switch']) and (status == "Complete"): thisCount += 1

在上面的代碼中,我檢索到的字段是交易類型和狀態,然後添加到計數中以確定有多少交易符合指定的規則。 但是,我可以檢索交易詳細信息中的其他字段,例如日期和時間、子類型等。

對於這個項目,計數被返回給調用應用程序。 但是,它和其他抓取的數據也可以存儲在平面文件或數據庫中。

其他可能的障礙和解決方案

使用您自己的瀏覽器實例抓取現代網站時可能會出現許多其他障礙,但大多數都可以解決。 這裡有幾個:

  • 試圖在它出現之前找到它

    在瀏覽自己時,您發現自己等待頁面出現的頻率是多少,有時是幾秒鐘? 好吧,以編程方式導航時也會發生同樣的情況。 你尋找一個類或其他元素——它不存在!

    幸運的是,Selenium 有能力等到它看到某個元素,如果元素沒有出現,它可以超時,如下所示:

 element = WebDriverWait(driver, 10). until(EC.presence_of_element_located((By.ID, "theFirstLabel")))


  • 通過驗證碼

    一些網站使用 Captcha 或類似方法來防止不需要的機器人(他們可能會認為是你)。 這可以抑製卷筒紙的刮擦並減慢速度。

對於簡單的提示(例如“什麼是 2 + 3?”),這些通常可以很容易地閱讀和計算出來。 但是,對於更高級的障礙,有一些庫可以幫助嘗試破解它。 一些示例包括 2Captcha、Death by Captcha 和 Bypass Captcha。

  • 網站結構變化

    網站注定要改變——而且他們經常這樣做。 這就是為什麼在編寫抓取腳本時,最好記住這一點。 您需要考慮將使用哪些方法來查找數據,以及不使用哪些方法。 考慮部分匹配技術,而不是嘗試匹配整個短語。 例如,網站可能會將消息從“未找到記錄”更改為“未找到記錄”——但如果您的匹配項是“未找到記錄”,則應該沒問題。 此外,請考慮是否匹配 XPATH、ID、名稱、鏈接文本、標籤或類名稱,或 CSS 選擇器 - 最不可能改變的。

總結:Python 和 Selenium

這是一個簡短的演示,表明幾乎任何網站都可以被抓取,無論使用什麼技術以及涉及什麼複雜性。 基本上,如果您可以自己瀏覽該網站,則通常可以被抓取。

現在,作為警告,這並不意味著每個網站都應該被抓取。 有些有合法的限制,並且有許多法庭案件決定了抓取某些網站的合法性。 另一方面,一些網站歡迎並鼓勵從其網站檢索數據,並且在某些情況下提供 API 以使事情變得更容易。

無論哪種方式,最好在開始任何項目之前檢查條款和條件。 但是,如果您繼續前進,請放心,您可以完成工作。

複雜網頁抓取的推薦資源:

  • 高級 Python Web Scraping:最佳實踐和解決方法
  • 可擴展的自己動手抓取:如何大規模構建和運行抓取工具