【Python 網路爬蟲】克服網站反爬蟲機制的 8 種做法

JY的興趣行李箱
·
·
IPFS
·

隨著公開資料使用的普及化,網站對自家資料的保護也越來越嚴謹。開發者如何在符合道德和不觸發網站擋爬機制的同時,合法取得所需的公開資訊,是當今資料技術開發人員不可或缺的技能點之一。

(I) 發送 Request 時帶上 Headers

Request Headers

客戶端用來告知伺服器端,客戶端這邊的瀏覽器資訊是什麼。

因此模仿你在瀏覽網頁時,在 Network Panel 所看到的 Request Headers,也是降低網站把你判斷成爬蟲程序的機率。

import requests 
from fake_useragent import UserAgent # 可隨機產生 User-Agent 

headers = { 
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate, br", 
    "Accept-Language": "zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7", 
    "Host": "https://mops.twse.com.tw", 
    "Connection": "keep-alive", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Sec-GPC": 1, 
    "User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Mobile Safari/537.36", 
} 
ua = UserAgent() 
headers["User-Agent"] = ua.random
r = requests.get(url, headers=headers)

補充:可以查看自己的瀏覽器 Headers: http://myhttpheader.com/

(或打開 Network Panel 選取一項資源即可在右側畫面看到)

瀏覽器當下傳送請求到網站時所帶的 headers
  • Accept-Language“: “<language_tag>”; q=”<weight_value>
    • langauge tag: 語系代碼
    • q-factor weighting: 各個語系的權重值。(詳細說明
  • Content-Type“: “<media_type>; charset=<charset>; boundary=<boundary>”
    • media type: 網站上的資料型態(型態種類列表
    • charset: 網站會從伺服器端接收到的資料編碼格式
    • boundary: 分隔符,常見於 email browser/server

(II) 替換 headers 中的 User-Agent

User Agent 是瀏覽器的資訊,告知網站這是來自真實用戶使用的瀏覽器所發出的請求。

如果是來自不同用戶所發出的正常請求,他的 User-Agent 應該不會一直相同;因此透過隨機替換不同 User Agent 可以模擬不同用戶造訪網站的樣貌。

  • User-Agent“: “<product> / <product-version> (<details>)”
    • product: 瀏覽器名稱
    • product-version: 瀏覽器版本
    • details: 瀏覽器細節描述
    • 切換 User-Agent 扮演不同瀏覽器發出請求,也可以降低被伺服器判定為機器人的機率

(III) <meta charset="utf-8">替換 headers 中的 Referer

Referer 是紀錄用戶從哪個位址進到當前網頁,如果是從 Google 搜尋進到網站的,那 Referer 就會是 “https://www.google.com/”;如果是從該網站(假設叫做 currentwebsite.com )的其他頁面而來, Referer 就可能是 “https://www.currentwebsite.com?page=1122” 。

  • Referer“: “<url>”
    • url: 指定參照位址,也就是紀錄是從哪邊進到當前網頁
    • 可以用 SEO 工具來獲取反向連結(哪個網址有連到當前頁面)當作切換池 
      • 例如使用 Ahrefs 的 Free Backlink Checker 獲取有引用 Cupoy 首頁的網址(就代表流量是有可能從那個網頁過來 Cupoy 首頁,你用這個網址做 Referer 也合理


(IV)  設定隨機延遲時間

就是讓 sleep 的秒數從隨機整數產生,讓網站不易發現,否則頻率一致的固定模式就比較容易被偵測到是爬蟲程序在發送請求了。

時間的設定依目標網站的容忍度調整。

import numpy as np 
import requests 
from time import sleep 

def fetch(url, header): 
    sleep(np.random.randint(5, 20)) 
    r = requests.get(url, headers=headers) 
    return r


(V)  輪流切換 IP 位址

如果同一組 IP 位址短時間內大量發送請求到目標網站,會有很大機率被網站判定為程序,因而被擋。

因此輪流切換 IP 位址也是一種降低被擋機率的方式。

import requests 
proxies = { 
    "http": "http://username:password@proxy_provider_host.com:20001", 
    "https": "http://username:password@proxy_provider_host.com:20001", 
}
r = requests.get(url, headers=headers, proxies=proxies)

其中,HTTP 及 HTTPS Proxy 間的差異如下:

  • HTTP Proxy 會從客戶端收到純文字的 Request,隨後再發送另一 Request 到目標伺服器端,最後伺服器端將資訊回傳至客戶端。
  • HTTPS Proxy 則是一個中繼站的概念,會先收到客戶端一個特殊的 Request 後,與目標伺服器端建立一個不透明的通道,隨後客戶端會發送 SSL/TLS Request 到伺服器端、繼續進行 SSL 交握。

如果不確定目標網站是否需要透過 HTTPS 連線才能回傳資料,則可以在設定 requests proxies 時像上面這樣設定一個字典檔分別傳入 http, https proxy

延伸閱讀:


(VI)  使用 headless browser 

有些網站對爬蟲的偵測比較嚴格,會檢查像是瀏覽器 Cookie 和 JavaScript 執行等,此時更建議使用 Selenium;但是預設用 Selenium 模擬瀏覽器行為時,速度會變慢很多,這時就可以用 headless 的設置,來取消網頁介面彈出,讓效能較好一些。

以下用需要 JavaScript 載入的 Pressplay 網站示範:

from fake_useragent import UserAgent 
import numpy as np 
from selenium import webdriver 
import time 

# 設定 webdriver 啟動檔位址
driver_path = "/Users/jiunyiyang/.wdm/drivers/chromedriver/mac64/102.0.5005.61/chromedriver" 
url = "https://www.pressplay.cc/project?type=507CF74F93AD23B6584EF2C5D5D4430E" 

# 設定 Webdriver Options 
opt = webdriver.ChromeOptions() 
user_agent = UserAgent() 
opt.add_argument("--user-agent=%s" % user_agent) 
opt.add_argument("--window-size=1920,1080") 
opt.add_argument("--headless")   # 啟用 headless 模式 
opt.add_argument("--disable-gpu")   # 測試關閉 GPU 避免某些錯誤出現 

# 載入目標網址 
driver = webdriver.Chrome(driver_path, options=opt) 
driver.get(url) 
print(driver.title) 
# 模擬用戶等待網站載入,每次都用隨機時間 
time.sleep(np.random.uniform(3, 5)) 
try: 
    ele = driver.find_elements_by_xpath('//div[@class="project-list-item"]')[1] 
    course = ele.find_elements_by_xpath('.//div[@id="project-card"]')[1] 
    print(course.text) 
except Exception as e: 
    print(e) 
finally: 
    driver.close() 
    print("driver closed.")

運行結果:

看到他可以成功印出那堂課程資訊卡的文字內容

P.S. 如果只用 requests 爬取就會抓不到課程區塊內的資訊卡


(VII)  <meta charset="utf-8">讓爬蟲步驟增加一些不可預測性

如果爬蟲步驟越固定,越容易被網站偵測出是程序;在使用 Selenium 時可以增加一些隨機的滾動或點擊行為,讓網站覺得你的程序越像真人的行為。

(VIII) 在網站流量的離峰時段爬取

如果在尖峰時段爬取,通常爬蟲程序的換頁速度比正常人瀏覽快很多,如此一來也會更明顯的拖累網站本身的效能和用戶體驗;在離峰時間爬取、並配合前面提過的隨機延時,可以避免讓伺服器超過負荷。

以上是從不同網站統整&測試過的 8 種克服網站擋爬的方法,參考文獻如下:

如覺得內容有幫助,請幫我多按幾次 LikeCoin!
(用 Google 帳戶就可以註冊會員、不需要付費即可對覺得有幫助的內容點讚!)
您的讚是我們創作的最大動力!

CC BY-NC-ND 2.0

Like my work? Don't forget to support and clap, let me know that you are with me on the road of creation. Keep this enthusiasm together!

JY的興趣行李箱一個數據分析師的個人興趣分享,走到哪寫到哪 可能是程式技能分享、綜藝點評、舞台/歌曲/樂曲/電影收藏、書摘、產業觀察等
  • Author
  • More

Git 協作不可不知的重要指令

【Python】複製一份 conda 環境的各種方式

【資料分析】認識統計顯著性|A/B Testing 觀測數值增減多少才是顯著有效?