大家在讀爬蟲系列的帖子時常常問我怎樣寫出不阻塞的爬蟲,這很難,但可行。通過實現一些小策略可以讓你的網頁爬蟲活得更久。那麼今天我就將和大家討論這方面的話題。
使用者代理
你需要關心的第一件事是設定使用者代理。 使用者代理是使用者訪問的工具,並告知伺服器使用者正在使用哪個網路瀏覽器訪問網站。 如果未設定使用者代理,許多網站不會讓你檢視內容。 如果你正在使用rquests庫,可以執行如下操作:
1 2 3 4 |
headers = { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36', } r = requests.get('example.com',headers=headers) |
你可以通過在 Google 搜尋欄中輸入 User-Agent 來獲取使用者代理的資訊,並且它會返回你當前的使用者代理資訊。
現在,你已經有了一個使用者代理,但如何去使用它? 那麼,最好的方法是從文字檔案、資料庫、Python 的列表中選擇一個隨機的 User-Agent 。 Udger 分享了大量的 UA w.r.t 瀏覽器。 比如,對於 Chrome 而言,它看起來像這樣,對 Firefox 來說,又像這樣。 現在讓我們來建立一個函式,它將返回一個隨機 UA ,你可以在請求中使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import numpy as np def get_random_ua(): random_ua = '' ua_file = 'ua_file.txt' try: with open(ua_file) as f: lines = f.readlines() if len(lines) > 0: prng = np.random.RandomState() index = prng.permutation(len(lines) - 1) idx = np.asarray(index, dtype=np.integer)[0] random_proxy = lines[int(idx)] except Exception as ex: print('Exception in random_ua') print(str(ex)) finally: return random_ua |
ua_file.txt 包含一個來自我上面共享的網站的每行 UA 。 函式 get_random_ua 將始終從該檔案中返回唯一的 UA 。 你現在可以呼叫如下函式:
1 2 3 4 5 |
user_agent = get_random_ua() headers = { 'user-agent': user_agent, } r = requests.get('example.com',headers=headers) |
Referrers
接下來你需要設定的是引用。 一般的規則是,如果它是一個列表頁面或主頁,那麼你可以設定該國家的 Google 主頁網址。 例如,如果我正在爬取 olx.com.pk ,那麼我會設定 https://google.com.pk 而不是 https://google.ca。
如果你要抓取各個產品頁面,可以在引用中設定相關類別的網址,或者可以找到要抓取的域的反向連結。 我通常使用 SEMRush 來這麼做。針對連結 https://www.olx.com.pk/furniture-home-decor/categories/ 通過 SEMRush 會返回如下所示的內容:
如果你點選檢視放大的影象,你可以看到一些連結指向我所需的類別。一旦你收集所有這些真實的反向連結,你可以通過複製邏輯 insideget_random_ua()返回隨機引用,並將它們用作引用。 如下所示:
1 2 3 4 |
headers = { 'user-agent': user_agent, 'referer':referer } |
代理 IP
我不得不強調這一點。如果認真研究,那麼你必須使用多個代理 IP 來避免阻塞。 大多數網站會根據你的伺服器或主機提供商的靜態 IP 來阻止抓取工具。 這些網站使用智慧的工具來確定某個 IP 或 IP 池的方式,並簡單地阻止它們。 這也是為什麼建議購買幾個 IP 地址,50-100個至少要避免阻塞。有許多可用的服務,但我對 Shaders(現在稱為 OxyLabs )感到滿意。 它們雖然很貴,但服務質量很好。 確保你在訂購多個 IP 時,要求提供隨機 IP 或至少不遵循 1.2.3.4 到 1.2.3.100 等特定模式。站點管理員將很簡單的設定 IP 地址不全部為 1.2.3.* 。 就這麼簡單。
如果你正在使用請求,你可以像下面這樣使用它:
1 |
r = requests.get('example.com',headers=headers,proxies={'https': proxy_url}) |
如果你在 Selenium 使用代理 IP ,那麼這將有點棘手。
1 2 3 4 5 6 7 8 9 |
r = requests.get('example.com',headers=headers,proxies={'https': proxy_url}) proxy = get_random_proxy().replace('\n', '') service_args = [ '--proxy={0}'.format(proxy), '--proxy-type=http', '--proxy-auth=user:path' ] print('Processing..' + url) driver = webdriver.PhantomJS(service_args=service_args) |
不用說,get_random_proxy() 是返回一個唯一且隨機代理的方法,就像上面獲得唯一且隨機的 UA 和 Referer 一樣。
你可以思考一個這樣的系統,在系統中你可以設定一個 IP 每天或每小時訪問網站頻率,如果它超過了,那麼它將被放入一個籠子裡直到第二天。 我所在的公司設計了一個這樣的系統,不僅設定了 IP 訪問頻率,還記錄了哪個 IP 被阻止。 最後,我只是使用代理服務提供者僅替換這些代理。 由於這超出了本文的範圍,所以我不會詳細介紹它。
Request Headers
到目前為止,事情你都已經做得很好,但是仍然有些狡猾的網站要求你做更多的事情。當你訪問頁面的時候他們會查詢特定的請求響應頭資訊,如果特定的頭資訊沒有被發現,他們會阻止內容顯示或者展示一個虛假的內容。模擬一個你想訪問的網站的請求是非常簡單的。例如,比如你正準備訪問一個 Craigslist URL ,並且想知道哪個頭部資訊是需要的。進入 Chrome/Firefox 瀏覽器,檢查正在訪問的頁面,你應該會看到下面這些內容:
如果你點選了圖示並檢視,你能找到除了 referer 和 user-agent 之外的大量資訊。你能一次性全都實現也可以一個個的實現並測試。無論訪問哪個網站,我總是去設定這些資訊。請確保你不只是複製貼上這些資訊去訪問所有網站,因為這些資訊通常會因網站不同而改變。
1 2 3 4 5 6 7 8 |
headers = { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36', 'referrer': 'https://google.com', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'en-US,en;q=0.9', 'Pragma': 'no-cache', } |
延時
在請求之間放置一些延遲總是很好的。我使用 numpy.random.choice() 來實現這一目標,該函式將在我想延遲的服務中傳遞隨機數列表:
1 2 3 |
delays = [7, 4, 6, 2, 10, 19] delay = np.random.choice(delays) time.sleep(delay) |
如果你還沒有使用過 numpy 庫,你也可以使用 random.choice 來達到同樣的目的。
如果你真的很急,那麼你可以並行執行 URL ,我之前在此文中解釋過了。
結語
Web 爬蟲被阻塞的不確定性永遠不會變為零,但你總是可以採取一些措施來規避它。我討論了一些你應該以某種或其他方式在 web 爬蟲中實現的策略。
如果你知道其他策略或技巧,請通過分享評論讓我獲知。一如既往地,期待你的反饋。
本文最初發表在此。