Squid代理伺服器搭建億級爬蟲IP代理池
- 2019 年 10 月 6 日
- 筆記
做爬蟲抓取時,我們經常會碰到網站針對IP地址封鎖的反爬蟲策略。但只要有大量可用的IP資源,問題自然迎刃而解。
以前嘗試過自己抓取網路上免費代理IP來搭建代理池,可免費IP品質參差不齊,不僅資源少、速度慢,而且失效快,滿足不了快速密集抓取的需求。
收費代理提供的代理資源品質明顯提升,最終選定使用站大爺作為代理提供平台。
站大爺每天能提供大概5萬個不重複的短效高匿代理,每個代理存活期為2分鐘,總IP數有20億,IP數量足夠使用。價格為包天30元,包月500元,還有半年及一年的套餐可供選擇。只要能滿足項目要求,提供優質穩定的服務,這些成本值得付出。
高匿代理才可以真正用來防止爬蟲被封鎖,如果使用普通代理,爬蟲的真實IP還是會暴露。
搭建思路
站大爺提供了大量的代理伺服器資源,主要考慮如何將這些伺服器分配給爬蟲伺服器使用。最初的想法是使用Redis作為代理伺服器資源隊列,一個程式自動獲取站大爺API提供的代理,驗證可用後push到Redis里,每個程式再從Redis中pop一個代理進行抓取,但這樣的缺點是不太好控制每台爬蟲伺服器的代理品質,有的代理速度快,有的速度比較慢,影響抓取效率,其次就是需要自行維護一套代理驗證、分配的程式,增加了程式碼量,不便後期維護。
為了解決這些問題,我想到可以使用 Squid 提供的父代理功能,自動將爬蟲伺服器的請求轉發給代理伺服器。Squid提供了自動輪詢功能,自動驗證並剔除不可用的代理。減少了我們多餘的驗證步驟。
爬蟲軟體只需將代理設置為 Squid 伺服器即可,不需要每次重新設置為其他的代理伺服器。
這套方案明顯減少了工作量,提高了易用性和可維護性。
實現過程
- 首先獲取代理平台提供的代理伺服器資源
- 建議購買短效代理,購買後在後台獲取API地址並設置IP白名單等參數
- 將獲取到的代理伺服器寫入squid配置文件
- 解析網站提供的代理伺服器,按照一定規則寫入
/etc/squid/squid.conf
- 解析網站提供的代理伺服器,按照一定規則寫入
- 重新配置 squid
- 寫入配置文件之後重新載入最新的文件,不會造成中斷
- 自動更新,重複1-3
- 由於網站提供的代理存活時間只有2分鐘,所以需要每隔一段時間重新獲取一批新IP
from gevent import monkey # isort:skip monkey.patch_all() # isort:skip import logging import os import time import requests from gevent.pool import Pool logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s: - %(message)s", datefmt="%Y-%m-%d %H:%M:%S" ) # 使用StreamHandler輸出到螢幕 ch = logging.StreamHandler() ch.setLevel(logging.INFO) ch.setFormatter(formatter) logger.addHandler(ch) # Squid的配置文件語法 # 將請求轉發到父代理 PEER_CONF = "cache_peer %s parent %s 0 no-query weighted-round-robin weight=1 connect-fail-limit=2 allow-miss max-conn=5n" # 可用代理 GOOD_PROXIES = [] pool = Pool(50) def check_proxy(proxy): """驗證代理是否可用 :param proxy list:[ip, port]""" global GOOD_PROXIES ip, port = proxy _proxies = {"http": "{}:{}".format(ip, port)} try: ip_url = "http://2019.ip138.com/ic.asp" res = requests.get(ip_url, proxies=_proxies, timeout=10) assert ip in res.content logger.info("[GOOD] - {}:{}".format(ip, port)) GOOD_PROXIES.append(proxy) except Exception as e: logger.error("[BAD] - {}:{}, {}".format(ip, port, e)) def update_conf(): with open("/etc/squid/squid.conf.original", "r") as F: squid_conf = F.readlines() squid_conf.append("n# Cache peer confign") for proxy in GOOD_PROXIES: squid_conf.append(PEER_CONF % (proxy[0], proxy[1])) with open("/etc/squid/squid.conf", "w") as F: F.writelines(squid_conf) def get_proxy(): global GOOD_PROXIES GOOD_PROXIES = [] # 1. 獲取代理IP資源 api_url = "http://s.zdaye.com/?api=YOUR_API&count=100&fitter=1&px=2" res = requests.get(api_url).content if len(res) == 0: logger.error("no data") elif "bad" in res: logger.error("bad request") else: logger.info("get all proxies") proxies = [] for line in res.split(): proxies.append(line.strip().split(":")) pool.map(check_proxy, proxies) pool.join() # 2. 寫入Squid配置文件 update_conf() # 3. 重新載入配置文件 os.system("squid -k reconfigure") logger.info(">>>> DONE! <<<<") def main(): start = time.time() while True: # 每30秒獲取一批新IP if time.time() - start >= 30: get_proxy() start = time.time() time.sleep(5) if __name__ == "__main__": main()
使用方法
- 按Squid 搭建正向代理伺服器、Squid 配置高匿代理介紹的方法搭建運行 Squid 高匿伺服器
- 備份原始配置文件
cp /etc/squid/squid.conf /etc/squid/squid.conf.original
,以供軟體使用 - 在squid伺服器上運行
python zdy.py
實例
如果按照上述方法搭建好代理IP池,只需要在爬蟲程式碼中設置設置squid代理伺服器地址和埠(比如139.xxx.xxx.66:3188)。
from __future__ import print_function import requests s = requests.Session() s.proxies.update({"http": "139.xxx.xxx.66:3188"}) print(s.get("http://httpbin.org/ip"))
每次運行這個程式時,返回的IP都不一樣,而且僅有一個,說明IP代理池已經搭建成功,可以應用在網路爬蟲項目中。
總結
這套解決方案結合了網路上的大量優質代理資源以及Squid伺服器提供的特性,基本解決了網站對於爬蟲IP的封鎖。
成本比較低,而且有比較高的易用性,很方便地能結合到各種爬蟲應用中,只需要增加一個代理地址即可,而由 Squid 統一管理父代理,不需要在爬蟲程式中進行代理的獲取驗證等等操作,便於維護。
實際使用中還沒有發現什麼特別重大的問題,更多擴展性還有待後續繼續研究。
項目地址: https://github.com/xNathan/squid_proxy_pool