如果代理经常被封禁:完整诊断和解决问题指南
代理经常被阻止是网页抓取、自动化和多账户工作中最常见的问题之一。在本文中,我们将分析为什么会发生这种情况,以及如何系统地解决问题,而不是无休止地更换提供商。
为什么代理实际上会被封禁
在寻找解决方案之前,您需要了解阻止的机制。现代反欺诈系统使用多层保护,代理被封禁只是结果,而不是原因。了解这些系统的工作原理可以帮助您制定有效的绕过策略。
IP信誉和黑名单
每个IP地址都有一个声誉,该声誉是根据其使用历史形成的。如果该地址以前用于垃圾邮件、DDoS攻击或大规模抓取,它会被列入Spamhaus、SORBS或特定服务的专有列表等数据库。当您通过这样的IP连接时,系统会立即对您产生怀疑。
数据中心代理特别容易受到此问题的影响。整个子网可能被标记为"托管",来自这些IP的任何流量都会自动获得更高级别的检查。Amazon AWS、Google Cloud、DigitalOcean——它们的IP范围众所周知,经常被预防性地阻止。
您可以通过IPQualityScore、Scamalytics或AbuseIPDB等服务检查IP信誉。如果您的代理显示欺诈分数高于75,问题就在这里——更换提供商或代理类型。
请求模式
人类不会每秒发出100个请求。人类不会以完美的2秒周期浏览页面。人类不会忽略图片、CSS和JavaScript,只请求HTML。反欺诈系统分析这些模式,任何偏离"人类"行为的地方都会增加被阻止的风险。
请求之间的时间间隔统计特别有说明性。如果您有稳定的间隔——这是自动化的明显迹象。添加随机延迟(例如1到5秒)会大大降低被检测的可能性。
元数据不匹配
当您的User-Agent说您使用的是Windows上的Chrome,但HTTP标头暴露了Python请求的特征——这是一个红旗。当IP地址位于德国,但浏览器语言设置指向俄语——又是一个红旗。当JavaScript中的时区与IP地理位置不匹配——第三个红旗。
这些不匹配的累积导致系统将连接分类为可疑,并应用保护措施:从验证码到完全的IP阻止。
浏览器指纹
现代保护系统收集数十个浏览器参数:屏幕分辨率、安装的字体、插件、WebGL渲染、音频上下文等。这些参数的组合创建了一个唯一的"指纹",即使在更改IP时也保持不变。
如果您更改代理但指纹保持不变,系统会理解这是同一用户。如果一个指纹在短时间内从数百个不同的IP出现——这是自动化的明显迹象。
诊断:如何了解阻止的原因
在盲目更改设置之前,请进行诊断。这将节省数小时的实验时间,并帮助您找到问题的真正原因。系统诊断方法是有效解决问题的关键。
步骤1:检查代理本身
从独立于主脚本的基本代理功能检查开始:
import requests
proxy = {
"http": "http://user:pass@proxy-server:port",
"https": "http://user:pass@proxy-server:port"
}
# 检查基本功能
try:
response = requests.get("https://httpbin.org/ip", proxies=proxy, timeout=10)
print(f"通过代理的IP: {response.json()['origin']}")
except Exception as e:
print(f"连接错误: {e}")
# 检查真实IP泄露
response = requests.get("https://browserleaks.com/ip", proxies=proxy)
# 与您的真实IP进行比较
如果代理甚至在简单请求上都不工作——问题出在代理本身或凭据上。检查连接格式的正确性、提供商账户余额和限制。
步骤2:检查IP信誉
使用多个服务进行全面评估:
# 获取代理IP
proxy_ip = requests.get("https://api.ipify.org", proxies=proxy).text
# 在这些服务上检查:
# https://www.ipqualityscore.com/free-ip-lookup-proxy-vpn-test
# https://scamalytics.com/ip/{proxy_ip}
# https://www.abuseipdb.com/check/{proxy_ip}
# https://whatismyipaddress.com/ip/{proxy_ip}
print(f"在上述服务上检查IP {proxy_ip}")
注意以下指标:欺诈分数(应低于50)、IP类型(住宅优于数据中心)、黑名单中的存在。如果IP被标记为VPN/代理——许多网站会从一开始就对其产生怀疑。
步骤3:隔离问题
尝试在不同的目标网站上使用相同的代理。如果阻止发生在任何地方——问题出在代理或您的设置上。如果仅在特定网站上——问题出在该网站的保护或您的行为上。
还要尝试在一个网站上使用不同的代理。如果所有代理都被阻止——问题不在代理,而在您的脚本、指纹或行为模式。这是一个关键测试,许多人会跳过。
步骤4:分析服务器响应
不同类型的阻止表现不同。学会区分它们:
def analyze_response(response):
status = response.status_code
if status == 403:
print("访问被拒绝——可能IP在黑名单中")
elif status == 429:
print("请求过多——降低频率")
elif status == 503:
print("服务不可用——可能是DDoS保护")
elif status == 407:
print("需要代理授权——检查凭据")
elif "captcha" in response.text.lower():
print("检测到验证码——怀疑是机器人")
elif "blocked" in response.text.lower():
print("明确阻止——更换IP并重新考虑方法")
elif len(response.text) < 1000:
print("响应可疑地短——可能是存根")
else:
print(f"状态 {status},响应长度: {len(response.text)}")
正确的轮换:频率、逻辑、实现
代理轮换不仅仅是"更频繁地更换IP"。不正确的轮换可能比没有轮换造成更多伤害。让我们考虑不同的策略以及何时应用它们。
策略1:按请求数量轮换
最简单的方法——在一定数量的请求后更改IP。适用于不需要会话的抓取:
import random
class ProxyRotator:
def __init__(self, proxy_list, requests_per_proxy=50):
self.proxies = proxy_list
self.requests_per_proxy = requests_per_proxy
self.current_proxy = None
self.request_count = 0
def get_proxy(self):
if self.current_proxy is None or self.request_count >= self.requests_per_proxy:
# 在请求数量中添加随机性
self.requests_per_proxy = random.randint(30, 70)
self.current_proxy = random.choice(self.proxies)
self.request_count = 0
self.request_count += 1
return self.current_proxy
# 使用
rotator = ProxyRotator(proxy_list)
for url in urls_to_scrape:
proxy = rotator.get_proxy()
response = requests.get(url, proxies={"http": proxy, "https": proxy})
注意请求数量中的随机性。固定数字(例如恰好50个)是可以被检测到的模式。随机范围使行为不太可预测。
策略2:按时间轮换
对于需要会话的任务(例如账户工作),最好将IP绑定到时间:
import time
import random
class TimeBasedRotator:
def __init__(self, proxy_list, min_minutes=10, max_minutes=30):
self.proxies = proxy_list
self.min_seconds = min_minutes * 60
self.max_seconds = max_minutes * 60
self.current_proxy = None
self.rotation_time = 0
def get_proxy(self):
current_time = time.time()
if self.current_proxy is None or current_time >= self.rotation_time:
self.current_proxy = random.choice(self.proxies)
# 随机间隔到下一次轮换
interval = random.randint(self.min_seconds, self.max_seconds)
self.rotation_time = current_time + interval
print(f"新代理,下次轮换在 {interval//60} 分钟后")
return self.current_proxy
策略3:账户的粘性会话
使用多个账户时,至关重要的是每个账户使用恒定的IP。为登录的账户更改IP是被封禁的必然途径:
class AccountProxyManager:
def __init__(self, proxy_pool):
self.proxy_pool = proxy_pool
self.account_proxies = {} # account_id -> proxy
self.used_proxies = set()
def get_proxy_for_account(self, account_id):
# 如果账户已分配代理——返回它
if account_id in self.account_proxies:
return self.account_proxies[account_id]
# 查找可用代理
available = [p for p in self.proxy_pool if p not in self.used_proxies]
if not available:
raise Exception("没有可用的代理用于新账户")
proxy = random.choice(available)
self.account_proxies[account_id] = proxy
self.used_proxies.add(proxy)
return proxy
def release_account(self, account_id):
"""删除账户时释放代理"""
if account_id in self.account_proxies:
proxy = self.account_proxies.pop(account_id)
self.used_proxies.discard(proxy)
# 使用
manager = AccountProxyManager(residential_proxy_list)
for account in accounts:
proxy = manager.get_proxy_for_account(account.id)
# 该账户的所有操作都通过一个IP进行
策略4:自适应轮换
最先进的方法——根据目标网站的信号更改代理:
class AdaptiveRotator:
def __init__(self, proxy_list):
self.proxies = proxy_list
self.current_proxy = random.choice(proxy_list)
self.proxy_scores = {p: 100 for p in proxy_list} # 初始代理"健康"
def get_proxy(self):
return self.current_proxy
def report_result(self, success, response_code=200):
"""在每个请求后调用"""
if success and response_code == 200:
# 成功请求——稍微提高分数
self.proxy_scores[self.current_proxy] = min(100,
self.proxy_scores[self.current_proxy] + 1)
elif response_code == 429:
# 速率限制——大幅降低并轮换
self.proxy_scores[self.current_proxy] -= 30
self._rotate()
elif response_code == 403:
# 封禁——重置分数并轮换
self.proxy_scores[self.current_proxy] = 0
self._rotate()
elif response_code == 503:
# 可能的保护——降低并轮换
self.proxy_scores[self.current_proxy] -= 20
self._rotate()
def _rotate(self):
# 选择分数最高的代理
available = [(p, s) for p, s in self.proxy_scores.items() if s > 20]
if not available:
# 所有代理都"死了"——重置分数
self.proxy_scores = {p: 50 for p in self.proxies}
available = list(self.proxy_scores.items())
# 按分数加权选择
self.current_proxy = max(available, key=lambda x: x[1])[0]
print(f"轮换到分数为 {self.proxy_scores[self.current_proxy]} 的代理")
浏览器指纹及其在阻止中的作用
指纹是浏览器特征的总和,即使没有cookies也允许识别用户。如果您更改代理但指纹保持不变,保护系统会轻松将您的所有会话联系起来。
指纹包含什么
现代指纹包括数十个参数。以下是主要类别:
| 类别 | 参数 | 识别权重 |
|---|---|---|
| User-Agent | 浏览器、版本、操作系统 | 中等 |
| 屏幕 | 分辨率、色深、像素比 | 中等 |
| 字体 | 已安装字体列表 | 高 |
| WebGL | 渲染器、供应商、渲染哈希 | 非常高 |
| Canvas | 绘制图像的哈希 | 非常高 |
| Audio | AudioContext指纹 | 高 |
| 时区 | 时区、偏移 | 中等 |
| 语言 | navigator.languages | 中等 |
| 插件 | navigator.plugins | 低(在现代浏览器中) |
指纹和IP的一致性
至关重要的是指纹与IP地理位置相匹配。如果代理位于德国,指纹应该看起来像德国用户:
// 不一致的例子(不好):
// IP:德国
// 时区:America/New_York
// 语言:["ru-RU", "ru"]
// 这会引起怀疑
// 一致的指纹(好):
// IP:德国
// 时区:Europe/Berlin
// 语言:["de-DE", "de", "en-US", "en"]
指纹管理工具
对于认真的工作,使用专门的工具:
带Stealth的Playwright:
from playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={"server": "http://proxy:port", "username": "user", "password": "pass"}
)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
locale="de-DE",
timezone_id="Europe/Berlin",
geolocation={"latitude": 52.52, "longitude": 13.405},
permissions=["geolocation"]
)
page = context.new_page()
stealth_sync(page) # 应用stealth补丁
page.goto("https://target-site.com")
带puppeteer-extra的Puppeteer:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch({
args: [`--proxy-server=http://proxy:port`]
});
const page = await browser.newPage();
// 覆盖时区
await page.evaluateOnNewDocument(() => {
Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {
value: function() {
return { timeZone: 'Europe/Berlin' };
}
});
});
反检测浏览器
对于账户工作,通常使用反检测浏览器(Multilogin、GoLogin、Dolphin Anty等)。它们允许创建具有唯一指纹的隔离配置文件。每个配置文件都有自己的参数集、cookies、localStorage——完全隔离的环境。
反检测浏览器的优点是它们"开箱即用"解决指纹问题。缺点是成本和自动化复杂性(尽管许多都有API)。
行为模式:如何不看起来像机器人
即使有完美的指纹和干净的IP,由于非人类行为,您仍然可能被封禁。现代系统不仅分析技术参数,还分析与网站的交互模式。
时间延迟
人类不会以恒定间隔发出请求。添加具有正态分布的随机延迟:
import random
import time
import numpy as np
def human_delay(min_sec=1, max_sec=5, mean=2.5):
"""
生成类似人类的延迟。
使用对数正态分布——
大多数延迟很短,但有时会很长。
"""
delay = np.random.lognormal(mean=np.log(mean), sigma=0.5)
delay = max(min_sec, min(max_sec, delay))
return delay
def human_typing_delay():
"""按键之间的延迟"""
return random.uniform(0.05, 0.25)
# 使用
for url in urls:
response = requests.get(url, proxies=proxy)
process(response)
time.sleep(human_delay()) # 请求之间的随机暂停
模拟导航
人类不会直接通过直接链接转到产品页面。他会进入主页、使用搜索、浏览类别。模拟这个路径:
async def human_like_navigation(page, target_url):
"""模拟人类导航到目标页面"""
# 1. 进入主页
await page.goto("https://example.com")
await page.wait_for_timeout(random.randint(2000, 4000))
# 2. 有时滚动主页
if random.random() > 0.5:
await page.evaluate("window.scrollBy(0, 300)")
await page.wait_for_timeout(random.randint(1000, 2000))
# 3. 使用搜索或导航
if random.random() > 0.3:
search_box = await page.query_selector('input[type="search"]')
if search_box:
await search_box.type("search query", delay=100)
await page.keyboard.press("Enter")
await page.wait_for_timeout(random.randint(2000, 4000))
# 4. 转到目标页面
await page.goto(target_url)
# 5. 像人类一样滚动页面
await human_scroll(page)
async def human_scroll(page):
"""模拟人类滚动"""
scroll_height = await page.evaluate("document.body.scrollHeight")
current_position = 0
while current_position < scroll_height * 0.7: # 不到底部
scroll_amount = random.randint(200, 500)
await page.evaluate(f"window.scrollBy(0, {scroll_amount})")
current_position += scroll_amount
await page.wait_for_timeout(random.randint(500, 1500))
鼠标移动
某些系统跟踪鼠标移动。从点A到点B的直线移动是机器人的标志。人类以曲线移动鼠标,带有微调整:
import bezier
import numpy as np
def generate_human_mouse_path(start, end, num_points=50):
"""
生成类似人类的鼠标路径,
使用贝塞尔曲线和轻微噪声。
"""
# 贝塞尔曲线的控制点
control1 = (
start[0] + (end[0] - start[0]) * random.uniform(0.2, 0.4) + random.randint(-50, 50),
start[1] + (end[1] - start[1]) * random.uniform(0.2, 0.4) + random.randint(-50, 50)
)
control2 = (
start[0] + (end[0] - start[0]) * random.uniform(0.6, 0.8) + random.randint(-50, 50),
start[1] + (end[1] - start[1]) * random.uniform(0.6, 0.8) + random.randint(-50, 50)
)
# 创建贝塞尔曲线
nodes = np.asfortranarray([
[start[0], control1[0], control2[0], end[0]],
[start[1], control1[1], control2[1], end[1]]
])
curve = bezier.Curve(nodes, degree=3)
# 在曲线上生成点
points = []
for t in np.linspace(0, 1, num_points):
point = curve.evaluate(t)
# 添加微噪声
x = point[0][0] + random.uniform(-2, 2)
y = point[1][0] + random.uniform(-2, 2)
points.append((x, y))
return points
async def human_click(page, selector):
"""用类似人类的鼠标移动点击元素"""
element = await page.query_selector(selector)
box = await element.bounding_box()
# 目标点——不是中心,而是元素内的随机点
target_x = box['x'] + random.uniform(box['width'] * 0.2, box['width'] * 0.8)
target_y = box['y'] + random.uniform(box['height'] * 0.2, box['height'] * 0.8)
# 当前鼠标位置(或随机起始位置)
start_x = random.randint(0, 1920)
start_y = random.randint(0, 1080)
# 生成路径
path = generate_human_mouse_path((start_x, start_y), (target_x, target_y))
# 沿路径移动鼠标
for x, y in path:
await page.mouse.move(x, y)
await page.wait_for_timeout(random.randint(5, 20))
# 点击前的小暂停
await page.wait_for_timeout(random.randint(50, 150))
await page.mouse.click(target_x, target_y)
资源加载
真实浏览器不仅加载HTML,还加载CSS、JavaScript、图像、字体。如果您使用requests并仅请求HTML——这很可疑。使用headless浏览器时,此问题会自动解决,但使用HTTP客户端时需要考虑这一点。
为任务选择代理类型
不同类型的代理具有不同的特征,适合不同的任务。选择错误的代理是阻止的常见原因。
数据中心代理
数据中心代理是属于托管提供商的IP地址。它们可以通过属于大型数据中心的AS(自治系统)来识别。
优点:
- 高速度和稳定性
- 低成本
- 大型IP池
缺点:
- 易于检测
- 经常在黑名单中
- 不适合具有严格保护的网站
适合: SEO工具、可用性检查、无严格保护的API工作、测试。
住宅代理
住宅代理是通过合作伙伴计划或应用中的SDK提供的真实用户的IP地址。它们属于普通互联网提供商(ISP)。
优点:
- 看起来像普通用户
- 低欺诈分数
- 广泛的地理覆盖
- 难以检测
缺点:
- 成本更高(按流量付费)
- 速度取决于最终用户
- IP可能"消失"(用户关闭设备)
适合: 受保护网站的抓取、社交媒体工作、电子商务、任何需要不被检测的任务。
移动代理
移动代理是移动运营商的IP地址(MTS、Beeline、Megafon等及其他国家的类似运营商)。由于CGNAT技术,它们具有特殊地位。
优点:
- 网站的最大信任
- 一个IP被数千个真实用户使用——难以封禁
- 理想用于账户工作
- 按请求更改IP(重新连接到网络)
缺点:
- 最高成本
- 速度受限
- 地理选择较少
适合: 多账户、Instagram/Facebook/TikTok工作、账户注册、任何高封禁风险的任务。
比较表
| 参数 | 数据中心 | 住宅 | 移动 |
|---|---|---|---|
| 可检测性 | 高 | 低 | 非常低 |
| 速度 | 高 | 中等 | 低-中等 |
| 成本 | $ | $$ | $$$ |
| 用于社交媒体 | 不适合 | 适合 | 理想 |
| 用于抓取 | 简单网站 | 任何网站 | 过度 |
绕过保护的高级技术
当基本方法不起作用时,需要使用更复杂的技术。让我们考虑几种高级方法。
使用Cloudflare和类似保护
Cloudflare、Akamai、PerimeterX——这些系统使用JavaScript挑战来验证浏览器。简单的HTTP请求不会通过。解决方案选项:
1. 使用真实浏览器:
from playwright.sync_api import sync_playwright
def bypass_cloudflare(url, proxy):
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False, # 有时headless会被检测
proxy={"server": proxy}
)
page = browser.new_page()
page.goto(url)
# 等待通过检查(通常5-10秒)
page.wait_for_timeout(10000)
# 检查我们是否通过了
if "challenge" not in page.url:
# 保存cookies供后续请求使用
cookies = page.context.cookies()
return cookies
browser.close()
return None
2. 使用现成的解决方案:
# cloudscraper——用于绕过Cloudflare的库
import cloudscraper
scraper = cloudscraper.create_scraper(
browser={
'browser': 'chrome',
'platform': 'windows',
'desktop': True
}
)
scraper.proxies = {"http": proxy, "https": proxy}
response = scraper.get("https://protected-site.com")
验证码解决
如果网站显示验证码,有几种方法:
识别服务: 2Captcha、Anti-Captcha、CapMonster。它们为您解决验证码