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平均仅承担1/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?
对于登录、购物车等需要保持Cookie连贯的操作,必须使用相同IP。首次请求时直接设置meta proxy,在回调中创建后续请求时传递response.meta中的proxy值,循环中间件就不会更换IP。配合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"])如何临时移除故障代理?
在中间件的 process_response 中收到 403 或 429 响应时,可以让该代理休息几分钟,然后将相同的请求发送到其他代理。复制请求,删除 meta 中的代理,并打开 dont_filter,然后返回,下一个代理就会接管。
通过字典管理闲置代理列表和复用时间最为简便。避免立即重复使用相同IP,为目标网站留出限制解除时间。
在Scrapy中如何节省带宽?
Scrapy默认只接收HTML,因此比浏览器工具消耗的带宽少得多。此外,默认启用的HttpCompressionMiddleware会接收压缩响应,进一步减少传输量。这就是为什么在共享计划的月带宽内可以采集大量页面的原因。
开发时启用HTTPCACHE_ENABLED可避免重复获取相同页面,测试时无需反复调用目标网站和代理。正式运营时应保持去重功能(默认开启),仅对必须重新获取的请求使用dont_filter参数。
限制链接的爬取范围,避免不必要地深入子页面,也很重要。明确收集范围可以减少时间、带宽和目标网站的负担。
常见问题如何解决?
出现407响应时请检查地址中的用户名和密码。若密码含特殊字符需进行百分号编码。TunnelError表示代理未收到CONNECT请求,请确认使用的是HTTP端口。
若所有请求均通过同一IP发出,可能因中间件顺序号在HttpProxyMiddleware之后,或爬虫直接使用meta proxy所致。请同步检查settings.py顺序与爬虫代码。