在眾多的網頁中,當要載入的資料量非常龐大時,為了不影響執行效能的情況下,除了會使用滾動捲軸來動態載入更多的資料外,另一種最常見的方式就是利用分頁,來分別顯示特定筆數的資料。
如果Python網頁爬蟲遇到需滾動捲軸,才可爬取更多資料的實作方式,可以參考[Python爬蟲教學]整合Python Selenium及BeautifulSoup實現動態網頁爬蟲文章,而Python網頁爬蟲遇到分頁的網站時,該如何翻頁爬取內容,就是本文要來分享的主題。其中的重點包含:
- HTTP GET vs POST方法
- 頁碼使用GET的方式
- 頁碼使用POST的方式
一、HTTP GET vs POST方法
在說明Python網頁爬蟲讀取分頁的資料前,先來簡單瞭解一下什麼是HTTP的GET及POST方法。
HTTP的GET方法就像明信片,在上面需要撰寫目的地的地址及內容,其中的地址就像是請求的網址,而內容就像是接在網址後面的參數(querystring),只要拿到這個明信片(網址)的人,都知道要寄送到哪裡及內容是什麼。
HTTP的POST方法就像一般的信件,在信封上需撰寫目的地的地址,而信件內容會在信封中,所以拿到這封信的人只知道會寄送到哪裡,但不會知道內容是什麼,就像是知道請求的網址,但是其中送到伺服器端的資料,無法在網址知道。
瞭解了這兩個HTTP方法的基本觀念後,接下來就使用兩個網站,來分別說明頁碼使用GET及POST方式處理的網站,Python網頁爬蟲該如何翻頁爬取資料。
二、頁碼使用GET的方式
這就是典型GET方法的分頁方式,從網址就可以知道目的地網址及傳至伺服器端的頁碼參數(page),當變換page參數的值時,就可以前往對應的頁碼網頁。
接下來,在網頁上按F12鍵,進入開發者模式後,先來看一下要爬取的文章標題HTML原始碼架構,如下圖:
瞭解它的階層架構,就可以利用BeautifulSoup套件爬取第一頁的內容,如下範例:
from bs4 import BeautifulSoup import requests # 連結網站 response = requests.get( "https://www.inside.com.tw/tag/AI?page=1") # HTML原始碼解析 soup = BeautifulSoup(response.text, "html.parser") # 取得所有class為post_title的<h3> titles = soup.find_all("h3", {"class": "post_title"}) for title in titles: print(title.select_one("a").getText()) # 取得標題文字
執行結果
成功爬取第一頁的內容,就可以使用Python迴圈,透過變換網址頁碼參數(page)的方式,翻頁爬取網頁內容了,如下範例:
from bs4 import BeautifulSoup import requests for page in range(1, 4): # 執行1~3頁 # 連結網站 response = requests.get( "https://www.inside.com.tw/tag/AI?page=" + str(page)) # HTML原始碼解析 soup = BeautifulSoup(response.text, "html.parser") # 取得所有class為post_title的<h3> titles = soup.find_all("h3", {"class": "post_title"}) print(f"====================第{str(page)}頁====================") for title in titles: print(title.select_one("a").getText()) # 取得連結的標題文字
範例中只爬取1~3頁的內容,如果要更多頁的話可以自行調整第4行的range()參數。另外,要特別注意的是,Python迴圈讀取range()函式時,page的型別為數值(Integer),所以為了要加在網址後面,需透過str()函式轉型成字串,如範例中第8行。詳細的Python轉型教學可以參考[Python教學]Python數值與型別轉換的重要觀念文章。
三、頁碼使用POST的方式
另一個網站頁碼切換的方式會採用POST方法,就像信件一樣,知道寄送的地址(網址),卻不會知道信封中的內容(送往伺服器的頁碼參數)。
由於是使用POST方法來撈取網頁資料,所以當切換到第二頁時,會發現網址並不會像Inside 硬塞的網路趨勢觀察網站一樣,會出現頁碼的參數,而是和第一頁的網址一模一樣,都沒有動靜。
這時候,就需要結合Selenium套件,透過模擬使用者點擊下一頁按鈕,來進行換頁的動作。同樣先看一下所要爬取的租屋標題HTML原始碼結構,如下圖:
接著,就可以利用Selenium套件開啟瀏覽器並且連線至591房屋交易網的「租屋」類別,再使用BeautifulSoup套件爬取第一頁的租屋標題。首先,利用以下指令安裝開啟瀏覽器的WebDriver套件:
$ pip install webdriver-manager
安裝完成後,就可以按照邏輯撰寫如下的程式碼:
from selenium import webdriver from selenium.webdriver.chrome.options import Options from webdriver_manager.chrome import ChromeDriverManager from bs4 import BeautifulSoup options = Options() options.add_argument("--disable-notifications") # 取消所有的alert彈出視窗 browser = webdriver.Chrome( ChromeDriverManager().install(), chrome_options=options) browser.get("https://rent.591.com.tw/") area = browser.find_element_by_id("area-box-close") area.click() # 取消「選擇縣市」的div視窗 soup = BeautifulSoup(browser.page_source, "html.parser") # 取得所有class為pull-left infoContent的<li>標籤 elements = soup.find_all("li", {"class": "pull-left infoContent"}) for element in elements: # 取得<li>標籤下的<h3>標籤,再取得<h3>標籤下的<a>標籤文字,並且去掉空白 title = element.find("h3").find("a").getText().strip() print(title)
執行結果
由於要點擊下一頁按鈕來進行換頁,所以,來觀察一下頁碼區域的HTML原始碼,如下圖:
from selenium import webdriver from selenium.webdriver.chrome.options import Options from webdriver_manager.chrome import ChromeDriverManager from bs4 import BeautifulSoup options = Options() options.add_argument("--disable-notifications") # 取消所有的alert彈出視窗 browser = webdriver.Chrome( ChromeDriverManager().install(), chrome_options=options) browser.get("https://rent.591.com.tw/") area = browser.find_element_by_id("area-box-close") area.click() # 取消「選擇縣市」的div視窗 soup = BeautifulSoup(browser.page_source, "html.parser") # 取得所有class為pull-left infoContent的<li>標籤 elements = soup.find_all("li", {"class": "pull-left infoContent"}) for element in elements: # 取得<li>標籤下的<h3>標籤,再取得<h3>標籤下的<a>標籤文字,並且去掉空白 title = element.find("h3").find("a").getText().strip() print(title) page_next = browser.find_element_by_class_name("pageNext") page_next.click() # 點擊下一頁按鈕
最後,透過Python迴圈重覆執行以上的動作,來達到動態翻頁爬取網頁內容的目的,如下範例:
from selenium import webdriver from selenium.webdriver.chrome.options import Options from webdriver_manager.chrome import ChromeDriverManager from bs4 import BeautifulSoup import time options = Options() options.add_argument("--disable-notifications") # 取消所有的alert彈出視窗 browser = webdriver.Chrome( ChromeDriverManager().install(), chrome_options=options) browser.get("https://rent.591.com.tw/") area = browser.find_element_by_id("area-box-close") area.click() # 取消「選擇縣市」的div視窗 for page in range(1, 3): # 執行1~2頁 soup = BeautifulSoup(browser.page_source, "html.parser") # 取得所有class為pull-left infoContent的<li>標籤 elements = soup.find_all("li", {"class": "pull-left infoContent"}) print(f"==========第{str(page)}頁==========") for element in elements: # 取得<li>標籤下的<h3>標籤,再取得<h3>標籤下的<a>標籤文字,並且去掉空白 title = element.find("h3").find("a").getText().strip() print(title) page_next = browser.find_element_by_class_name("pageNext") page_next.click() # 點擊下一頁按鈕 time.sleep(10) # 暫停10秒
四、小結
以上就是Python網頁爬蟲在遇到有分頁的網站時,能夠動態換頁爬取網頁內容的實作方式,詳細的程式碼可以參考下方的GitHub網址,希望有幫助到大家。
有想要看的教學內容嗎?歡迎利用以下的Google表單讓我知道,將有機會成為教學文章,分享給大家😊
Python學習資源
Python網頁爬蟲推薦課程
Python網頁爬蟲-BeautifulSoup教學
Python網頁爬蟲-Selenium教學
Python非同步網頁爬蟲
Python網頁爬蟲應用
Python網頁爬蟲部署
Python網頁爬蟲資料儲存
Python網頁爬蟲技巧
你好,謝謝你詳細的分享~~想另外請教一下,目前的教學比較著重在去爬「已知網站」的網頁資料;那如果是想要爬「未知網站」的資料,因為是未知的網頁結構,會有比較建議用什麼方式或套件嗎~應用場景大概是:使用者在前端輸入某個網址,後端(預計用python爬蟲)要提供出這個網址的文章內容
回覆刪除我蠻好奇的。因為你的留言已經過一年了。不知道你解決了嗎?因為就目前我學爬蟲的部分,未知的網頁應該看起來是沒辦法抓取吧?因為光是已知的狀態之下,你要知道位置定位才能抓到,更何況如果是未知的?謝謝
刪除嗨,後來是 import 一個開源套件 去試著實作,叫 Readability,github 網址:https://github.com/buriy/python-readability。這個 lib 的原理應該是用大部分網址應有的架構去抓出網頁的內容,不過實作起來,效果沒有很好,十個網頁大概只能有約4.5個可完整抓出(越單純網頁效果越好),提供你參考~謝謝
刪除謝謝你的回覆。聽起來是蠻合理的。因為複雜的網頁定位抓取就是比較麻煩一點。所以如果是未知的,照著整體一般架構去抓,的確就是越單純越好。
刪除