推送公司今日選單內容到手機
此文已由作者張耕源授權網易雲社群釋出。
歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。
自從公司的易信公眾服務號有了查詢今日選單的功能,自己慢慢養成了每次去吃飯前查一 下各個視窗的菜譜,再決定去哪吃飯的習慣。
不過這個功能使用的越多,越來越覺得它不方便。目前在易信公眾號查詢選單的步驟是:
-
開啟易信
-
開啟網易精靈公眾號
-
點選便捷服務
-
點選今日選單
-
等待返回今日選單的入口連結
-
點選入口連結檢視今日選單
作為固定的每天至少要操作兩次的動作,整個流程是被動的、並且有點複雜了。特別是 第五步、第六步都需要網路訪問,如果手機網路訪問不穩定(WiFi、4G訊號不好等,在 坐、等電梯時很容易碰到這個情況),其中任何一步都會卡住導致無法查詢;還有一些 同學由於種種原因根本就沒有關注網易精靈公共號,無從檢視今日選單。
所以我就想,有沒有更簡單的辦法,直接把每日選單內容直接主動推送到手機,只需要 簡單的點一下就能檢視選單了呢:
-
點選推送訊息
-
檢視今日選單
有了這個想法就動手做了。
這件事可以拆成三步:
-
資料抓取
-
資料處理
-
資料推送
下面詳細說。
資料抓取
要想爬菜譜的資料,首先需要知道這些資訊是從哪裡查詢出來的。我沒有做過易信公眾號 的開發,但是根據一般的經驗,不管是微信還是易信的公眾號釋出的文章一般就是一個 簡單的 HTTP 頁面。想要找出每日選單資料的來源,找出這些 HTTP 頁面地址的 pattern 一般就搞定了。
抓取手機網路請求的方法很多,最方便的辦法應該是在手機後臺跑一個類似 tcpdump 功能 的工具的同時,訪問易信的今日選單,就可以抓到想要的結果。不過由於我的手機是沒有 越獄的 iOS 系統,由於沙盒機制的限制無法做到。
最後用的辦法是,在相同網路的電腦上跑一個 mitmproxy ofollow,noindex">1 服務,將手機的 HTTP Proxy 地址指定為電腦的地址,在易信裡開啟今日選單的連結,就可以在 mitmproxy 裡看到一串 手機的 HTTP 訪問記錄,其中就包含我們要抓取今日選單的 HTTP URL 。
可以發現今日選單的 HTTP URL 連結就是下面的 pattern:
http://numenplus.yixin.im/singleNewsWap.do?companyId=1&materialId=${id}複製程式碼
其中只有一個變數 ${id} ,是個正整數,應該就是文章的ID。我們要爬的每日選單內容, 都在這些連結裡面,還包含網易精靈公眾號釋出的其他一些廣告文章,這個頁面就是簡單 的 HTTP GET 就可以爬到內容,不需要做額外的處理。
自己研究了一下沒有找出今日選單的文章的 ID 生成的規律,推測應該是在易信後端生成 的,手機客戶端無法直接拿到這個 ID。於是乾脆就每個ID都爬一遍,檢查內容是今日選單 的文章就處理,不是就忽略。這樣就搞定今日選單的資料來源了。
比較熟悉 Python,就用 Python 實現了:
import requestsdef http_get(url, timeout=3): try: res = requests.get(url, timeout)except: LOG.exception("Failed to GET: %s" % url)else:if res.status_code != 200:return None else:return resdef fetch(start, step=300): last_id = startfor i in iter(range(start, start + step)): url = ("http://numenplus.yixin.im/singleNewsWap.do?" "companyId=1&materialId=%d" % i) response = http_get(url)if not response:continue # handle menu data here複製程式碼
資料處理
資料處理,主要有兩個任務:
-
上面也提到了,需要檢查爬取的文章內容是不是今日選單
-
解析 HTML 內容,獲得我們想要的選單資訊
第一個問題比較簡單,我們可以直接通過簡單的關鍵詞正則表示式匹配來檢查。比如文章 內容含有“今日選單”這四個字,我們就認為這篇文章內容是今日選單。
第二個問題稍微複雜一些,我們需要從爬取的 HTML 源資料提取出其中的文字資料,然後 從中生成這份選單的日期、早餐、午餐、晚餐資訊。這個用稍微複雜一點的正則表示式也 可以搞定。
Python 中有一個比較有名的處理 HTML 格式內容的第三方庫 BeautifulSoup , 使用非常方便:
獲取選單正文內容
def _parse(self, content): try: bs = BS(content, "html.parser")if bs.find_all(class_="m-error"):return None else:return bsexcept: LOG.exception("Failed to Parse content: %s" % content)def _handle_menu(bs): try: content = bs.find(id="divCNT")except: LOG.warn("Failed to get content")return None else:return content複製程式碼
HTML 解析前是這個樣子
解析後就是這個樣子,已經把 HTML 的 tag 都脫掉了
判斷是否是今日選單
def _is_menu(text): # \u4eca\u65e5\u83dc\u5355 => 今日選單 if re.findall(ur"\u4eca\u65e5\u83dc\u5355", text, re.UNICODE):return True else:return False複製程式碼
提取選單日期
def _handle_date(content): # \u6708 => 月 \u65e5 => 日 res = re.findall(ur"(\d+)\u6708(\d+)\u65e5", content.text, re.UNICODE)if not res: LOG.warn("Failed to parse date")return None else: month, day = tuple([int(i) for i in res[0]]) year = datetime.datetime.now().yearreturn datetime.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)if not res: LOG.warn("Failed to match menu")return None else: menu = {} menu[BREAKFAST] = res[0][0] menu[LUNCH] = res[0][1] menu[SUPPER] = res[0][2]return menu複製程式碼
資料推送
現在已經解決了今日選單的資料爬取、處理,就差如何把選單內容推送到手機了。
經過調研,iOS 平臺上比較好用的第三方訊息推送服務有 Pushover、Pushbullet、 Boxcar、Amazon SNS 等。
Amazon SNS 沒有提供現成的客戶端首先否決掉;Pushover 綜合看起來是最好的選擇, 不過每個手機客戶端使用需要付 5$ 的一次性授權費用,窮,也否決掉;綜合看起來, Pushbullet 功能較全、免費、文件清晰、全平臺支援,最後選擇 Pushbullet 推送訊息。
按照 Pushbullet 提供的 API 文件寫一個 HTTP POST 請求就可以實現推送功能了:
def send_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("Failed to send notification")else:if res.status_code != 200: LOG.warn("Error when pushing notification")複製程式碼
推送過來的選單就是這樣了:
PC/Mac 端同樣支援:
把上面這些程式碼片段拼起來,就是一個可以抓取、推送今日選單的小專案了,最後能跑的 程式碼放在這裡(程式碼裡還包含之前寫的把選單內容發郵件通知的功能):
https://g.hz.netease.com/hzzhanggy/what2eat2day_ntes複製程式碼
自動化
整個資料爬取、推送的流程都寫好了,最後剩下的需要做的事情就是讓整個流程自動化 執行,我們只需要每天飯點看手機推送訊息就可以了。
其實就是將資料爬取、推送做成定時任務就可以了。我通過 systemd timer 實現:
在 virtualenv 中執行指令碼的 wrapper run.sh
#!/bin/bashBASE=/home/stanzgy/workspace/what2eat2day_ntes$BASE/.venv/bin/python $BASE/fetch.py $@複製程式碼
今日選單抓取 service 檔案 menu_fetch.service
[Unit]Description=Fetch NetEase menu today[Service]Type=oneshotExecStart=/home/stanzgy/workspace/what2eat2day_ntes/run.sh f[Install]WantedBy=multi-user.target複製程式碼
今日選單抓取 timer 檔案 menu_fetch.timer
[Unit]Description=Fetch NetEase menu everyday[Timer]OnCalendar=Mon-Fri *-*-* 10:00:00Unit=menu_fetch.service[Install]WantedBy=multi-user.target複製程式碼
推送今日選單的 timer 配置類似上面,僅僅是命令列傳入的引數不一樣,這裡就省略了。 最後效果如下
更多網易技術、產品、運營經驗分享請點選。
相關文章:
【推薦】 關於網易雲驗證碼V1.0版本的服務介紹