入门级反爬看 UA 和 IP,进阶反爬看验证码、参数加密、行为风控。这一篇讲目前最常见的三种进阶反爬手段怎么应对。
一、滑动验证码
滑块验证码是目前应用最广的人机验证方式,常见的产品有:
| 产品 | 使用方 | 难度 |
|---|---|---|
| 极验 Geetest | 知乎、B站、斗鱼 | ⭐⭐⭐ 标准版 |
| 阿里云滑块 | 淘宝、1688 | ⭐⭐⭐⭐ |
| 腾讯防水墙 | 微信、QQ | ⭐⭐⭐ |
滑动验证的校验逻辑
用户操作:
拖动滑块 → 前端记录轨迹数据(坐标、速度、加速度)
↓
服务器校验:
① 轨迹是否像人类操作(不是直线)
② 拖动的距离是否正确(缺口位置)
③ 浏览器环境特征(WebDriver、Canvas指纹)
方案一:Selenium + 模拟拖动
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
import time
driver = webdriver.Chrome()
driver.get("https://example.com/login")
# 等待滑块出现
slider = driver.find_element(By.CLASS_NAME, "geetest_slider_button")
# 模拟人类拖动(关键:不是匀速直线!)
action = ActionChains(driver)
action.click_and_hold(slider).perform()
# 分段拖动:先慢后快再慢,模拟人类
track = [
(0, 0), # 起始
(50, 3), # 开始移动,轻微上下抖动
(100, -2),
(150, 5),
(200, 1),
(230, -3),
(250, 0), # 接近目标,放慢
(258, 0),
(262, 0), # 到达
]
for x, y in track:
action.move_by_offset(x, y).perform()
time.sleep(0.01) # 每步间隔10ms
action.release().perform()
time.sleep(2)
方案二:机器学习识别缺口位置
import cv2
import numpy as np
def find_gap_position(bg_image_path, slider_image_path):
"""识别滑块缺口位置(像素坐标)"""
# 读取背景图和滑块图
bg = cv2.imread(bg_image_path, 0) # 灰度图
slider = cv2.imread(slider_image_path, 0)
# 模板匹配,找到滑块在背景中的位置
result = cv2.matchTemplate(bg, slider, cv2.TM_CCOEFF_NORMED)
_, _, _, max_loc = cv2.minMaxLoc(result)
# 缺口左上角坐标
x, y = max_loc
return x
# 使用
gap_x = find_gap_position("bg.jpg", "slider.png")
print(f"缺口位置在 {gap_x} 像素处")
方案三:直接用打码平台(省事)
国内主流的打码平台都支持滑块验证码识别,价格通常 1-3 分钱/次:
# 以某打码平台为例
import requests
def solve_slider_captcha(bg_url, slider_url):
"""调用打码平台识别滑块"""
api_key = "your_api_key"
data = {
"key": api_key,
"method": "slide_captcha",
"bg": bg_url, # 背景图URL
"slider": slider_url # 滑块图URL
}
resp = requests.post("http://api.damaplatform.com/solve", json=data)
result = resp.json()
if result["success"]:
# 返回缺口 x 坐标,直接传给浏览器
return result["data"]["x"]
else:
print(f"识别失败: {result.get('message')}")
return None
建议: 免费折腾的时间成本往往比付费高。如果只是需要采集数据,打码平台几块钱就能解决大部分验证码。
二、请求参数加密
越来越多的 App 和 Web 端对请求参数做了加密,直接抓包看到的是一堆乱码。
1. 常见加密方式
① 参数签名:params + secret → MD5/SHA256 → sign
服务端用同样的方式算一遍,不一致就拒绝
② 请求体加密:整个 body 用 AES/RSA 加密
服务端解密后再处理
③ 请求头自定义:x-timestamp、x-nonce、x-sign
防重放攻击
2. 逆向找到加密代码
# 通过抓包定位加密入口
# 方法:在 Fiddler/Charles 中找加密参数
# 然后在 JS 代码中搜索关键字
# 常见的搜索关键词:
search_keywords = [
"sign", "signature", "token",
"encrypt", "encode",
"md5", "sha256", "aes", "rsa",
"nonce", "timestamp",
]
3. 用 Python 复现加密逻辑
import hashlib
import time
import random
import string
def generate_sign(params, secret="your_secret_key"):
"""复现 App 的签名算法"""
# 1. 参数按字典序排序
sorted_keys = sorted(params.keys())
# 2. 拼接成字符串
raw_parts = []
for k in sorted_keys:
raw_parts.append(f"{k}={params[k]}")
# 3. 拼接密钥
raw_str = "&".join(raw_parts) + secret
# 4. MD5 加密
sign = hashlib.md5(raw_str.encode()).hexdigest()
return sign
def generate_nonce(length=10):
"""生成随机字符串(防重放)"""
return "".join(random.choices(string.ascii_letters + string.digits, k=length))
def make_request(url, params):
"""带签名的请求"""
params["timestamp"] = int(time.time())
params["nonce"] = generate_nonce()
params["sign"] = generate_sign(params)
resp = requests.get(url, params=params)
return resp.json()
4. 逆向工具推荐
| 工具 | 用途 | 难度 |
|---|---|---|
| Fiddler + FiddlerScript | 打断点调试 JS | ⭐⭐ |
| Chrome DevTools | 在 Sources 中搜索加密函数 | ⭐⭐ |
| AST(抽象语法树) | 反混淆加密 JS 代码 | ⭐⭐⭐⭐ |
| PyExecJS | 用 Python 执行 JS 代码 | ⭐⭐⭐ |
| JSDom | 模拟浏览器环境执行 JS | ⭐⭐⭐ |
最实用的方法:用 PyExecJS 直接调用网站的加密 JS:
import execjs
import requests
# 加载网站的加密 JS 文件(从 Chrome Sources 中复制出来)
with open("encrypt.js", "r", encoding="utf-8") as f:
js_code = f.read()
# 编译 JS
ctx = execjs.compile(js_code)
# 调用 JS 中的加密函数
params = {"username": "test", "password": "123456"}
sign = ctx.call("encryptSign", params)
print(f"加密签名: {sign}")
三、风控绕过——不要像机器人一样访问
现在的大厂(淘宝、抖音、美团)都有风控系统,技术上的反爬只是一个层面,行为层面的风控往往更敏感。
1. 风控系统会检测什么
① 访问模式
❌ 每隔 3 秒整点访问 → 像定时任务
✅ 随机间隔 2-5 秒 → 像人类
② 浏览路径
❌ 直接访问目标页面 → 不经过首页
✅ 先首页 → 列表页 → 详情页 → 像正常用户
③ 鼠标/手势
❌ 无鼠标轨迹 → Selenium 特征
✅ 有曲线移动 → 人类行为
④ 账户行为
❌ 一个账号每天爬 10 万次
✅ 多个账号分散爬
2. 模拟人类行为
import time
import random
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
def human_like_scroll(driver):
"""模拟人类滚动页面"""
for _ in range(random.randint(3, 6)):
scroll_distance = random.randint(200, 500)
driver.execute_script(f"window.scrollBy(0, {scroll_distance});")
time.sleep(random.uniform(0.5, 1.5))
def human_like_click(driver, element):
"""模拟人类点击"""
# 先移动鼠标到元素
action = webdriver.ActionChains(driver)
action.move_to_element(element).perform()
time.sleep(random.uniform(0.2, 0.8))
element.click()
def random_delay():
"""随机延时,避免固定间隔"""
time.sleep(random.uniform(1.5, 4.5))
# 使用
driver.get("https://example.com")
time.sleep(random.uniform(2, 4)) # 模拟页面加载延迟
# 模拟人类浏览
human_like_scroll(driver)
human_like_click(driver, driver.find_element(By.LINK_TEXT, "下一页"))
random_delay()
3. 多账号轮换
import itertools
class AccountPool:
"""账号池,轮流使用"""
def __init__(self):
self.accounts = [
{"phone": "1380001", "token": "token_a"},
{"phone": "1380002", "token": "token_b"},
{"phone": "1380003", "token": "token_c"},
]
self.pool = itertools.cycle(self.accounts)
def get_account(self):
return next(self.pool)
def mark_banned(self, account):
print(f"账号 {account['phone']} 被封,移除")
self.accounts.remove(account)
pool = AccountPool()
for i in range(100):
account = pool.get_account()
headers = {"Authorization": f"Bearer {account['token']}"}
resp = requests.get("https://api.example.com/data", headers=headers)
# ...
time.sleep(random.uniform(2, 5))
4. 风控降级策略
当爬虫触发风控时,不要硬刚,降低采集频率:
class AdaptiveCrawler:
"""自适应降级爬虫"""
def __init__(self, initial_delay=1):
self.delay = initial_delay
self.min_delay = 0.5
self.max_delay = 30
self.success_count = 0
self.fail_count = 0
def request(self, url):
"""自适应请求"""
time.sleep(self.delay)
resp = requests.get(url, headers=headers)
if resp.status_code == 200:
self.success_count += 1
self.fail_count = 0
# 连续成功 10 次,适当加速
if self.success_count >= 10:
self.delay = max(self.min_delay, self.delay * 0.8)
self.success_count = 0
elif resp.status_code in [403, 429]:
self.fail_count += 1
self.success_count = 0
# 触发风控,减速
self.delay = min(self.max_delay, self.delay * 2)
print(f"触发风控,降级到 {self.delay}s 间隔")
return resp
crawler = AdaptiveCrawler(initial_delay=2)
data = crawler.request("https://example.com/api")
四、反爬对抗的真正原则
反爬对抗不是技术的较量,而是成本和收益的博弈
网站为什么反爬?
怕你影响服务器性能、怕数据被竞对采集
如果你:
爬得慢、像个人、不是大规模批量
大多数网站根本就不管你
实用的反反爬策略优先级:
先试试不加任何伪装 → 被拦了再加UA
→ 被拦了加延时
→ 被拦了换代理IP
→ 被拦了处理验证码
→ 被拦了逆向加密参数
→ 被拦了模拟行为风控
从最简单的方法开始,够用就行,不要一上来就搞签名逆向和机器学习。
💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。

3884

被折叠的 条评论
为什么被折叠?



