齐鲁工大充电桩实时监控:从协议逆向到多线程自动化实战
YZDR Lv1

一、 项目背景

齐鲁工业大学 23 号楼下的充电桩由威胜集团提供技术支持。由于官方小程序仅支持近距离扫码查看,无法远程获知全场 70 多个桩位的实时状态。在北方的严冬深夜,为了找一个空桩而在寒风中逐一尝试显然不是最优解。

本项目旨在通过 Python 逆向监测,将全场状态聚合为一个实时更新的可视化看板。

image-20260106134939643


二、 协议逆向全流程

1. 流量嗅探与抓包

使用 Fiddler 对微信小程序进行 SSL 解密。通过分析发现,查询一个充电桩的状态需要遵循严格的顺序逻辑:

  • 第一步:Session 激活 (AddSession)
    告知服务器“我要查看某个桩”。如果跳过此步直接取数,后端会报错。
  • 第二步:数据拉取 (getWxInfo)
    服务器根据当前 Session 绑定的设备 ID,返回详细的 JSON 数据(包含剩余时间、占用状态等)。

2. 身份凭证拆解

接口校验的核心在于 Header 中的 Cookie

  • ASP.NET_SessionId: 服务端生成的会话标识,具有时效性。
  • OpenID: 用户在微信生态下的唯一标识。

三、 核心架构设计

由于单 Session 存在严重的频率限制,且极易因“操作频繁”被封禁。我设计了一个 Cookie 池 (Cookie Pool)
通过维护多个有效的 Session,系统可以利用多线程并发技术,将 70 个点位的查询压力平摊给多个“虚拟用户”。

2. 关键代码片段

A. 核心通信逻辑

这是避开 .NET 后台 NullReferenceException 报错的关键。必须保证 Body 中的 openid 与 Cookie 严格一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def fetch_status(device_id, cookie, openid):
headers = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...",
"Cookie": cookie,
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
}

# 1. 激活 Session 绑定
reg_url = "[https://semiot.wasion.cn/Main/AddSession](https://semiot.wasion.cn/Main/AddSession)"
requests.post(reg_url, headers=headers, data={"AdminName": "共享用电", "DeviceNumber": device_id, "PageTaype": "1"})

# 2. 拉取实时 JSON 报文
info_url = "[https://semiot.wasion.cn/DeviceCurrentData/getWxInfo](https://semiot.wasion.cn/DeviceCurrentData/getWxInfo)"
response = requests.post(info_url, headers=headers, data={"DeviceNumber": device_id, "openid": openid})

return response.json()

B. 多线程任务调度

使用 Queue 队列实现生产者-消费者模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
def worker(cookie, task_queue):
while True:
pos, d_id = task_queue.get()
# 提取当前线程分配到的 OpenID
current_oid = extract_openid(cookie)

result = fetch_status(d_id, cookie, current_oid)
if result:
update_local_json(pos, result) # 线程安全写入

time.sleep(10) # 模拟人类操作间隔,保护 Session
task_queue.task_done()


四、 坑位总结 (Post-Mortem)

1. 致命的 500 错误

现象:程序运行初期,频繁收到服务器返回的 HTML 错误页面,提示“未将对象引用设置到对象的实例”。
分析:威胜后台代码鲁棒性不足。当 AddSessiongetWxInfo 之间的调用间隔过短,或者 openid 参数大小写不敏感时,后端无法在内存中初始化用户对象。
解决:严格对齐参数,并在两步操作间加入 0.5 秒的微小延迟。

2. 动态 Session 过期

现象:脚本运行数小时后,所有桩位显示“离线”。
解决:编写了一个 Cookie Check 脚本。每隔一小时对池内 Cookie 进行“健康检查”,若失效则通过钉钉或企业微信机器人提醒手动更新抓包。

五、伦理与安全性思考

1
本项目的初衷是公共数据透明化,而非恶意攻击。在实现过程中,我严格限制了请求频率(单个 Cookie 间隔 10s 以上),确保不对官方服务器造成额外压力。

六、 项目成果

通过这套系统,全场 70 个充电桩的巡检周期从手工扫码(约 1 小时)缩短到了自动化轮询(约 3 分钟)
可视化看板不仅能显示哪个桩空着,还能通过 sytime 字段预测该桩位何时会释放,极大地优化了排队体验。

image-20260106134519753

技术栈: Python / Flask / Requests / Fiddler / Threading

 评论
评论插件加载失败
正在加载评论插件