【硬核摸鱼】为了多睡10分钟,我用Python手撸了一个“邮件控制充电桩”的自动系统
小区楼下的电动车充电桩(某小程序)太卷了。规则是这样的:必须扫码才能通电。但我回家太晚,22点之后充电桩停止工作,想等到第二天早上6点再充吧,我又起不来。更惨的是,如果我先把插头插上占位,等到下午才想起来扫码,插头早就被大爷大妈拔掉扔地上了……能不能让我晚上插好插头,拍个照,然后程序在第二天早上6:05自动帮我扫码通电?为了这个需求,我经历了一场从安卓自动化到Web逆向,最后回归SMTP协议的奇妙
前言:一场关于“插头”的保卫战
作为一个每天晚上10点才回家的“社畜”,我面临着一个巨大的生存危机:小区楼下的电动车充电桩(某小程序)太卷了。
规则是这样的:必须扫码才能通电。但我回家太晚,22点之后充电桩停止工作,想等到第二天早上6点再充吧,我又起不来。更惨的是,如果我先把插头插上占位,等到下午才想起来扫码,插头早就被大爷大妈拔掉扔地上了……
于是,一个伟大的需求诞生了:能不能让我晚上插好插头,拍个照,然后程序在第二天早上6:05自动帮我扫码通电?
为了这个需求,我经历了一场从安卓自动化到Web逆向,最后回归SMTP协议的奇妙旅程。
第一回合:试图用 Auto.js 物理外挂(卒)
最开始,我的思路很单纯:用手机搞个自动化脚本不就行了?
我掏出了我的华为 P50 Pro,写了个 Auto.js 脚本:
-
定时唤醒。
-
自动打开微信。
-
模拟点击“扫一扫”,从相册选图。
碰壁现场:
首先是我的手机不支持无障碍录制并自动定时执行,下面的问题也很难克服
代码写得行云流水,一跑起来发现——锁屏是道硬伤。
安卓系统出于安全考虑,脚本不能在黑屏/锁屏状态下解锁手机。除非我让手机屏幕亮一整晚(OLED屏直呼内行),否则早上6点它根本动不了。
结论: 物理模拟这条路,走不通。
第二回合:Web 端自动化与“微信白页”的博弈
既然手机端不行,我把目光投向了电脑。只要能拿到小程序的 H5 链接,用 Python 的 Selenium 控制浏览器去跑不就行了?
1. 伪装手机浏览器
我把 User-Agent 改成了 iPhone,成功在 Chrome 里打开了充电页面。
2. 注入 Token
通过 Fiddler 抓包,我发现这系统不用 Cookie,而是用 LocalStorage 存 token。于是我写了段 Python 脚本,启动浏览器后疯狂注入 Token:
driver.execute_script("localStorage.setItem('token', '我的密钥');")
碰壁现场:
当我让脚本点击“开启充电”按钮时,浏览器直接跳到了一个全白的页面:open.weixin.qq.com...。
原因: 页面前端代码检测到环境不对(不是真微信),试图调用微信的 OAuth 授权,结果 Selenium 根本响应不了,直接卡死。
结论: 浏览器模拟太重了,还容易被前端 JS 逻辑“防”住。
第三回合:协议逆向,直捣黄龙
既然浏览器有各种跳转和检测,那我直接给后端服务器发 HTTP 请求不就完了?
1. 抓包分析
我再次打开 Fiddler,在电脑微信里完整走了一遍充电流程。终于,让我抓到了那个关键的 POST 请求:
-
URL:
https://api.xxx.com/charge/v1/charging/open -
Body:
{"outletNo": "O241123xxxxx", "billingType": 5}
2. 发现盲点
原来每一个插座都有一个唯一的 outletNo(插座号)。只要拿到了这个号,加上我的 token,直接发一个 POST 包就能充电,根本不需要扫码!
3. 解析二维码
那么问题来了,我怎么知道我今天插的是哪个插座?
答案就在二维码链接里。
链接长这样:.../qrcodescan/加密字符串.shtml。
只要访问这个链接,服务器会自动 302 跳转到带参数的地址:.../chargingIndex/O241123xxxxx.shtml。
正则提取一下,插座号到手!
# 核心逻辑:二维码链接 -> 提取插座号 -> 发送充电POST请求
import requests
import re
def get_outlet(url):
res = requests.get(url, allow_redirects=False) # 禁止自动跳转,为了拿Location
target = res.headers.get('Location')
# 正则提取 O开头的插座号
return re.search(r'/chargingIndex/([a-zA-Z0-9]+)\.shtml', target).group(1)
结论: 纯协议流,稳如老狗!
第四回合:部署方案的究极折腾
核心代码通了,怎么用呢?
-
方案 A:Flask + 内网穿透
在工位电脑(24小时开机)上搭个网页。
缺点: 免费的内网穿透(如Cpolar)每24小时变一次网址,我每天还得先查今天的网址是啥,太累。
-
方案 B:开发个 App
缺点: 杀鸡焉用牛刀,而且我也懒得写 UI。
这时,我想到了一个上古神器 —— 电子邮件。
最终方案:邮件遥控机器人
逻辑极其风骚:
-
控制端: 我的手机。
-
服务端: 工位电脑(运行 Python 脚本,轮询检查 QQ 邮箱)。
-
操作流程:
-
晚上插好电,手机扫码,复制链接。
-
给自己的 QQ 邮箱发一封邮件,标题粘贴链接。
-
电脑脚本检测到新邮件 -> 解析标题里的链接 -> 拿到插座号。
-
脚本立即发送充电指令(测试通断)。
-
脚本自动设定一个明早 6:05 的定时任务(APScheduler)。
-
代码实现亮点(防坑):
-
只读未读邮件: 使用
conn.search(None, 'UNSEEN'),处理完立刻标记为已读,防止重复执行。 -
本地去重: 记录 Message-ID,双重保险防止脚本重启后发疯式充电。
-
正则兼容: 不管链接是在标题里、正文里,甚至是发了张二维码图片(集成 OpenCV/Pyzbar),统统都能识别。
# 核心调度逻辑
def check_email():
# 连接邮箱...
# 搜索未读...
for e_id in email_ids:
# 提取链接...
if outlet_no:
# 1. 立即充一下试试
send_charge_command(outlet_no, "立即")
# 2. 预约明早 6:05
target = get_tomorrow_6am()
scheduler.add_job(send_charge_command, 'date', run_date=target)
# 3. 回复邮件给我
send_email_reply("⚡ 充电任务已锁定", f"明早 6:05 准时干活!")
结局:懒人的胜利
现在,我的充电流程变成了:
-
下班插电。
-
手机扫码 -> 复制链接。
-
发个邮件给自己。
-
收到电脑回复:“主人,任务已部署,明早见。”
到了第二天早上 6:05,我的电脑会准时像个特工一样,远程发送一条指令,把电给续上。
遇到的一个小插曲:
脚本第一次运行时提示 失败: 成功。
我查了半天 bug,发现是因为我余额充足,服务器直接扣款成功了,没有返回支付链接(alipayUrl),我的代码以为没链接就是失败。
这大概就是所谓的:成功得太得措手不及。
技术总结
在这个过程中,我用到了:
-
Web 逆向: Fiddler 抓包、User-Agent 伪装。
-
Python 爬虫: Requests、Regular Expression。
-
计算机视觉: OpenCV/Pyzbar(虽然最后直接发链接更香)。
-
自动化运维: SMTP/IMAP 协议、APScheduler 定时任务。
果然,为了偷懒,程序员什么都干得出来。
注:本文仅供技术交流,请勿用于非法用途,还是要素质充电哦!
更多推荐



所有评论(0)