自从公司的易信公众服务号有了查询今日菜单的功能,自己慢慢养成了每次去吃饭前查一下各个窗口的菜谱,再决定去哪吃饭的习惯。
不过这个功能使用的越多,越来越觉得它不方便。目前在易信公众号查询菜单的步骤是:
所以我就想,有没有更简单的办法,直接把每日菜单内容直接主动推送到手机,只需要简单的点一下就能查看菜单了呢:
有了这个想法就动手做了。
这件事可以拆成三步:
下面详细说。
抓取手机网络请求的方法很多,最方便的办法应该是在手机后台跑一个类似tcpdump功能的工具的同时,访问易信的今日菜单,就可以抓到想要的结果。不过由于我的手机是没有越狱的iOS系统,由于沙盒机制的限制无法做到。
可以发现今日菜单的HTTPURL链接就是下面的pattern:
比较熟悉Python,就用Python实现了:
第二个问题稍微复杂一些,我们需要从爬取的HTML源数据提取出其中的文本数据,然后从中生成这份菜单的日期、早餐、午餐、晚餐信息。这个用稍微复杂一点的正则表达式也可以搞定。
Python中有一个比较有名的处理HTML格式内容的第三方库BeautifulSoup,使用非常方便:
获取菜单正文内容
def_parse(self,content):try:bs=BS(content,"html.parser")ifbs.find_all(class_="m-error"):returnNoneelse:returnbsexcept:LOG.exception("FailedtoParsecontent:%s"%content)def_handle_menu(bs):try:content=bs.find(id="divCNT")except:LOG.warn("Failedtogetcontent")returnNoneelse:returncontentHTML解析前是这个样子
解析后就是这个样子,已经把HTML的tag都脱掉了
判断是否是今日菜单
def_is_menu(text):#\u4eca\u65e5\u83dc\u5355=>今日菜单ifre.findall(ur"\u4eca\u65e5\u83dc\u5355",text,re.UNICODE):returnTrueelse:returnFalse提取菜单日期
def_handle_date(content):#\u6708=>月\u65e5=>日res=re.findall(ur"(\d+)\u6708(\d+)\u65e5",content.text,re.UNICODE)ifnotres:LOG.warn("Failedtoparsedate")returnNoneelse:month,day=tuple([int(i)foriinres[0]])year=datetime.datetime.now().yearreturndatetime.datetime(year,month,day)提取菜单早餐、午餐、晚餐内容
def_menu_to_text(content):#\u65e9\u9910=>早餐#\u4e2d\u9910=>中餐#\u665a\u9910=>晚餐#\u591c\u5bb5=>夜宵text=content.get_text()res=re.findall(ur"\u65e9\u9910([\s\S]+)\u4e2d\u9910([\s\S]+)"ur"\u665a\u9910([\s\S]+)\u591c\u5bb5",text,re.UNICODE|re.MULTILINE)ifnotres:LOG.warn("Failedtomatchmenu")returnNoneelse:menu={}menu[BREAKFAST]=res[0][0]menu[LUNCH]=res[0][1]menu[SUPPER]=res[0][2]returnmenu数据推送现在已经解决了今日菜单的数据爬取、处理,就差如何把菜单内容推送到手机了。
经过调研,iOS平台上比较好用的第三方消息推送服务有Pushover、Pushbullet、Boxcar、AmazonSNS等。
按照Pushbullet提供的API文档写一个HTTPPOST请求就可以实现推送功能了:
defsend_notification(subject,content,channel=PUSHBULLET_CHANNEL):try:res=requests.post("%s/pushes"%PUSHBULLET_API,headers={"Access-Token":PUSHBULLET_TOKEN},data={"title":subject,"body":content,"type":"note","channel_tag":channel},timeout=30)except:LOG.exception("Failedtosendnotification")else:ifres.status_code!=200:LOG.warn("Errorwhenpushingnotification")推送过来的菜单就是这样了:
PC/Mac端同样支持:
把上面这些代码片段拼起来,就是一个可以抓取、推送今日菜单的小项目了,最后能跑的代码放在这里(代码里还包含之前写的把菜单内容发邮件通知的功能):
其实就是将数据爬取、推送做成定时任务就可以了。我通过systemdtimer实现:
在virtualenv中运行脚本的wrapperrun.sh
#!/bin/bashBASE=/home/stanzgy/workspace/what2eat2day_ntes$BASE/.venv/bin/python$BASE/fetch.py$@今日菜单抓取service文件menu_fetch.service
[Unit]Description=FetchNetEasemenutoday[Service]Type=oneshotExecStart=/home/stanzgy/workspace/what2eat2day_ntes/run.shf[Install]WantedBy=multi-user.target今日菜单抓取timer文件menu_fetch.timer
[Unit]Description=FetchNetEasemenueveryday[Timer]OnCalendar=Mon-Fri*-*-*10:00:00Unit=menu_fetch.service[Install]WantedBy=multi-user.target推送今日菜单的timer配置类似上面,仅仅是命令行传入的参数不一样,这里就省略了。最后效果如下