Scrapy如何處理代理?
Scrapy默認啟用HttpProxyMiddleware。在請求的meta字典中添加proxy鍵和地址,該請求就會通過指定代理發出,地址中的用戶名和密碼會被中間件轉換為Proxy-Authorization頭髮送。HTTPS地址通過CONNECT隧道傳輸。
Scrapy默認下載處理器支持HTTP代理。若必須使用SOCKS5需額外處理器,建議在Scrapy中使用同一IP的HTTP端口。目標網站看到的IP相同。
示例使用 IP 198.51.100.23,HTTP 端口 8000,用戶名 spx_user,密碼 spx_pass。
pip install scrapy
scrapy startproject shop
cd shop如何在請求中加入代理?
於start_requests建立Request時,透過meta傳入proxy參數即可。回調中請求下一頁時請以相同方式設定。需維持相同會話時,可從前次響應的meta中繼承proxy值以保持IP不變。
import scrapy
PROXY = "http://spx_user:spx_pass@198.51.100.23:8000"
class IpCheckSpider(scrapy.Spider):
name = "ip_check"
def start_requests(self):
yield scrapy.Request("https://api.ipify.org?format=json", meta={"proxy": PROXY})
def parse(self, response):
self.logger.info("seen as %s", response.json()["ip"])如何實現每次請求更換IP?
創建下載器中間件,為所有請求添加代理即可無需修改爬蟲代碼實現輪換。以下中間件啟動時通過API獲取列表,若meta已含proxy則保留原值,兼容會話固定與輪換需求。
請將API密鑰置於settings.py或從環境變量讀取。註冊順序需早於默認HttpProxyMiddleware(750)的數字,以便中間件處理由基本中間件插入的地址。
import itertools
import requests
class RotatingProxyMiddleware:
def __init__(self, key):
text = requests.get(
"https://socks5proxy.io/api/v1/proxies/download",
params={"format": "user:pass@ip:port", "protocol": "http"},
headers={"Authorization": "Bearer " + key},
timeout=30,
).text
self.pool = itertools.cycle(["http://" + l.strip() for l in text.splitlines() if l.strip()])
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.settings.get("SPX_API_KEY"))
def process_request(self, request, spider):
if "proxy" not in request.meta: # keep a proxy that was set on purpose (sticky sessions)
request.meta["proxy"] = next(self.pool)SPX_API_KEY = "YOUR_API_KEY"
DOWNLOADER_MIDDLEWARES = {
"shop.middlewares.RotatingProxyMiddleware": 740,
}
CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 16
DOWNLOAD_DELAY = 0.25
AUTOTHROTTLE_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [429, 500, 502, 503, 504, 522, 524, 408]
DOWNLOAD_TIMEOUT = 30如何調整速度和重試次數?
CONCURRENT_REQUESTS_PER_DOMAIN 係指同一個域名可以同時發出嘅請求數量。當你有多個代理 IP 時,提高呢個數值並唔會增加單一 IP 嘅負擔。例如,用 500 個 IP 同時發出 50 個請求,每個 IP 平均只會處理 10 次請求中嘅一次。
啟用AUTOTHROTTLE可根據響應速度自動調整請求間隔。429響應增多時可用RETRY_HTTP_CODES重試,重試時清除meta的proxy使中間件插入下一代理。
在管道中加入檢查收集結果是否為實際頁面而非確認頁面的步驟,可以提高數據質量。如果回應中缺少所需元素,可以使用其他 IP 重新請求。
- 根據 IP 數量調整各域名並發請求數
- AUTOTHROTTLE根據響應速度自動調整間隔
- 429及5xx錯誤請嘗試其他IP
- 透過響應內容過濾驗證頁面
IP 授權連接有何不同?
在儀表板中註冊運行Scrapy的伺服器的IPv4地址,這樣在meta proxy中就可以像http://198.51.100.23:8000這樣無需用戶名和密碼使用。列表也可以以format=ip:port的格式獲取。如果有多台伺服器運行爬蟲,請註冊每台伺服器的IP。最多可以註冊10個IP。
如何將需要會話的請求綁定到相同 IP?
像登入或購物車這樣需要保持Cookie連續的流程,必須使用同一IP。在首次請求中直接加入meta proxy,並在回調中創建下一個請求時,將response.meta的proxy值直接傳遞,這樣循環中間件就不會更改它。與Scrapy的cookiejar meta鍵一起使用,Cookie也會按會話分離。
將會話與代理和 Cookie 綁定,即使同時運行多個會話也不會混淆。
import scrapy
PROXIES = [
"http://spx_user:spx_pass@198.51.100.23:8000",
"http://spx_user:spx_pass@198.51.100.24:8000",
]
class SessionSpider(scrapy.Spider):
name = "sessions"
def start_requests(self):
for i, proxy in enumerate(PROXIES):
yield scrapy.Request("https://example.com/", meta={"proxy": proxy, "cookiejar": i}, dont_filter=True)
def parse(self, response):
# follow-up request keeps the same proxy and the same cookie jar
yield response.follow("/account", meta={"proxy": response.meta["proxy"], "cookiejar": response.meta["cookiejar"]}, callback=self.parse_account)
def parse_account(self, response):
self.logger.info("status %s via %s", response.status, response.meta["proxy"])如何暫時移除失敗代理?
如果從 middleware 的 process_response 收到 403 或 429 回應,可以讓該代理休息幾分鐘,並將相同的請求發送到另一個代理。複製請求,刪除 meta 中的 proxy,開啟 dont_filter,然後返回,下一個代理就會接手。
簡單管理休息中的 proxy 列表和重新使用的時間。避免立即重複使用同一 IP,可以讓目標網站的限制有時間解除。
在Scrapy中如何節省帶寬?
Scrapy默認僅獲取HTML,比瀏覽器工具消耗少得多帶寬。啟用默認的HttpCompressionMiddleware接收壓縮響應可進一步減少傳輸量。這是共享計劃月帶寬內能採集大量頁面的原因。
開發時啟用HTTPCACHE_ENABLED可避免重複獲取頁面,減少測試時對目標網站與代理的請求。正式運行時保持去重功能(預設開啟),僅對必須重新獲取的請求使用dont_filter。
限制鏈接跟蹤範圍避免爬取不必要子頁面也很重要。明確收集範圍可節省時間、帶寬並減輕目標網站負擔。
如何解決常見問題?
出現407回應時,請檢查地址中的用戶名及密碼。若密碼包含特殊字符,需進行百分比編碼後輸入。TunnelError表示代理未收到CONNECT請求,請確認是否使用HTTP端口。
如果所有請求都經同一IP發出,可能係middleware順序中HttpProxyMiddleware排得太後,或者爬蟲直接喺meta加入proxy。請同時檢查settings.py嘅順序同爬蟲代碼。