吃透爬虫反爬:从JS加密、滑块验证到指纹识别,一文破解90%网站防御
JS加密:逆向算法,复现参数;滑块验证:模拟真人轨迹,别太规律;指纹识别:修改或替换浏览器指纹,避免被标记;最后用代理+延迟+Cookie池做组合防御。现在我用这套方法爬过电商、自媒体、招聘等平台,除了极少数用AI风控的网站(比如支付宝),90%的网站都能突破。如果你们遇到“JS混淆看不懂”“滑块一直失败”等问题,评论区留具体场景,我会用实战经验帮你拆解!
上个月爬某电商平台的商品数据,前3天被封了3次IP、2次账号——一开始卡在JS加密的sign参数,好不容易破解后又遇到滑块验证,过了滑块又被指纹识别打回原形。折腾一周后,把JS逆向、滑块轨迹、指纹修改这三板斧吃透,现在爬同类网站成功率稳定在95%以上。这篇文章把我踩过的坑、破解的思路和可直接复用的代码全分享出来,新手也能跟着突破大部分网站的反爬防御。
一、JS加密逆向:破解API参数的“密码锁”
现在90%的网站都会对API请求参数做加密(比如sign、token),直接传明文参数会返回403或400。破解JS加密的核心不是“看懂所有JS代码”,而是“找到加密函数、提取密钥、复现逻辑”。
实战案例:破解某短视频平台的sign参数
以某短视频平台的商品列表API为例,抓包发现请求参数里有个sign字段,每次请求都不一样,其他参数(timestamp、page)是明文。
步骤1:定位加密函数
- 打开Chrome开发者工具(F12)→ Network,找到目标API请求,复制
sign的值(比如abc123def456); - 切换到Sources → 按Ctrl+Shift+F,粘贴
sign或关键词getSign/sign=搜索,找到加密相关的JS代码; - 发现一段混淆后的代码(变量名是a/b/c,典型的混淆特征):
function e(t) { var e = "doubao_secret_2025"; // 密钥藏在这里 return md5(t + e) } var a = Date.parse(new Date()) / 1000; // timestamp var c = e(a + "page=" + b); // sign = md5(timestamp+"page="+page+密钥)
步骤2:复现加密逻辑(Python实现)
不管JS多混淆,只要找到加密算法(MD5/SHA256/AES)和密钥,就能用Python复现:
import requests
import time
import hashlib
def generate_sign(timestamp, page):
"""复现JS加密逻辑:sign = md5(timestamp+"page="+page+密钥)"""
secret = "doubao_secret_2025" # 从JS中提取的密钥
sign_str = f"{timestamp}page={page}{secret}"
# MD5加密,转小写(JS里md5默认返回小写)
sign = hashlib.md5(sign_str.encode()).hexdigest().lower()
return sign
def crawl_encrypted_api():
url = "https://api.example.com/goods/list"
page = 1
timestamp = int(time.time()) # 与JS一致的时间戳(秒级)
sign = generate_sign(timestamp, page)
params = {
"timestamp": timestamp,
"page": page,
"sign": sign,
"size": 20
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
print("JS加密破解成功!返回数据:")
print(response.json()["data"][0]["title"])
else:
print(f"破解失败,状态码:{response.status_code}")
if __name__ == "__main__":
crawl_encrypted_api()
踩坑记录:
- 混淆JS看不懂? 用Chrome的“Pretty Print”({}按钮)格式化代码,再用断点调试(在加密函数前打√),一步步看参数传递;
- 密钥藏在其他JS文件? 搜索
secret/key/token等关键词,或跟踪e函数的调用链; - 时间戳不一致? JS里用
Date.parse(new Date())/1000(秒级),Python别用毫秒级的time.time()。
二、滑块验证突破:模拟真人轨迹,骗过“机器检测”
滑块验证(比如极验、腾讯防水墙)是网站防爬虫的“守门员”,核心难点不是找缺口,而是模拟真人的滑动轨迹——匀速滑动、直线滑动都会被识别为爬虫。
实战案例:突破极验4.0滑块验证
步骤1:找缺口位置(OpenCV图像识别)
先用OpenCV对比原图和缺口图,定位缺口的x坐标:
import cv2
import numpy as np
def find_gap(img_path, gap_img_path):
"""
找缺口位置:返回缺口的x坐标
img_path: 原图路径
gap_img_path: 缺口图路径
"""
# 读取图片
img = cv2.imread(img_path, 0)
gap_img = cv2.imread(gap_img_path, 0)
# 用模板匹配找缺口
res = cv2.matchTemplate(img, gap_img, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 缺口的x坐标(max_loc是匹配到的左上角坐标)
gap_x = max_loc[0]
return gap_x
步骤2:模拟真人滑动轨迹(关键!)
真人滑动的特点:先加速、再匀速、最后减速,轨迹有微小偏移(不是绝对直线):
import random
from playwright.sync_api import sync_playwright
def human_slide_track(distance):
"""生成真人滑动轨迹:返回[(x1,y1), (x2,y2), ...]"""
track = []
current_x = 0
# 总时间控制在1-1.5秒,步数20-30步
steps = random.randint(20, 30)
for step in range(steps):
# 加速阶段(前1/3):x增量大
if step < steps/3:
x = random.uniform(1, 3) * (distance/steps)
# 匀速阶段(中间1/3):x增量稳定
elif step < steps*2/3:
x = random.uniform(0.8, 1.2) * (distance/steps)
# 减速阶段(最后1/3):x增量小
else:
x = random.uniform(0.3, 0.8) * (distance/steps)
current_x += x
# y坐标加微小偏移(真人不会绝对水平)
y = random.uniform(-1, 1)
track.append((current_x, y))
# 最后补到目标距离,避免差一点
track.append((distance, random.uniform(-1, 1)))
return track
def slide_verification():
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.geetest.com/demo/slide-float.html") # 极验测试页
# 等待滑块加载
page.wait_for_selector(".geetest_slider_button")
# 保存原图和缺口图(实际爬取时从页面获取图片URL)
page.screenshot(path="full.png") # 原图
page.click(".geetest_slider_button") # 点击滑块显示缺口
page.wait_for_timeout(1000)
page.screenshot(path="gap.png") # 缺口图
# 找缺口位置
gap_x = find_gap("full.png", "gap.png")
print(f"缺口x坐标:{gap_x}")
# 生成轨迹
track = human_slide_track(gap_x - 10) # 减10是滑块本身的宽度
# 模拟滑动
slider = page.locator(".geetest_slider_button")
slider.hover()
page.mouse.down() # 按下鼠标
for x, y in track:
page.mouse.move(x, y + 50) # y固定50(滑块高度),加微小偏移
page.wait_for_timeout(random.randint(10, 30)) # 每步间隔10-30ms
page.mouse.up() # 松开鼠标
page.wait_for_timeout(2000)
# 验证是否成功
if page.locator(".geetest_success_radar").is_visible():
print("滑块验证成功!")
else:
print("滑块验证失败,可能轨迹太规律")
browser.close()
if __name__ == "__main__":
slide_verification()
踩坑记录:
- 轨迹太规律被检测? 加随机偏移(x/y都加±1),总时间控制在1-1.5秒(太快像机器);
- 缺口识别不准? 用
cv2.TM_CCOEFF_NORMED匹配算法,或对图片做灰度化/降噪处理; - Playwright被识别? 加
--disable-blink-features=AutomationControlled参数(禁用自动化标记)。
三、指纹识别绕过:修改浏览器“身份证”,避免被标记
浏览器指纹是网站识别爬虫的“终极手段”——即使换IP、换Cookie,指纹相同还是会被封。常见的指纹类型有:Canvas指纹、WebGL指纹、User-Agent组合、字体列表等。
实战案例:修改Canvas+WebGL指纹
步骤1:固定Canvas指纹
Canvas指纹是通过绘制图形生成的唯一哈希值,修改toDataURL方法固定返回值:
from playwright.sync_api import sync_playwright
def modify_canvas_fingerprint():
with sync_playwright() as p:
# 启动浏览器,禁用自动化检测
browser = p.chromium.launch(
headless=False,
args=[
"--disable-blink-features=AutomationControlled",
"--disable-webgl", # 禁用WebGL指纹(简单粗暴)
"--lang=zh-CN"
]
)
page = browser.new_page()
# 注入JS修改Canvas指纹
page.add_init_script('''
// 重写Canvas的toDataURL和getImageData方法
Object.defineProperty(HTMLCanvasElement.prototype, 'toDataURL', {
value: function() {
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
}
});
Object.defineProperty(HTMLCanvasElement.prototype, 'getImageData', {
value: function() {
return { data: new Uint8ClampedArray([255, 255, 255, 255]) };
}
});
''')
# 访问指纹检测网站验证
page.goto("https://browserleaks.com/canvas")
print("请查看Canvas指纹是否固定(哈希值不变),按回车关闭...")
input()
browser.close()
步骤2:搭建指纹池(进阶)
如果需要多个不同指纹,用指纹池随机选择User-Agent+Canvas值:
# 指纹池:User-Agent + Canvas哈希值
FINGERPRINT_POOL = [
{
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"canvas_hash": "abc123def456"
},
{
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
"canvas_hash": "789xyz012abc"
}
]
def random_fingerprint():
"""随机选一个指纹"""
return random.choice(FINGERPRINT_POOL)
# 在Playwright中使用随机指纹
def use_random_fingerprint():
fingerprint = random_fingerprint()
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page(user_agent=fingerprint["user_agent"])
# 注入对应Canvas指纹
page.add_init_script(f'''
Object.defineProperty(HTMLCanvasElement.prototype, 'toDataURL', {{
value: function() {{
return 'data:image/png;base64,{fingerprint["canvas_hash"]}';
}}
}});
''')
page.goto("https://browserleaks.com/canvas")
input()
browser.close()
踩坑记录:
- 指纹修改不彻底? 除了Canvas,还要改WebGL(禁用或修改)、字体列表(注入假字体);
- User-Agent和系统不匹配? 比如User-Agent是Windows,但页面检测到是Mac,会被识别——指纹池里要保证User-Agent和系统信息一致。
四、综合防御破解:组合拳突破90%网站
单一方法很难突破复杂反爬,需要用“代理+加密破解+滑块+指纹+延迟”的组合拳:
实战组合方案(可直接复用)
import requests
import time
import random
from playwright.sync_api import sync_playwright
import hashlib
# 1. 代理配置(隧道代理,自动换IP)
PROXY_HOST = "http-dyn.abuyun.com"
PROXY_PORT = "9020"
PROXY_USER = "你的代理账号"
PROXY_PASS = "你的代理密码"
proxies = {
"http": f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}",
"https": f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
}
# 2. JS加密函数
def generate_sign(timestamp, page):
secret = "site_secret_key"
sign_str = f"{timestamp}page={page}{secret}"
return hashlib.md5(sign_str.encode()).hexdigest()
# 3. 主爬取函数
def crawl_with_defense():
# 第一步:用Playwright过滑块验证,获取Cookie
cookie = None
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False,
args=["--disable-blink-features=AutomationControlled"]
)
page = browser.new_page()
page.goto("https://www.target.com/login")
# 过滑块验证(省略滑块代码,参考前面的slide_verification)
# ...
# 获取登录后的Cookie
cookie = page.context.cookies()[0]["value"]
browser.close()
# 第二步:用requests+代理+加密参数爬取数据
headers = {
"User-Agent": random.choice([
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
]),
"Cookie": f"session={cookie}",
"Referer": "https://www.target.com/"
}
for page in range(1, 10):
timestamp = int(time.time())
sign = generate_sign(timestamp, page)
params = {"timestamp": timestamp, "page": page, "sign": sign}
try:
response = requests.get(
"https://api.target.com/data",
headers=headers,
params=params,
proxies=proxies,
timeout=10
)
if response.status_code == 200:
print(f"第{page}页爬取成功,数据量:{len(response.json()['data'])}")
else:
print(f"第{page}页失败,状态码:{response.status_code}")
# 随机延迟1-3秒,模拟真人
time.sleep(random.uniform(1, 3))
except Exception as e:
print(f"第{page}页异常:{str(e)}")
# 代理失效,重试一次
time.sleep(5)
continue
if __name__ == "__main__":
crawl_with_defense()
五、避坑与合规提醒
- 别用免费代理:免费代理90%是爬虫IP,反而容易被封,练手用阿布云/快代理的试用流量;
- 频率控制:即使有代理,也别每秒爬10次——大部分网站的频率限制是“1分钟≤20次”;
- 合规第一:
- 不爬取隐私数据(身份证、手机号、银行卡);
- 遵守robots.txt协议(虽然法律不强制,但能减少麻烦);
- 不爬取付费内容或 copyrighted 数据(比如付费课程、电影);
- 爬取前看网站的《用户协议》,明确禁止爬取的别碰。
总结
反爬的本质是“网站识别机器行为”,破解的核心就是“让爬虫行为无限接近真人”:
- JS加密:逆向算法,复现参数;
- 滑块验证:模拟真人轨迹,别太规律;
- 指纹识别:修改或替换浏览器指纹,避免被标记;
- 最后用代理+延迟+Cookie池做组合防御。
现在我用这套方法爬过电商、自媒体、招聘等平台,除了极少数用AI风控的网站(比如支付宝),90%的网站都能突破。如果你们遇到“JS混淆看不懂”“滑块一直失败”等问题,评论区留具体场景,我会用实战经验帮你拆解!
更多推荐

所有评论(0)