在树莓派上部署微信机器人
第二季度接了一个有意思的任务——用机器学习算法做数据拟合,算法本身没什么,但是如何让一个在python中的算法变成一个有界面的服务就比较有意思了。我刚好又买了一块树莓派3,于是打算把后端程序放在树莓派上运行。经过上一篇的工作,树莓派已经具备web发布功能,我们可以部署一个微信机器人用来监视树莓派上的计算任务……
说实在的,这篇文章算是一个闲篇。就是最近帮一个做期货的朋友做了一个盯盘小程序,这个程序是需要24小时运行的,而且没有什么计算量,主要就是抓数据,是一个非常适合树莓派的应用场景。
1. 安装itchat
itchat是一个通过抓包分析并实现的基于python的微信客户端(个人感觉相当给力)。
首先是安装:
1 | sudo pip3 install itchat |
然后按照官方教程写一个测试小程序:
1 | import itchat |
将小程序拷贝到树莓派上(貌似VNC客户端也可以通过图形界面拷贝,但我没试过,在使用scp
命令前最好在树莓派上配置ssh
,以后会比较方便):
1 | scp -r test pi@192.168.1.106:webapp/ |
这时候测试发现问题来了,不论是用树莓派自带的python3.4、或是用conda中的python3.4、甚至是树莓派自带的python2.7运行测试脚本,都会报一河滩错,大致类似:
1 | requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE', 'certificate verify failed')],)",) |
乍一看是requests的报错,觉得是小事,没想到接下来是长达几小时的填坑[捂脸]……
1.1 尝试解决requests报错
为了解决上面这个requests
报错,需要安装相关库(操作依据Raspbian (latest) + Python : SSL error when trying to send data):
1 | sudo apt-get install libffi-dev libssl-dev |
经测试发现上面这个方法对报错没有任何影响(-_-)
1.2 尝试解决OpenSSL报错
考虑更新树莓派上的OpenSSL(操作依据SSL Error: bad handshake #3212的第一种方法):
测试OpenSSL时可以直接使用openssl
命令,会发现能够正常连接的主机(比如github.com:443
)与会产生证书错误的主机(比如login.weixin.qq.com:443
)的返回信息状态不同(正常是0
,微信非正常的返回了20
):
1 | openssl s_client -connect "<url>:443" -showcerts -servername "<url>" |
更新方法参考How to install OpenSSL 1.0.2 on Raspberry Pi3
为包管理器手动新增源:
1 | echo "deb-src http://httpredir.debian.org/debian jessie-backports main contrib non-free" | sudo tee /etc/apt/sources.list.d/jessie-backports.list |
更新包管理器:
1 | sudo apt-get update |
下载jessie-backports
中的OpenSSL源码:
1 | apt-get source --allow-unauthenticated openssl/jessie-backports |
安装debain构建程序:
1 | sudo apt-get install devscripts |
一步一步执行这个过程会用掉几个小时[捂脸]……结束后就可以测试安装结果了:
1 | /usr/bin/openssl version |
此时在使用openssl
命令会发现刚才提到的两个主机的返回值一致了。然而并没有什么卵用,用python运行程序依然是一样的报错(这时候已经是凌晨了[捂脸]此时我是崩溃的)。
这时候突然意识到,python本身build的时候是需要使用OpenSSL源码的,也就是说,python本身有自己的OpenSSL。到python的/bin
文件夹下一看,果然是有openssl,检查一下:
1 | python -c "import ssl; print(ssl.OPENSSL_VERSION)" |
因为python本身的OpenSSL版本和系统OpenSSL版本不同,要使用这种方法解决问题,必须使用当前树莓派的OpenSSL版本(我们升级过的1.0.2l)源码编译python[捂脸]……
1.3 将requests的环境变量指向旧证书
我真是懒得在树莓派上编译python,于是只好再试另一种补丁试的方法,虽然不彻底,但是能解决问题(操作依据SSL Error: bad handshake #3212的第二种方法):
找到当前python的weak.pem:
1 | python -c "import certifi; print(certifi.old_where())" |
此时,要为requests配置环境变量:
1 | export REQUESTS_CA_BUNDLE=/home/pi/miniconda3/envs/webenv/lib/python3.4/site-packages/certifi/weak.pem |
再使用python的requests.get测试连接微信主机,成功!在以后的python代码的requests发送https请求之前,加上以下几行代码即可:
1 | import os, certifi |
如果像我一样在Flask程序中使用celery
异步处理作业,则需要在celery运行入口中(比如我的就是在app/__init__
)加入上面这段代码。
2. 安装终端复用程序Tmux
上面这个小程序需要同时开三四个终端监视运行状态(redis-server
、celery
、flask
、redis-cli
等),所以安装一个tmux
会非常方便。
1 | sudo apt-get install tmux |
配置鼠标模式参考:https://gist.github.com/niun/c7fd6abb5c0d5e847890
tmux重新连接会话:session:tmux attach [-t target-session] <host>
tmux退出连接的会话:^b
+d
或者^b
+命令:detach
另外,将wget的内容输出到终端的命令时:wget -qO- <url>
(-q
为quiet模式,屏蔽request header信息的回显;-O
指定输出文件,后面加-
,就定向为标准输出了)。