当前位置: 首页 > news >正文

移动端H5爬虫:绕过APP限制+破解H5接口,数据采集新思路


干了11年爬虫,最近两年明显感觉APP越来越难爬了。从最开始的简单抓包就能用,到后来的SSL Pinning、签名算法、设备指纹,再到现在的加固壳、虚拟机保护,逆向一个APP的成本越来越高。

上个月帮一个朋友爬某电商APP的商品数据,光脱壳就花了3天,逆向签名算法又花了2天,结果刚写好爬虫,人家APP更新了,签名算法全变了,白忙活一场。

后来我换了个思路:既然APP这么难搞,那它内嵌的H5页面呢?

结果发现,90%以上的混合APP,核心业务逻辑其实都在H5里。而且H5的逆向难度比原生APP低太多了!今天就把我这半年踩坑总结出来的H5爬虫新思路分享给大家,保证看完就能上手。

一、为什么H5成了移动端爬虫的突破口?

先给大家看一张我总结的原生APP和H5爬虫难度对比图:

原生APP爬虫

脱壳/反编译

逆向Java/Kotlin代码

分析签名算法

处理设备指纹

绕过反调试

编写爬虫

H5爬虫

抓包分析接口

逆向JS代码

还原签名算法

编写爬虫

看到了吧?H5爬虫直接跳过了最耗时的脱壳和原生代码逆向步骤。而且JS代码即使被混淆了,也比Java字节码好理解得多。

更重要的是:

  • 更新频率低:H5页面更新不需要用户下载APP,所以接口和签名算法相对稳定
  • 跨平台通用:同一个H5页面在Android和iOS上是一样的,写一次爬虫就能用
  • 调试方便:Chrome开发者工具直接就能调试,不需要复杂的逆向环境

当然,H5也不是完全没有反爬,但相比原生APP来说,真的是小巫见大巫了。

二、第一步:绕过APP限制,抓包H5接口

很多人说:“我用Charles抓APP的H5页面,要么抓不到,要么全是乱码!”

这很正常,因为现在的APP基本都做了SSL Pinning。不过别担心,我有一套万能的抓包方案,99%的APP都能搞定。

2.1 基础环境准备

首先你需要准备:

  • 一台电脑(Windows/Mac都可以)
  • 安卓模拟器(推荐雷电模拟器9,Android 9系统)
  • Charles抓包工具
  • Frida动态注入工具

为什么用Android 9?因为Android 10及以上系统对证书的限制更严格,把证书放到系统目录需要Root,而Android 9只需要安装用户证书就能用。

2.2 SSL Pinning绕过

这是最关键的一步。我试过很多方法,包括Xposed的JustTrustMe模块,但最稳定的还是Frida脚本。

给大家分享一个我一直在用的万能SSL Pinning绕过脚本,适配99%的安卓APP:

/* Frida全局SSL Pinning绕过脚本 */Java.perform(function(){// 绕过OkHttp证书锁定try{constCertificatePinner=Java.use("okhttp3.CertificatePinner");CertificatePinner.check.implementation=function(){};}catch(e){}// 绕过原生URLConnection证书校验try{constHttpsURLConnection=Java.use("javax.net.ssl.HttpsURLConnection");HttpsURLConnection.setDefaultSSLSocketFactory.implementation=function(){};}catch(e){}// 绕过WebView网页SSL校验try{constWebViewClient=Java.use("android.webkit.WebViewClient");WebViewClient.onReceivedSslError.implementation=function(webview,errorHandler,sslError){errorHandler.proceed();};}catch(e){}// 全局信任所有证书try{constX509TrustManager=Java.use("javax.net.ssl.X509TrustManager");X509TrustManager.checkClientTrusted.implementation=function(){};X509TrustManager.checkServerTrusted.implementation=function(){};X509TrustManager.getAcceptedIssuers.implementation=function(){return[];};}catch(e){}console.log("SSL Pinning绕过成功!");});

使用方法也很简单:

  1. 在模拟器上安装Frida-server
  2. 电脑上执行命令:frida -U -f com.example.app -l ssl_bypass.js --no-pause
  3. 打开Charles,配置好代理和证书
  4. 现在你就能看到APP里所有的HTTPS请求了,包括H5页面的!

2.3 特殊情况处理

有些APP的WebView会绕过系统代理,导致Charles抓不到包。这时候有两种解决方法:

方法一:使用透明代理
在路由器上配置透明代理,所有流量都经过Charles。这个方法最彻底,但需要路由器支持。

方法二:Hook WebView的请求
用Frida Hook WebView的shouldInterceptRequest方法,强制所有请求走代理:

try{constWebView=Java.use("android.webkit.WebView");WebView.loadUrl.overload("java.lang.String").implementation=function(url){console.log("WebView加载URL:"+url);returnthis.loadUrl(url);};}catch(e){}

三、第二步:破解H5接口,逆向签名算法

抓包之后,你会发现大部分H5接口都有一个sign参数。这个参数就是我们需要破解的核心。

3.1 定位签名函数

定位签名函数有三个技巧,我按优先级排序:

技巧一:全局搜索关键词
在Chrome开发者工具的Sources面板,按Ctrl+Shift+F全局搜索:

  • sign=
  • signature
  • md5(
  • sha1(
  • encrypt(

90%的情况你都能直接找到签名生成的地方。

技巧二:查看请求发起者
在Network面板,找到你要分析的请求,点击Initiator列,就能直接跳转到发起请求的JS代码。然后在这行代码打个断点,刷新页面,就能看到调用栈了。

技巧三:XHR断点
如果上面两个方法都不行,就在Sources面板的XHR/fetch Breakpoints里添加断点,输入接口的部分URL。当请求发起时,调试器会自动断住,然后你就能一步步回溯找到签名函数。

3.2 常见签名算法分析

我分析过上百个H5接口的签名算法,发现90%以上都是下面这几种:

类型一:参数排序+MD5
这是最常见的一种,也是最好破解的。

functiongenerateSign(params){// 1. 获取所有参数名,过滤掉sign本身,然后按字母顺序排序constsortedKeys=Object.keys(params).filter(k=>k!=='sign').sort();// 2. 将参数按key=value格式拼接,用&连接letsignStr=sortedKeys.map(k=>`${k}=${params[k]}`).join('&');// 3. 在末尾追加密钥(这就是签名的关键!密钥硬编码在前端)signStr+='&key=WaterCard@2024#SecretKey';// 4. 对拼接后的字符串进行MD5哈希,得到签名returnmd5(signStr).toLowerCase();}

类型二:参数拼接+HMAC-SHA256
比MD5稍微复杂一点,但原理一样。

functiongenerateSign(params,secretKey){constsortedKeys=Object.keys(params).sort();constsignStr=sortedKeys.map(k=>`${k}=${params[k]}`).join('&');returnCryptoJS.HmacSHA256(signStr,secretKey).toString(CryptoJS.enc.Hex);}

类型三:时间戳+随机数+签名
这种会在参数里加上timestampnonce,防止重放攻击。

functiongenerateSign(params){params.timestamp=Date.now().toString();params.nonce=Math.random().toString(36).substring(2,15);constsortedKeys=Object.keys(params).filter(k=>k!=='sign').sort();constsignStr=sortedKeys.map(k=>`${k}=${params[k]}`).join('&');returnmd5(signStr+'app_secret_key').toUpperCase();}

3.3 反调试绕过

有些H5页面会加反调试,最常见的就是无限debugger语句。当你打开开发者工具时,页面会一直卡在debugger状态。

绕过方法很简单:

  1. 在Sources面板,找到debugger语句所在的行
  2. 右键点击行号,选择"Never pause here"
  3. 刷新页面,debugger就不会再断住了

如果是动态生成的debugger语句(通过eval执行),可以用这个Frida脚本直接禁用所有debugger:

Java.perform(function(){constWebView=Java.use("android.webkit.WebView");WebView.evaluateJavascript.implementation=function(script,callback){if(script.indexOf('debugger')!==-1){script=script.replace(/debugger;/g,'');}returnthis.evaluateJavascript(script,callback);};});

四、完整实战案例:某电商APP商品列表爬虫

光说不练假把式,下面我用一个真实的电商APP来演示完整的H5爬虫流程。

4.1 抓包分析接口

首先用Charles抓包,找到商品列表接口:

GET https://api.example.com/v2/goods/list? category_id=1001& page=1& page_size=20& timestamp=1716624000& nonce=abcdef123456& sign=7a9f3d2b8c6e4a0d1f5b7c9e3a2d8f1b

可以看到有timestampnoncesign三个动态参数。

4.2 逆向签名算法

按照上面的方法,全局搜索sign=,很快找到了签名函数:

functiongetSign(params){varkeys=Object.keys(params).sort();varstr='';for(vari=0;i<keys.length;i++){str+=keys[i]+'='+params[keys[i]]+'&';}str=str.substring(0,str.length-1);str+='APP_SECRET_2026';returnmd5(str).toLowerCase();}

签名算法很简单:

  1. 参数按键名排序
  2. 拼接成key=value&key=value格式
  3. 末尾加上固定密钥APP_SECRET_2026
  4. MD5加密,转小写

4.3 Python爬虫实现

现在我们可以用Python来实现这个签名算法,然后编写爬虫:

importrequestsimporthashlibimporttimeimportrandomdefgenerate_sign(params):# 参数按键名排序sorted_keys=sorted(params.keys())# 拼接成key=value&key=value格式sign_str='&'.join([f'{k}={params[k]}'forkinsorted_keys])# 末尾加上密钥sign_str+='APP_SECRET_2026'# MD5加密,转小写returnhashlib.md5(sign_str.encode('utf-8')).hexdigest().lower()defget_goods_list(category_id,page=1,page_size=20):url='https://api.example.com/v2/goods/list'# 构造参数params={'category_id':category_id,'page':page,'page_size':page_size,'timestamp':str(int(time.time())),'nonce':''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789',k=12))}# 生成签名params['sign']=generate_sign(params)# 发送请求headers={'User-Agent':'Mozilla/5.0 (Linux; Android 9; SM-G960F Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36','Referer':'https://m.example.com/goods/list'}response=requests.get(url,params=params,headers=headers)returnresponse.json()# 测试if__name__=='__main__':goods_list=get_goods_list(1001,page=1)print(goods_list)

就是这么简单!不到50行代码就搞定了。如果是原生APP的话,至少要写几百行,还得处理各种设备指纹和反调试。

五、反反爬进阶策略

当然,有些H5页面的反爬会比较严格。下面分享几个我常用的进阶技巧:

5.1 设备指纹伪装

现在很多H5页面会收集设备信息生成设备指纹,包括:

  • 屏幕分辨率
  • 浏览器版本
  • 系统版本
  • Canvas指纹
  • WebGL指纹

如果你的爬虫请求的设备指纹都一样,很容易被封。

解决方法:使用Playwright或Puppeteer无头浏览器,每次请求都随机生成不同的设备指纹。

fromplaywright.sync_apiimportsync_playwrightimportrandomdefget_random_user_agent():user_agents=['Mozilla/5.0 (Linux; Android 9; SM-G960F Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/120.0.6099.230 Mobile Safari/537.36','Mozilla/5.0 (Linux; Android 10; SM-G973F Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/121.0.6167.164 Mobile Safari/537.36','Mozilla/5.0 (Linux; Android 11; SM-G991B Build/RP1A.200720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.119 Mobile Safari/537.36']returnrandom.choice(user_agents)withsync_playwright()asp:browser=p.chromium.launch(headless=True)context=browser.new_context(user_agent=get_random_user_agent(),viewport={'width':360,'height':640},device_scale_factor=2)page=context.new_page()# 注入脚本,修改navigator.webdriver属性page.add_init_script(""" Object.defineProperty(navigator, 'webdriver', { get: () => false }); """)page.goto('https://m.example.com/goods/list')print(page.content())browser.close()

5.2 行为模拟

有些H5页面会检测用户行为,比如鼠标移动、点击、滚动等。如果你的爬虫只是简单地请求接口,很容易被识别。

解决方法:用Playwright模拟真实用户行为:

# 模拟滚动页面foriinrange(5):page.evaluate(f'window.scrollTo(0,{i*200})')page.wait_for_timeout(random.randint(500,1000))# 模拟点击商品page.locator('.goods-item').nth(0).click()page.wait_for_timeout(random.randint(1000,2000))# 返回上一页page.go_back()page.wait_for_timeout(random.randint(500,1000))

5.3 代理池与IP轮换

IP限制是最常见的反爬手段。解决方法就是使用代理池,每次请求都换一个IP。

推荐使用住宅代理,比数据中心代理更难被检测到。我自己用的是四叶天代理,稳定性还不错。

proxies={'http':'http://username:password@proxy.example.com:8080','https':'http://username:password@proxy.example.com:8080'}response=requests.get(url,params=params,headers=headers,proxies=proxies)

六、总结

今天给大家分享了移动端H5爬虫的新思路,核心就是:绕过APP的SSL Pinning,直接抓包H5接口,然后逆向JS代码破解签名算法

相比原生APP爬虫,H5爬虫有以下优势:

  • 开发成本低,周期短
  • 维护简单,接口更新频率低
  • 跨平台通用,一次编写到处运行
  • 调试方便,不需要复杂的逆向环境

当然,H5爬虫也不是万能的。有些APP的核心数据还是通过原生接口返回的,这时候就不得不逆向原生APP了。但对于大部分场景来说,H5爬虫已经足够用了。

最后提醒大家:爬虫技术是一把双刃剑,一定要在法律允许的范围内使用。只爬取公开的数据,不要侵犯他人的隐私和商业利益。

http://www.jsqmd.com/news/882462/

相关文章:

  • RustDesk自建服务器防白嫖实战:ID准入控制与密钥安全加固
  • Unity与Android Studio协同开发实战指南
  • PINNSR-DA框架:从噪声数据中自动发现颗粒材料本构方程
  • 如何快速解决视频字幕不同步问题:video-subtitle-extractor终极指南
  • 如何让Windows 11真正“吃上“安卓应用?探索WSA的跨平台融合之路
  • AIMS-PAX:基于主动学习的并行化机器学习力场高效构建指南
  • Unity与Android Studio联合开发:AAR集成与双向调用实战指南
  • 逆向工程能力成长路线图:Windows内核、安卓安全与游戏协议实战
  • 探索 IwaraDownloadTool:从手动下载到智能嗅探的实践路径
  • Unity UI适配终极指南:CanvasScaler原理与SafeArea实战
  • Unity触控开发实战:TouchScript零基础集成与多点手势详解
  • Godot与AI深度协作:重构游戏开发工作流的5步实践
  • MinIO CVE-2023-28432漏洞深度解析:健康检查接口泄露根密钥
  • 简历离职原因避坑指南:HR直呼“加分”的标准答案(附反例吐槽)
  • Unity XR中Point Light不生效的根源与解决方案
  • 2026年亲测|7款必备降AI率工具推荐,论文快速过AI检测不踩坑 - 降AI实验室
  • Unity XR中Point Light不生效的四大根源与解决路径
  • 实时机器学习中的可扩展差分隐私:分层聚合与自适应噪声调度实践
  • 猫抓:浏览器资源嗅探工具终极指南 - 5步轻松下载全网视频音频资源
  • Keil µVision中实现函数级编译时间戳追踪方案
  • ESP32四次握手捕获实战:嵌入式Wi-Fi安全调试与协议验证
  • 5分钟解锁QQ音乐加密文件:Mac用户的免费音频转换神器
  • 广义随机占优:多准则算法比较的稳健统计框架
  • 三步免费获取百度网盘真实下载链接,告别限速烦恼的完整指南
  • 用GPT-4玩转《我的世界》:手把手教你复现VOYAGER智能体的核心代码逻辑
  • TrueAsync Server 为 PHP 带来了原生的高性能 HTTP 服务器
  • Unity运行时Lightmap切换:不重烘的光照方案动态替换
  • ParsecVDD虚拟显示器驱动技术深度解析:Windows IddCx架构下的性能革命
  • Unity UI零运行时适配:基于Viewport锚点与自定义Shader的生产级方案
  • 机器学习加速辐照材料缺陷预测:从团簇动力学到神经网络代理模型