Skip to content

Rainynitesky/kaipanla-data-parser

Repository files navigation

开盘啦数据解析项目

项目目标

抓取开盘啦App(com.aiyu.kaipanla)的板块行情数据板块-个股关联数据,输出为CSV。

板块列表通过 RealRankingInfo API 动态分页获取(不再依赖静态 kaipanla-plates-267.json 文件),数量随市场变化。


1. 核心发现(已确认,勿重复验证)

1.1 267板块列表走Socket不走HTTP

  • PlateTCConfig HTTP接口只返回58个行业板块
  • 完整267板块(含概念板块)通过TCP Socket + TLS + Protobuf传输
  • Socket服务器: 124.71.166.244:14000, 1.94.128.64:14000, 60.204.212.123:14000
  • IP获取接口: getsockip.longhuvip.com/getIPList?UserID=...&Token=... (HTTP, 已验证可用)
  • 板块列表已通过 RealRankingInfo API 动态获取,无需静态映射文件

1.2 Socket协议栈

App → AES加密(protobuf数据) → TLS加密 → TCP → 服务器
  • DEX中发现 getSocketAESKey/getHttpAESKey → Socket通信有AES加密层
  • libsockSign.so 负责签名计算,AuthReq.signature字段需要此库生成

1.3 App加固

  • libzxprotect.so 加固框架,导致:
    • Frida Java.perform 报 "Java is not defined" — Java层hook完全不可用
    • Frida Native hook(send/recv/SSL_write/BIO_write)全部0调用
    • classes.dex是壳,真正代码被加密
  • frida-dexdump运行时脱壳成功:41/54个DEX已dump

1.4 App网络行为

  • 不强制SSL pinning — mitmproxy无需Frida bypass即可解密HTTPS
  • Socket连接绕过系统HTTP代理 — 不走CONNECT隧道,直连服务器
  • iptables DNAT在MuMu模拟器上不生效
  • 可行的Socket拦截方案: mitmproxy劫持getIPList响应,把IP改为10.0.2.2:14000

2. 已尝试且失败的方案(勿重复尝试)

方案 结果 原因
Frida Java.perform hook libzxprotect.so加固导致Java VM不可达
Frida Native hook send/recv 加固替换libc函数指针
Frida Native hook SSL_write/BIO_write App用内嵌BoringSSL
iptables DNAT重定向Socket MuMu模拟器QEMU用户模式网络不经过内核netfilter
Python直连Socket发AuthReq AES加密层+签名缺失,服务器关闭连接
GetConceptJXBKw23 HTTP接口 接口存在但返回空List
jadx反编译 classes.dex是壳无法静态分析

3. 已成功的方案

方案 结果 产出
mitmproxy抓包HTTPS API captures/ 800+个JSON
frida-dexdump运行时脱壳 41个DEX(已清理)
从脱壳DEX提取Protobuf定义 proto/*.proto
getIPList劫持+TCP代理拦截Socket tcp_proxy_raw.py收到TLS ClientHello
HTTP API动态获取板块数据 data/ (板块+个股, CSV格式)

4. API接口文档

4.1 域名

域名 用途
apphwshhq.longhuvip.com 行情数据(板块/指数/盘口)
applhb.longhuvip.com 题材/概念/龙虎榜
apphis.longhuvip.com 历史数据(个股列表) ⚠️ ZhiShuStockList_W8必须用这个域名
apparticle.longhuvip.com 文章/资讯
applog.longhuvip.com 日志
getsockip.longhuvip.com Socket服务器IP列表

4.2 通用请求参数

PhoneOSNew=1&DeviceID={did}&VerSion=5.23.0.4&apiv=w44&UserID={uid}&Token={token}
  • ⚠️ 必须使用Dalvik User-Agent! 服务器校验UA,非Dalvik UA返回空数据
    User-Agent: Dalvik/2.1.0 (Linux; U; Android 12; ALN-AL00 Build/W528JS)
    Connection: Keep-Alive
    
  • 认证信息从抓包获取,Token可能过期需重新抓包
  • 非交易时间需传 Date=交易日期(如2026-05-08)

4.3 板块数据接口

GetPlate_Info_QJ (c=ZhiShuRanking)

  • 参数: PlateID=801159, Date=2026-05-08
  • 概念板块(267个)和行业板块(58个)字段映射不同!

概念板块格式(267个,PlateID以8010-8018开头):

List[0] = 涨跌家数差/强度排名 (如1)
List[1] = 强度 (如16304)
List[2] = 成交额(元) (如812032290529 → 8120亿)
List[3] = 主力净额(元) (如8437255508 → 84亿)
List[4] = 未知 (如1.98) ⚠️ 含义待确认
List[5] = 涨停数 (如24)
List[6] = 涨停封单(元) (如2123483126 → 21.23亿)
List[7] = 大单封单(元) (如1347672506 → 13.48亿)

涨跌幅不在GetPlate_Info_QJ中! 需从 Index/GetInfoBaceFaceList 获取:

BaceFaceList = [[板块名称, 涨幅%, PlateID], ...]
例: ["机器人概念", "1.67", 801159]

行业板块格式(58个,PlateID以8019/803/880开头):

List[0] = 强度 (如239, 或'--'表示无数据)
List[1] = 涨跌幅*100 (如-38 → -0.38%)
List[2] = 成交额(元)
List[3] = 主力净额(元)
List[4] = 量比 (如0.16)
List[5-7] = 通常为0

GetPanKou (c=ZhiShuL2Data, 参数用StockID不是PlateID!)

  • 参数: StockID=801159, Date=2026-05-08
  • ⚠️ 控制器是 ZhiShuL2Data 不是 ZhiShuRanking!之前用错控制器导致返回空
  • 返回盘口数据(12字段数组):
pankou[0]  = 成交额(元)
pankou[1]  = 换手率(%) (如3.994)
pankou[2]  = 未知(220.505)
pankou[3]  = 未知(615.74亿)
pankou[4]  = 未知(-531.37亿)
pankou[5]  = 主力净额(元)
pankou[6]  = 上涨家数 (如872)
pankou[7]  = 下跌家数 (如295)
pankou[8]  = 未知(18)
pankou[9]  = 未知(203298.83亿)
pankou[10] = 未知(258127.24亿)
pankou[11] = 强度 (16304.42)

GetBaseFaceListZDEvnArtNew (c=ZhiShuL2Data)

  • 参数: StockID=801159
  • ⚠️ 控制器是 ZhiShuL2Data 不是 ZhiShuRanking!
  • 返回当日爆发原因

BKFenShiZhiBo (c=ConceptionPoint)

  • 参数: PlateID=801159
  • ⚠️ 控制器是 ConceptionPoint 不是 ZhiShuRanking!
  • 返回分时直播事件(涨停股等)

SonPlate_Info (c=ZhiShuRanking)

  • 参数: PlateID=801159
  • 返回子板块列表: [[代码, 名称, 强度], ...]

ZhiShuStockList_W8 (c=ZhiShuRanking, 域名=apphis)

  • 参数: PlateID=801821, Date=2026-05-08, Type=0~19
  • ⚠️ 域名必须是 apphis.longhuvip.com,不能用apphwshhq!
  • ⚠️ 响应key是小写 list 不是大写 List 之前用大写取一直显示0
  • ⚠️ Type参数是排序标签,每种Type返回9只个股(Top9),需遍历Type 0~19合并去重才是完整列表!
  • Type对应GetGPCPHBTS_Tag的排序标签(机构增仓TSZB=20、低PE=7、高股息=8等)
  • 个股字段(63个) — 2026-05-10v4 用户确认:
[0]=代码, [1]=名称, [4]=所属板块标签,
[5]=价格, [6]=涨幅%, [7]=成交额(元),
[8]=实际换手率%, [9]=涨速, [10]=实际流通(元),
[11]=主力买(元), [12]=主力卖(元), [13]=主力净额(元),
[18]=卖流占比, [19]=净流占比, [20]=区间涨幅,
[21]=量比, [23]=几天几板, [25]=换手率%,
[28]=收盘封单(元), [29]=最大封单(元),
[33]=振幅, [37]=总市值(元), [38]=流通市值(元),
[40]=领涨次数, [42]=机构增仓Q1(元), [50]=300万以上大单净额(元),
[53]=市净率,
[58]=人气值, [59]=人气排名变化,
[60]=市盈率动, [61]=市盈率TTM, [62]=PE静
  • 字段名来源: PaiHangBangOption/GetUserOptionB的Bind数组含字段名→arrayPosition映射

GetGPCPHBTS_Tag (c=ZhiShuRanking)

  • 参数: PlateID=801821
  • 返回标签配置(最正宗、机构增仓等排序选项)

GetDayBaseFaceListZDEvnArt (c=ZhiShuKLine)

  • 参数: PlateID=801159
  • 返回爆发原因历史列表

Theme/InfoBKR (c=Theme, 域名=applhb)

  • 参数: ZSCode=801159
  • 返回子概念列表

4.4 首页接口

Index/GetInfo (c=Index)

  • 返回首页聚合数据,含BaceFaceList活跃板块(涨跌幅来源)

Index/NewGetList (c=Index, 域名=applhb)

  • 返回Theme.List热门板块

5. Socket协议(Protobuf)

5.1 服务定义(从classes05.dex提取)

service HQ {
  rpc SubThemeList(Empty) returns (themeListResp);
  rpc SubPlateTypeQuotasList(PlateTypeQuotasListReq) returns (PlateTypeQuotasListResp);
  rpc SubAuth(AuthReq) returns (AuthResp);
}

5.2 消息定义

AuthReq

deviceId(1,string), platformId(2,int), versionName(3,string),
channelId(4,int), signature(5,string), userId(6,string),
token(7,string), connType(8,int), curTime(9,int)

themeListResp.Item(267板块列表)

id(1), name(2), desc(3), pinyin(4), isHot(5),
ztNum(6), ratio(7), sortType(8), conceptType(9),
codeSwitch(10), state(11)

PlateTypeQuotasListResp.Item(板块行情)

plateId(1), plateName(2), strength(3), incRate(4),
tur(5), mainNetAmount(6), volRatio(7), institutionIncrease(8),
circularCaptital(9), yearPE(10), nextYearPE(11)
  • volRatio=量比, institutionIncrease=机构增仓 — 这两个字段在HTTP API中找不到,只在Socket推送中

5.3 Proto文件

  • proto/kpl.proto — 合并后的完整protobuf定义
  • proto/kpl_pb2.py — 编译后的Python模块(需protobuf v6运行时)

6. 当前数据资产

路径 内容
data/main_plate_info_{date}.csv 主板块汇总(强度/成交额/主力/涨停封单/大单封单/换手率/上涨下跌/爆发原因,merge 4 组数据)
data/sub_plate_info_{date}.csv 子板块关联(父代码/子代码/子板块名/子板块强度)
data/stock_info_{date}.csv 个股详情(代码/名称/价格/涨幅/成交额/主力/换手率/量比/封单/市值/PE 等 63 字段)
captures/ mitmproxy 抓包原始数据(用于 token 刷新)

7. 环境配置

MuMu模拟器

  • ADB路径: /Applications/MuMuPlayer.app/Contents/MacOS/MuMuEmulator.app/Contents/MacOS/tools/adb
  • ADB端口: 不稳定,每次重启后需扫描
  • 网关: 10.0.2.2 = Mac主机
  • 默认root
  • 极不稳定 — 频繁操作后容易崩溃,ADB断连

Frida

  • 版本: 17.9.1
  • Server路径: /data/local/tmp/frida-server
  • 必须用attach模式(-p PID),spawn模式(-f)会导致libzxprotect.so crash
  • 必须adb forward tcp:27042
  • Java.perform不可用 / Native hook不可用

mitmproxy

  • CA证书: ~/.mitmproxy/mitmproxy-ca.pem
  • 抓包脚本: mitm_capture.py(项目根目录下)
  • 启动命令: mitmdump -s mitm_capture.py --listen-port 8080
  • ⚠️ protobuf版本冲突: 修复: pip install protobuf==4.25.9 typing-extensions==4.4.0

Python

  • 依赖: requests, pandas, akshare, mitmproxy
  • 运行: 直接 python3 crawler_batch.py 或在 Jupyter/Notebook 中 import 调用

8. 脚本清单

8.1 核心抓取脚本

脚本 用途 状态
crawler_batch.py 批量爬虫(并行):token校验/刷新 + 交易日获取 + 并行抓取板块+个股数据,输出CSV ✅ 推荐
crawler_raw.py 原版串行爬虫:逐板块串行抓取,硬编码日期和Token,输出CSV ✅ 可用(调试参考)

8.2 抓包/代理脚本

一键启动/停止(推荐):

脚本 用途 状态
start_capture.sh 一键启动抓包:自动配置代理 (10.0.2.2:8080) + 启动 mitmproxy
stop_capture.sh 一键停止抓包:清理进程 + 关闭代理

使用方法:

# 启动抓包
cd /Users/yixin/agent_project/开盘啦_数据解析
./start_capture.sh

# 在 MuMu 模拟器中打开 开盘啦 App 并操作

# 停止抓包
./stop_capture.sh

⚠️ 关键配置:

  • 代理地址必须是 10.0.2.2:8080(MuMu 网关 = Mac 主机)
  • 不能用 127.0.0.1:8080(模拟器内 127.0.0.1 指向自己,会导致网络受限)

8.3 mitmproxy 抓包脚本

脚本 用途 状态
mitm_capture.py 统一版抓包脚本:过滤 longhuvip 域名,保存到 captures/,可选 SOCKET_HIJACK 劫持 getIPList,用于 crawler_batch.py 的 token 自动刷新 ✅ 推荐

8.4 crawler_batch.py 使用说明

前置配置

.env 文件中配置认证参数(已自动生成):

KPL_USER_ID=7079210
KPL_TOKEN=你的 token 值
KPL_DEVICE_ID=你的设备 ID

基本用法

# 抓取指定日期范围(推荐)
python3 crawler_batch.py --start 2026-05-01 --end 2026-05-15

# 只抓最近一个交易日
python3 crawler_batch.py

# 自定义并发线程数和请求间隔
python3 crawler_batch.py --start 2026-05-01 --end 2026-05-15 --workers 5 --delay 0.1

或在 Python 中直接调用:

from crawler_batch import main
main(start_date="2026-05-01", end_date="2026-05-15", delay=0.05, workers=10)

工作流程

crawler_batch.py 按天循环:
├── 加载 .env 中的认证参数
├── 验证 token → 过期则自动启动 mitmproxy 抓包刷新
├── 获取交易日列表(akshare)
├── 对每个交易日:
│   ├── 阶段1:串行分页 RealRankingInfo → 动态获取板块列表
│   ├── 阶段2:并行抓取板块数据 (PlateInfo_QJ + PanKou + BoomReason + SonPlate)
│   ├── 阶段3:并行抓取成分股 (ZhiShuStockList_W8)
│   └── 阶段4:构建 DataFrame → 输出 3 个 CSV
│       ├── data/main_plate_info_{date}.csv  (主板块汇总)
│       ├── data/sub_plate_info_{date}.csv   (子板块关联)
│       └── data/stock_info_{date}.csv       (个股详情)

Token 过期处理

脚本自动检测 token 是否过期。过期时:

  1. 自动启动 mitm_capture.py(mitmproxy 抓包)
  2. 轮询 captures/ 目录等待新 Token
  3. 从捕获数据中提取新 UserID 和 Token
  4. 自动更新 .env 文件
  5. 继续执行抓取

如何获取新 token(手动方式):

  1. 在 MuMu 模拟器中打开 App 并登录
  2. 运行 ./start_capture.sh 启动抓包
  3. 从抓包数据中找到新的 Token 值
  4. 更新 .env 文件
  5. 重新运行 python3 crawler_batch.py

断点续传

脚本会自动检查 data/main_plate_info_{date}.csv 是否存在:

  • 已存在 → 自动跳过该日期
  • 目前不支持 --force 覆盖,需手动删除已有 CSV 后重新运行

日志输出

控制台实时输出抓取进度,包括:

  • 板块数量、token 状态、每个板块的抓取结果
  • 并发线程执行情况
  • 失败重试记录

9. 已踩过的坑(勿重复)

  1. Dalvik UA 必须 — 不用则 API 返回 errcode=0 但 List=[]
  2. 概念板块 vs 行业板块字段映射不同 — GetPlate_Info_QJ 两种板块格式不同
  3. List[4] 不是涨跌幅 — 涨跌幅在 Index/GetInfo 的 BaceFaceList 中
  4. List[6] 是涨停封单不是主力流入 — List[7] 是大单封单不是主力流出
  5. GetPanKou 控制器是 ZhiShuL2Data 不是 ZhiShuRanking — 参数用 StockID 不是 PlateID
  6. BKFenShiZhiBo 控制器是 ConceptionPoint — 不是 ZhiShuRanking
  7. ZhiShuStockList_W8 域名是 apphis 不是 apphwshhq — 用错域名返回 0
  8. ZhiShuStockList_W8 响应 key 是小写 list 不是 List — 用大写取一直显示 0
  9. mitmproxy 需放行非 longhuvip 域名 — 否则 DNS 失败返回 502 阻断 App 网络
  10. 非交易时间 Index/GetInfo 的 BaceFaceList 返回空 — 涨跌幅列暂为空
  11. ZhiShuStockList_W8 只返回 9 只 — 需遍历 Type 0~19 合并去重才是完整个股列表
  12. [5] 是价格不是涨幅 — 之前误以为是涨幅导致数据错乱
  13. [11] 是主力买不是流通市值 — 之前映射错误
  14. [28]=收盘封单 [29]=最大封单 [50]=300 万以上大单净额 — 从 PaiHangBangOption/GetUserOptionB 的 Bind 数组确认
  15. 子板块个股数据 App 走 Socket 不走 HTTP — App 进入子板块时不请求个股列表接口,数据通过 Socket 推送
  16. ⚠️ MuMu 代理必须用 10.0.2.2:8080 — 127.0.0.1 在模拟器内指向自己,会导致 App 网络受限

10. 项目文件结构

开盘啦_数据解析/
├── README.md                    # 本文件(项目文档)
├── crawler_batch.py             # 批量爬虫(并行,token自动刷新,推荐使用)
├── crawler_raw.py               # 原版串行爬虫(硬编码日期和Token,调试参考)
├── mitm_capture.py              # mitmproxy 抓包脚本(域名过滤 + 可选 SOCKET_HIJACK)
├── aes.min.js                   # AES 加密 JS(App 加密逻辑参考)
├── start_capture.sh             # 一键启动抓包(自动配置代理 + 启动 mitmproxy)
├── stop_capture.sh              # 一键停止抓包(清理进程 + 关闭代理)
├── .env                         # 认证配置(KPL_TOKEN/KPL_USER_ID/KPL_DEVICE_ID)
├── data/                        # 输出数据
│   ├── main_plate_info_{date}.csv   # 主板块汇总
│   ├── sub_plate_info_{date}.csv    # 子板块关联
│   └── stock_info_{date}.csv        # 个股详情
├── captures/                    # mitmproxy 原始抓包(用于 token 刷新)
└── .gitignore                   # 排除 .env / data / captures / __pycache__

About

开盘啦 App 数据抓取与解析工具 - 批量抓取板块/个股数据,Token自动更新,mitmproxy流量拦截

Topics

Resources

License

Stars

41 stars

Watchers

2 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors