楔子
好友李博士要買房了, 前幾天應邀幫他抓鏈家的數據分析下房價, 爬到一半遇到了驗證碼.
李博士的想法是每天把鏈家在售的二手房數據都抓一遍, 然后按照時間序列分析. 鏈家線上在交易的二手房數據大概有30000余套, 而一天只有86400秒, 也就是最慢3s也要訪問一個頁面. 測試按照這種頻率, 大概訪問100個頁面就會被封了, 而且封鎖的時間還不短, 至少在12小時左右. 如果想要每天快速的爬一些數據, 必須得搞定驗證碼.
鏈家封鎖是按照IP封禁的, 簡單的添加上cookies並沒有什么效果. 要破解IP封禁, 自然地想法是加上代理就好了. 在公司里代碼各種工具都很成熟, 有現成的代理池可用, 自己要爬些數據的時候發現原來什么都需要自己做. 那就自己做個代理池吧.
方案
三種方法:
需要付費的方法
- 自己買IP地址,自己做代理池. 可以利用各種雲可以換IP的api(彈性IP),采用幾台實例做出口,如果被封了就換IP, 大概看一下IP的價格的話, 就知道這個實在不太現實, 太貴了理論上這個比上一個便宜, 但是還是很貴.
阿里雲上最便宜的主機(內網主機, 沒有公網IP)大概價格在60元左右, 如果自己做代理池的話, 想要多少並發出口就需要多少台主機. 假如我們需要同時有10個IP出口是可用的, 那么主機的費用就是 600元. 另外一個1Mbps的IP的價格大概是30元左右, IP的價格是300元. 每個月搭建一個代理池就要花費近1000元, 實在是用不起.
而且雲主機廠商的IP地址往往是連續的, 很可能換來換去都在一個C段, 這時候對於直接封禁IP段的網站就無解了.
- 直接購買代理,這些代理往往是通過一些接近黑產的方式掃描端口得來的,需要驗證才能使用.
購買的代理的價格大概是每天幾塊到幾十塊不等, 不同廠商之家大同小異, 一個月最少也都需要幾百元左右. 不過之前聽同事說這些代理的可用性一般, 拿到的代理還需要自己驗證是否可用, 也就是花了錢還不能爽, 自然沒有了買的欲望.
窮人的方法
這些賣代理的網站往往都提供了一些免費代理在首頁來吸引流量, 少則幾十, 多則幾百, 初步測試了幾個還算能用. 既然直接買來代理也還需要驗證, 那還不如直接抓取他們網站上提供的免費代理了. 驗證后入庫, 用的時候直接選取一個用就好了. 如果代理庫里有1000個左右的可用IP, 那還是能解決不少問題的.
代理的一些基礎知識
某網站的代理列表:
一般來說, http/https代理用的比較多, socks代理似乎很少使用. 其中好多代理服務器只支持http代理. 比如 https://www.baidu.com 就只能使用https代理訪問, 而不能使用http代理. 上圖中給出了這些代理的類型, 然而實際測試發現, 這些類型好多都是不准的. 還有響應時間等數據也和服務器有關, 並不能直接使用上面的數據. 所以我們的思路是只要地址和端口, 其他的數據都自己驗證.
架構設計
存儲
我們把抓到的代理存到mysql中, 具體的表設計如下:
CREATE TABLE `proxy` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`ip` varchar(15) NOT NULL,
`port` int(10) unsigned NOT NULL DEFAULT '3128',
`type` smallint(5) unsigned NOT NULL DEFAULT '0',
`country` varchar(2) DEFAULT NULL,
`state` varchar(15) DEFAULT NULL,
`enqueue_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_check_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`used_times` int(11) NOT NULL DEFAULT '0',
`from_url` varchar(1024) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `idx_ip_port` (`ip`,`port`)
) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8mb4
抓取和驗證
代理的獲取分為兩個線程, 一個線程使用爬蟲不斷掃描已知的代理的網站, 獲得代理的地址, 驗證后入庫. 另一個線程掃描庫里的代理, 驗證是否依然可用.
接口
代理服務對外主要提供一個接口, get_proxy
. 需要使用代理的程序每次調用這個接口獲得一個可用代理使用. 每次盡量選取使用次數最少但是存活時間最長的代理.
實現
之前寫過一個簡單的按照配置文件抓取網頁的小爬蟲(https://github.com/yifeikong/minicrawler). 這下終於派上用場了. 把每個網頁抽取代理的xpath配好, 之后就等着在數據庫看數據就好了 _
使用 django 簡單實現了一個后台, 大概是這樣的:
經過測試, 一般代理的存活時間往往在十幾分鍾左右, 但是這些代理網站的更新時間一般也在十幾分鍾, 而且由於找到了大概幾十個網站, 掃一遍並驗證花費的時間可能在幾個小時左右. 所以維持一個可用代理在1k左右的代理庫是不成問題的.
get_proxy 的接口如下:

后記
-
經過測試發現, 國外的代理網站往往量很大, 而且可用率也比較高. 然而由於防火牆的原因, 這些網站以及甚至他們提供的代理在國內是不能訪問的, 不過我的主機在阿里雲香港, 所以也就沒有問題了.
-
有不少網站顯然是發現了我們這種抓代理的行為, 他們頁面的IP或者端口是加密的, 而在瀏覽器中js執行過后則是有正確結果的. 也就是簡單地通過抽取是得不到正確結果的. 比如這樣:
比如這里在原始網頁中是沒有代理的IP的, 而是通過js動態生成的.
對於這種網頁怎么操作的, 且聽下回分解: 使用headless chrome抓取動態網頁.