所有分类
主题 主题
平台 平台
我的工作台
userHead
注册时间 [[userInfo.create_time]]
创造力 [[userInfo.creativity]]
[[userInfo.remark]]
[[d.project_title]]
articleThumb
[[d.material_name]]
timelineThumb
进入工作台
折叠
所有分类 我的工作台
展开
#创意# 懒人的快递提醒小助手P2
pATAq pATAq 2020-05-24 23:08:46
4
1
简单

【项目介绍】利用计算机定期查询快递物流信息,有变动时主动提醒 

由于本文使用MD写成,本地也没保存插图,所以排版这方面挣扎了两下放弃了,有兴趣的请移步社区的同名帖子。https://mc.dfrobot.com.cn/thread-305556-1-1.html

前言

欢迎小伙伴们来到第二部分,在前文中 Latest Linux Distributions on LattePanda and Kernel Customization 我们费尽周折配置好了相关环境,本文开始进入第二部分:Core Code to Track the Packages | 包裹监视追踪项目的核心代码。

在这里我们会用到以下知识:

1. Interaction between MPU and MCU on LattePanda with Firmata protocol | LattePanda上Linux和Arduino使用Firmata协议进行交互

2. Python GUI Programming with remi | Python图形化编程(使用remi)

3. Usage of TinyDB——a lightweight database | 介绍轻量级数据库TinyDB的使用

4. Wechat notification via ServerChan | 使用Server酱实现微信通知功能

5. Remote Development with Visual Studio Code | 使用VSC进行远程开发

这一部分将实现如下功能:

1. 网页前端添加待监视快递信息

2. 查询的所有快递历史记录

3. 自定义查询频率

4. 快递信息变动时微信通知

The Purpose of this Project | 为什么要做这个项目

可能很多小伙伴要问了,我的淘宝、京东、菜鸟裹裹也可以帮我查快递,为啥还要弄这个哩?

1. 隐私性。现在的信息泄露太严重了,不想使用第三方软件。

2. 合并通知。希望有一个软件可以统一查询大多数的

3. 减少冗杂的程序。不希望安装过多软件,特别国内的一些软件太流氓,捆绑过多无用功能

4. 物流信息的订阅与实时追踪。一般的是服务端定时 push 物流信息到手机上,可能存在一定滞后性,而主动 pull 物流信息则可以更快获得相关信息。 

另外我不知道大家是怎样,我有时候买到心仪的东西,特别盼着早点到货,然后隔一会就去打开手机客户端刷新一下查看快递信息,时间久了浪费生命,所以想着用电脑帮我查,有变动主动告诉我。

## Project Architecture | 项目架构 ![](https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20200524031039.png) 1. 通过网页前端或者命令行前端,录入待查询快递信息到数据库中,例如 `TinyDB`。 2. 读取数据库中的信息,利用 `Requests` 从互联网上的相关网站获取物流信息。 3. 对获取的物流信息进行处理、如果有变动则录入数据库中,同时调用微信通知接口,使用 Firmata 协议与板载 Arduino Leonardo 交互。 4. 使用计划任务 `crontab` 定期获取物流信息。 5. 使用 Kismet 进行网络嗅探,判断目标设备是否在附近,是的话就启用额外的通知手段。否则只微信通知和在网页面板上显示。 **注意:** Kismet 网络嗅探将在第三部分中详述。 ### 配置VSC远程开发环境 参考 [Visual Studio Code remote_ssh](https://code.visualstudio.com/docs/remote/ssh) 即可。官方提供的工具,非常简单方便。 * 注意一些拓展需要在 Server 上重新安装一遍, * 支持密钥和账号密码登陆,连接远程服务器时每次都要输入密码,不会保存。建议使用密钥登陆 * Windows 需要ssh-client,我的 Windows_10_1909 默认安装了,没有的在应用-可选里面安装 OpenSSH-Client ,使用 ssh 登陆后会自动配置 server 。 ![](https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20200521205719.png) ### 安装常用库 * [TinyDB](https://github.com/msiemens/tinydb) 简单轻量的数据库,除了官方文档,也可以看看 [王大桃zzZ](https://www.cnblogs.com/watalo/p/12392518.html) 的系列分享 * [Requests](https://github.com/psf/requests) | 优雅好用的 Python HTTP 库,an elegant and simple HTTP library for Python, built for human beings. ### 提供物流信息查询服务API的网站 1. kuaidi100、kuaidi、baidu—— [express-plus](https://github.com/minosss/express-plus) 上述实行了严格的请求限制,不推荐使用 2. [伙伴数据资源](http://www.kdpt.net/express_api.html) | **推荐** 加客服QQ即可以获得密钥,调用简单,但是支持的快递公司不全 3. [TrackingMore](https://www.trackingmore.com/cn.html) | [免费API申请](https://www.trackingmore.com/apply-api-cn.html),不推荐 4. [快宝开放平台](https://open.kuaidihelp.com/serve/wlgz) | 可用,类似快递鸟,需要加密 5. [快递鸟](http://www.kdniao.com) | **推荐**,支持快递公司较全,但是调用麻烦点?参考 [kdniao_python](https://github.com/menduo/kdniao_python) 及 [Python3 调用快递鸟 Api 查快递](https://www.v2ex.com/t/310616) 这里我使用的是伙伴数据资源提供的API,[首页](http://www.kdpt.net/)可以看到支持的快递公司列表。 ### 核心代码(后端) ```python #!/usr/bin/env python3 # -*- coding: utf-8 -*- __author__ = 'Code_Paintium' import requests, time, sys from tinydb import TinyDB, Query db = TinyDB("/home/sjqlwy/Projects/kd/db.json") parcel = Query() # 填入自己的授权密钥,http://www.kdpt.net/express_api.html # 支持快递公司列表 http://www.kdpt.net/index.html#companies id = 'YOURIDKEY' # 增加数据记录 def AddItem(name, com, postid): if db.search(parcel.postid == postid) == []: db.insert({'name': str(name), 'com': str(com), 'postid': str(postid), 'Fstatus': {}, 'Cstatus': {}, 'state': ''}) # 微信提醒 def fqtt(desp): payload_ftqq = {'text': name, 'desp': str(desp)} requests.get('https://sc.ftqq.com/YOURKEY.send', params=payload_ftqq) # 检查快递状态 def check_status(com, postid, name, Cstatus): payload_status = {'id': id, 'com': com, 'nu': postid} r = requests.get('http://q.kdpt.net/api', params=payload_status).json() if Cstatus != r['data'][0]: Cstatus = r['data'][0] desp = Cstatus['time'], Cstatus['context'] fqtt(desp) db.update({'Cstatus': Cstatus, 'Fstatus': r['data'], 'state': r['state']}, parcel.postid == postid) # Main,当没有输入参数时检查已有数据;正确输入参数时创建条目;错误输入时提示 if len(sys.argv) == 4: AddItem(sys.argv[1], sys.argv[2], sys.argv[3]) elif len(sys.argv) == 1: pass else: print("INVAIND INPUT! pls input name, companny, postid and try again.") print("e.g., python3 kd.py Gaoda zhongtong zt1124991") exit # 仅检查未签收的项目,注意调用时间不小于1s for item in db.search(parcel.state != '3'): com = item['com'] postid = item['postid'] name = item['name'] Cstatus = item['Cstatus'] # print(Cstatus) check_status(com, postid, name, Cstatus) time.sleep(2) ``` **代码逻辑:** 因为整体代码比较简单,所以就不用面向对象代码了。 * `AddItem()` 向表中写入数据,该表中包括以下内容: * 物品名称 name * 快递公司 com * 快递单号 str * 物流全程信息 Fstatus * 最新物流信息 Cstatus * 是否已签收 state * `fqtt()` 用来实现微信提醒功能,借助简单好用的 Server酱 的服务 * `check_status()` 获取快递的最新物流信息,通过 [伙伴数据资源](http://www.kdpt.net/) 提供的API,可以联系QQ获取KEY * 首先分析获取的数据,将最新的物流状态与已保存对比 * 有变化的话更新数据库内容 * 运行程序时如果没有参数,则循环查询各个快递状态,有完整参数时添加新的快递以便追踪,参数不对时报错 * 循环查询时,采用`db.search(parcel.state != '3')`来排除已经签收的 ![](https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20200521210500.png) #### 如何使用 * 你需要去 [Server酱](http://sc.ftqq.com/) 注册登录账号,获取自己的 **SCKEY** ,然后绑定微信即可 * 去伙伴数据联系客服 QQ 免费获取自己的 **IDKEY** * 设置**数据库存储路径**,建议使用绝对路径,我的是`/home/sjqlwy/Projects/kd/db.json` 效果展示: ```shell # 首次需要输入参数以向数据库中添加相关信息,为物品名称(自定义)、快递公司(可以选auto来自动识别,如果获取信息错误可以移步伙伴数据获取名称,如shentong)、快递单号。 python3 kd.py FS-T6 auto 773037873703622 # 录入信息后,后续查询不带参数运行则会遍历数据库中所有未签收的快递 python3 kd.py ``` ![](https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20200524145837.png) ### 定期执行 这里想到最省资源最方便的就是系统自带的计划任务,定时每 1h 无参数运行 `kd.py` 即可。 ```shell crontab -l crontab -e 0 */1 * * * /usr/bin/python3 /home/sjqlwy/Projects/kd/kd.py 2 */1 * * * /usr/bin/curl -l https://hc-ping.com/6ebdfd32-1fd5-4ecf-******* # use your own ``` 同时添加计划任务提醒,这里借助 [healthchecks](https://healthchecks.io/) 。填写任务名称,备注等,然后设置时间和之前的差不多,执行的命令为向下述网址发送一个 http 请求,建议用 `curl -l URL`, 最省事。 ![image-20200524211421567](C:\Users\Xin\AppData\Roaming\Typora\typora-user-images\image-20200524211421567.png) 关于 crontab 的使用参考 [之前的文章](https://www.cnblogs.com/sjqlwy/p/ddns.html) ,直接 crontab -e 编辑即可,不需要重启服务,现在都是 systemd 接管。记得设置好时区,参考 [System time](https://wiki.archlinux.org/index.php/System_time_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) 。 ## 网页前端 [TBD] 相信了解我的小伙伴知道我之前在 [LattePanda 项目之 P2.2 起飞条件检测系统(CLI & GUI)](https://www.cnblogs.com/sjqlwy/p/lattepanda_pyui.html) 一文中介绍过 [remi](https://github.com/dddomodossola/remi), Remi is a GUI library for Python applications that gets rendered in web browsers。 ![](https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20200521211147.png) 该前端实现的功能就是增加欲追踪包裹数据,发给后端。remi带了一个图形化编辑器,支持拖拽。[TBD]![](https://cdn.jsdelivr.net/gh/sjqlwy/blog_imgs@default/images/20200521220827.png) ## 使用 Firmata协议 交互 之前写过一部分,由于相关库更新,这里写一下。 之前主要介绍三个两款: * PyMata 和 PyMata-AIO,目前已更新为 [pymata-express](https://github.com/MrYsLab/pymata-express) 和 [pymata4](https://github.com/MrYsLab/pymata4), 支持 [FirmataExpress](https://github.com/MrYsLab/FirmataExpress) 。 * [pyFirmata](https://github.com/tino/pyFirmata) | 仅支持到 Firmata 2.1和 StandardaFirmata 如果你需要的功能不多,用起来越简单越好,就选pyFirmata。如果你有一些高级功能的应用需求,就选上面的两个,同时提供了增强版的[FirmataExpress](https://github.com/MrYsLab/FirmataExpress) sketch, 相比Arduino IDE 自带的 StandardFirmata 它提供了如下特性: - **HC-SR04 Ultrasonic Distance Sensors using a single pin.** - **DHT Humidity/Temperature Sensors.** - **Stepper Motors.** - **Piezo Tone Generation.** - **Baud rate of 115200** ### 更新Arduino IDE 之前用的 `sudo apt install arduino`进行安装的,打开感觉不太对,一股子历史感。一看还是超级老的版本,怀疑根本没人维护源里面的这个软件包,果断官网下载最新的 `arduino ide 1.8.12`。同时通过包管理器安装 `` ![image-20200524212347456](C:\Users\Xin\AppData\Roaming\Typora\typora-user-images\image-20200524212347456.png) ```python from pyfirmata import Arduino, util board = Arduino('/dev/ttyACM0') board.digital[13].write(1) ``` 剩下的就简单了,创建一个函数 `ArduCom()` , 想干啥干啥了。 ## Additional Functions | 额外功能 ### 热敏打印机 今天看了葛老师的帖子,想着也可以用那个标签打印机打印出来,后续会更新。 ### 网页面板 类似 Home Assistant 那种 ## 下期预告 想要实现一个功能,就是如果探测到人在旁边,就激活额外的提醒功能,例如语音播报等。难点就是如何知道人在旁边,[发现的一个思路]()就是使用 Kismet 这个软件进行无线嗅探。 一般情况下,我们的设备是如何连接到已经保存的无线网的呢?原理大致是设备一直向空中发送数据包,询问名为 XXX0 的无线网络在吗,如果无应答,就继续询问 XXX1 在吗?而如果相应的无线网络存在,就会回复我在,可以连接。 **题外话:** 这也就是网络欺骗的原理之一,设备询问有没有 XXX0 的时候,按规矩如果你不是 XXX0 的话,就不要吱声,而诱骗设备则会告诉设备我就是 XXX0 ,然后接收设备提供的密码进行连接,继而实现 mitm 中间人攻击。 那我们实现无线追踪的方法就是记录设备的 mac 地址,听到该设备在吆喝,就知道人在旁边。因为需要一个支持 monitor 模式的无线网卡,所以我们要额外通过有线网或者第二块无线网卡以供连接。因为板载的无线网卡虽然支持 monitor/station/client 模式的,但是 `iw list` 不支持同时运行2个以上模式。当然 Kismet 也支持蓝牙嗅探,但是需要专门的蓝牙芯片,适用性不那么强,而且并不是所有人的手机蓝牙都是一直开着的,比如我。 效果类似于这样: ![](https://magazine.odroid.com/wp-content/uploads/odroid_device_tracker_fig4.png) 好,我们下期再见!

代码 代码
	                    					#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__ = 'Code_Paintium'

import requests, time, sys
from tinydb import TinyDB, Query

db = TinyDB("/home/sjqlwy/Projects/kd/db.json")
parcel = Query()

# 填入自己的授权密钥,http://www.kdpt.net/express_api.html
# 支持快递公司列表 http://www.kdpt.net/index.html#companies
id = 'YOURIDKEY'


# 增加数据记录
def AddItem(name, com, postid):
    if db.search(parcel.postid == postid) == []:
        db.insert({'name': str(name), 'com': str(com), 'postid': str(postid), 'Fstatus': {}, 'Cstatus': {}, 'state': ''})


# 微信提醒
def fqtt(desp):
    payload_ftqq = {'text': name, 'desp': str(desp)}
    requests.get('https://sc.ftqq.com/YOURKEY.send', params=payload_ftqq)


# 检查快递状态
def check_status(com, postid, name, Cstatus):
    payload_status = {'id': id, 'com': com, 'nu': postid}
    r = requests.get('http://q.kdpt.net/api', params=payload_status).json()
    if Cstatus != r['data'][0]:
        Cstatus = r['data'][0]
        desp = Cstatus['time'], Cstatus['context']
        fqtt(desp)
        db.update({'Cstatus': Cstatus, 'Fstatus': r['data'], 'state': r['state']}, parcel.postid == postid)


# Main,当没有输入参数时检查已有数据;正确输入参数时创建条目;错误输入时提示
if len(sys.argv) == 4:
    AddItem(sys.argv[1], sys.argv[2], sys.argv[3])
elif len(sys.argv) == 1:
    pass
else:
    print("INVAIND INPUT! pls input name, companny, postid and try again.")
    print("e.g., python3 kd.py Gaoda zhongtong zt1124991")
    exit

# 仅检查未签收的项目,注意调用时间不小于1s
for item in db.search(parcel.state != '3'):
    com = item['com']
    postid = item['postid']
    name = item['name']
    Cstatus = item['Cstatus']
    # print(Cstatus)
    check_status(com, postid, name, Cstatus)
    time.sleep(2)
	                    				
Makelog作者原创文章,未经授权禁止转载。
4
1
评论
[[c.user_name]] [[c.create_time]]
[[c.parent_comment.count]]
[[c.comment_content]]