为什么每晚九点都有专车在楼下等我

阅读 1199
收藏 33
2017-02-14
原文链接:mp.weixin.qq.com

背景

每天晚上下班叫车是一件很痛苦的事情,滴滴没有给百度开通企业版,所以叫不了快车,而出租车又需要很高的调度费才能叫到,所以我一般选择首汽约车,既享受专车的服务,也一样可以报销。

上周五叫了一辆丰田混动,据说是高配版,确实很爽。司机秀了一把加速,百公里加速很快,有推背感,而且拥堵路段自动使用电动机,省油又静音,百公里耗油只有 5 升。除了售价贵了点,别的真心不错……打住,扯远了

不过比较头疼的是,晚上不太好叫车,app 里面每次叫车订单只能持续两分钟,两分钟以后还得手动重新下单,于是我不得不每写两分钟代码就低头看一眼手机并手动重新下单,导致九点以后的黄金时间段内工作效率极度低下。

作为一名程序员,这种事情必然不可忍受,我决定优化一下叫车流程,最终的用户体验是

每天九点都有专车在楼下等我回家!

上两张图大家感受一下,第一张是我运行脚本后,人不在电脑旁,收到的邮件提醒:



第二张是调试的界面,可以看到入口脚本很简洁,主要逻辑是子线程做轮询,欢迎大牛 review 一下^_^。



流程简介

首先放弃考虑滴滴叫车的自动化脚本,因为我估计滴滴这种体量的 app 必然采用websocket 长连接加上  protobuf 传输数据,首先面对的困难是无法解密。其次估计业务逻辑也会比较复杂,而国企作风的首汽约车显然是时间成本最低的目标。

用 Charles 简单抓包并分析了一下叫车的全过程,大概分为这么几步:

  1. 登陆: 上传本地 token 并获取更新

    虽然 token 一般不会变,但毕竟 Server 给了返回结果,还是要存储一下以防万一,否则 token 错乱的话后续的操作都无法进行。另外要注意模拟 User-Agent,否则服务器会拒绝响应。

    比较有意思的是,用户的基础参数都被放在 HTTP Header 里面,所以整个 Header 都要按照真实请求来模拟,并且存为全局变量方便每次复用。

  2. 下单: 填写上下车地点

    这一步比较简单,上下车地点主要是传一个地名的中文名和经纬度信息,这些都是写死的数据。下单几乎是一定成功,会拿到订单 ID 和订单编号,这个存起来之后还要用到。

    我关注了一下几次下单的订单 ID,发现满足严格递增,每次增幅大约在 400 左右。由于我每次下单间隔为 2min,也就是说目前首汽约车的订单量大约为 3个/秒,对于一家公司来说这个下单量似乎有点惨不忍睹。而且如果一个创业公司把自己的业务量这样暴露出来,实际上是非常被动的。

  3. 叫车: 发送一个异步叫车请求

    一开始漏掉了这个请求,导致总是叫不到车,后来才知道这个请求是用来叫车的,而下单请求仅仅是拿到订单信息。这个请求也不用过于关心返回结果,反正发送一个叫车请求就可以了。

  4. 轮询: 维持叫车状态

    轮询请求用来不断查询叫车信息,需要上传 token 和订单编号,另外我也猜测轮询请求会维持上述叫车请求的状态,否则就会被超时自动取消,这个以后有空可以验证一下。

    不得不吐槽一下,三秒一次的轮询实在是太 low,不仅增加对服务器的压力,客户端流量消耗也多。不过也得感谢这些开发者们,如果他们以长连接的形式由服务器主动 推送消息,那脚本开发难度就要大幅度提高了。

    另外需要注意的是,由于网络请求需要耗时,所以不能把轮询请求放在主线程来做,否则无法控制间隔时间为严格的三秒钟,解决方案也很简单, 子线程发送网络请求,主线程循环 sleep 即可。

  5. 取消订单

    由于客户端的逻辑是每次订单只维持两分钟,所以我选择了最安全的做法,即与客户端保持一致。订单取消后,回到步骤 2,重新下单即可,也就是上面截图中的主要逻辑。

  6. 邮件提醒

    Python 发送邮件很简单,只要配置简单的 SMTP 服务器信息即可,需要提供发件人邮箱、密码,收件人邮箱、主题、正文等基本信息,总共大约 20 行代码。

    公司邮箱貌似对外部邮件做了一定的过滤或者延迟,而 QQ 邮箱自动发邮件又会被认定为垃圾邮件,因此我选择从我的企业邮箱发送到 QQ 邮箱。

  7. 定时任务

    因为晚上定期要去健身房,个人电脑不能保证一直开机,所以不适合用自己的电脑来跑脚本。这个事情也很好实现,写好 crontab 定时脚本扔到 VPS 上定时执行 Python 脚本即可。

    一般来说工作日晚上 8:50 叫车比较合适,这时候叫车人少,基本上能叫到车,这样九点钟左右专车就会在楼下等我了(抱歉做了标题党,有时候九点也叫不到车),由于有时候晚上临时有事早走不了,还得再开一个接口,能临时禁止当天任务的执行。

后记

昨天听了陈皓老师的一番话很有意思:“不做自动化要累死,做了自动化要累趴下,累死和累趴下二选一吧”。

整个脚本的开发并不困难,由于使用了 requests 库,一个号称给人类使用的 Python 网络请求库,使用起来真的非常简洁方便:



总耗时大约三四个小时,既能巩固 Python 知识(说实话这个难度基本上学不到啥,也就学了一下引用别的模块),又能持续提高自己的效率,现在看来还是很不错的 一次尝试。

当然后续可做的功能还有很多,比如叫到车以后司机位置定时发送,更用户友好的邮件提示,提前半小时开始预约等等。不过这样就违背了自己 提高效率 + 学习 Python 的初衷,因此暂时不会考虑实现这些功能。

最后吐槽一句,Python 的字符串编码问题一如既往的恶心,.encode(latin1) 也是让我一脸懵逼。

广告

我是张星宇,一名刚刚入门 iOS 开发的大四学生,喜欢探索问题的本质,讨厌一切不说人话的描述。正在学习前端,励志成为一名全栈工程师。博客、Github、微博、简书、掘金等平台的 ID 都是 bestswifter,如果觉得本文有帮助,欢迎关注我的公众号并持续与我互动,不定期分享自己的收获。


评论