前言:一场关于“插头”的保卫战

作为一个每天晚上10点才回家的“社畜”,我面临着一个巨大的生存危机:小区楼下的电动车充电桩(某小程序)太卷了。

规则是这样的:必须扫码才能通电。但我回家太晚,22点之后充电桩停止工作,想等到第二天早上6点再充吧,我又起不来。更惨的是,如果我先把插头插上占位,等到下午才想起来扫码,插头早就被大爷大妈拔掉扔地上了……

于是,一个伟大的需求诞生了:能不能让我晚上插好插头,拍个照,然后程序在第二天早上6:05自动帮我扫码通电?

为了这个需求,我经历了一场从安卓自动化Web逆向,最后回归SMTP协议的奇妙旅程。


第一回合:试图用 Auto.js 物理外挂(卒)

最开始,我的思路很单纯:用手机搞个自动化脚本不就行了?

我掏出了我的华为 P50 Pro,写了个 Auto.js 脚本:

  1. 定时唤醒。

  2. 自动打开微信。

  3. 模拟点击“扫一扫”,从相册选图。

碰壁现场:

首先是我的手机不支持无障碍录制并自动定时执行,下面的问题也很难克服

代码写得行云流水,一跑起来发现——锁屏是道硬伤。

安卓系统出于安全考虑,脚本不能在黑屏/锁屏状态下解锁手机。除非我让手机屏幕亮一整晚(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。

这时,我想到了一个上古神器 —— 电子邮件

最终方案:邮件遥控机器人

逻辑极其风骚:

  1. 控制端: 我的手机。

  2. 服务端: 工位电脑(运行 Python 脚本,轮询检查 QQ 邮箱)。

  3. 操作流程:

    • 晚上插好电,手机扫码,复制链接。

    • 给自己的 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 准时干活!")

结局:懒人的胜利

现在,我的充电流程变成了:

  1. 下班插电。

  2. 手机扫码 -> 复制链接。

  3. 发个邮件给自己。

  4. 收到电脑回复:“主人,任务已部署,明早见。”

到了第二天早上 6:05,我的电脑会准时像个特工一样,远程发送一条指令,把电给续上。

遇到的一个小插曲:

脚本第一次运行时提示 失败: 成功。

我查了半天 bug,发现是因为我余额充足,服务器直接扣款成功了,没有返回支付链接(alipayUrl),我的代码以为没链接就是失败。

这大概就是所谓的:成功得太得措手不及。

技术总结

在这个过程中,我用到了:

  • Web 逆向: Fiddler 抓包、User-Agent 伪装。

  • Python 爬虫: Requests、Regular Expression。

  • 计算机视觉: OpenCV/Pyzbar(虽然最后直接发链接更香)。

  • 自动化运维: SMTP/IMAP 协议、APScheduler 定时任务。

果然,为了偷懒,程序员什么都干得出来。


注:本文仅供技术交流,请勿用于非法用途,还是要素质充电哦!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐