博客搬遷至https://blog.wangjiegulu.comcss
RSS訂閱:https://blog.wangjiegulu.com/feed.xmlhtml
原文連接:https://blog.wangjiegulu.com/2018/04/03/huginn_douban_high_score_movies_and_slack/ios
若是還沒有安裝 Huginn,能夠參考這裏git
想象下如下場景:每當有正在上映的電影在豆瓣上的評分超過7.8分,則 huginn 自動編輯一條信息並經過 Slack
(固然也能夠用 telegram 等app)通知到我電腦或者手機上。收到信息後,點擊不喜歡忽略,或者點擊購票按鈕直接進入到購票頁面。甚至 Huginn 能夠結合 Google Calendar
查詢你這幾天的行程安排,推送高分電影信息的同時給你選擇一個比較合適觀看電影的時間點,購買好電影票後,huginn 又自動幫你把日程事件寫入到 Google Calendar
中,並設置提醒。是否是很酷?!github
Huginn 就如你的貼心管家,按照你的意願自動幫你完成不少事情。web
咱們先來實現 每當有正在上映的電影在豆瓣上的評分超過7.8分,則給我推送 Slack 信息
這一部分需求。chrome
最後達到的效果以下:json
首先進入 Huginn 首頁(默認localhost:3000
),左上角進入 Scenarios
:api
個人理解:Scenario 表明一種場景,通常會包含多個 agent,一個 agent 表示進行一次事件的處理或者變換。拿咱們如今的例子來講,自動經過slack推送豆瓣高分電影 這一整個就是一個 Scenario
,可是這個 Scenario
會有不少的 agent
s 組成,好比:網絡
score > 7.8
,看着跟 RxJava
的觀察者模式是否是很像?第一個從豆瓣頁面拉取數據的過程就像是 Observable
,而後其它的 agent 就像不少的 operator
用來把數據進行轉換和變化,最終通知到 subscriber
,這裏的 subscriber
就是咱們本身。咱們經過 huginn 訂閱了 豆瓣高分電影
,就是這麼簡單。
點擊左下角的 New Scenario
建立一個名爲 douban_high_score_movie
的 Scenario。
第一個 agent 用來從豆瓣官網獲取全部正在上映的電影
在 douban_high_score_movie
的 Scenario 中點擊 + New Agent
來建立第一個 Agent。
如上圖,你須要去決定你要建立的 agent 的類型(這裏是目前 Huginn 支持的全部的類型)。
咱們經過輸入 "web" 來進行過濾選擇 Website Agent
。
上圖,左邊是咱們須要去配置的地方;右邊是每一個設置對應的說明。
step1_get_douban_playing_movies
,表示這個 agent 是 douban_high_score_movie
這個 Scenario 的第一步,是用來從豆瓣獲取當前正在上映的全部電影。Every 1d
表示每一天執行一次、Every 2h
表示每2小時執行一次、8pm
表示天天下午8點執行等等;這裏咱們選擇 3pm
,天天下午3點執行一次。RxJava
作類比,由於每一個 agnet 都有可能只是整個觀察者模式中的一個操做符,用來轉化數據,數據轉化完以後,可能還須要其餘 agent 把這些數據作進一步的轉化。注意:以上沒提到的配置能夠留空
Options 配置其實就是一個 JSON 文件。Website Agent 的 Options 主要的元素有以下:
https://movie.douban.com/cinema/nowplaying/hangzhou/
,固然最後一個地點能夠根據你的常駐地點作相應的修改。xml
、html
、json
、text
四種,當前豆瓣網址返回的固然是 html 了,因此這裏咱們填寫 html
。若是其餘場景,好比 調用第三方開放的 api,返回的類型可能就是 json
或者 xml
了。on_change
。
type
是 html
,則每一個數據經過 css
選擇器或者 xpath
來解析出真正的數據。注意:
on_change
這個設置在咱們如今的場景下其實用處不大,這個下面咱們會再討論。
最後的 options 以下:
{ "expected_update_period_in_days": "2", "url": "https://movie.douban.com/cinema/nowplaying/hangzhou/", "type": "html", "mode": "on_change", "extract": { "title": { "css": "li[@data-category='nowplaying']", "value": "@data-title" }, "score": { "css": "li[@data-category='nowplaying']", "value": "@data-score" }, "star": { "css": "li[@data-category='nowplaying']", "value": "@data-star" }, "release": { "css": "li[@data-category='nowplaying']", "value": "@data-release" }, "region": { "css": "li[@data-category='nowplaying']", "value": "@data-region" }, "actors": { "css": "li[@data-category='nowplaying']", "value": "@data-actors" }, "director": { "css": "li[@data-category='nowplaying']", "value": "@data-director" }, "detail_url": { "css": "li[@data-category='nowplaying']/ul/li/a[@data-psource='poster']", "value": "@href" }, "image_url": { "css": "li[@data-category='nowplaying']/ul/li/a[@data-psource='poster']/img", "value": "@src" } } }
以上能夠看出,咱們從豆瓣的每部電影中獲取瞭如下信息:
注意:獲取具體 xpath 比較簡單的方法:經過 chrome 右鍵的
inspect
來複制拿到。
以上配置完畢後,點擊下面的 Dry Run
,應該就會出現如下頁面
最後進行保存。第一個 agent 就建立完畢了。
同時,這個 agent 在運行的過程當中會生成如下 events:
step1_get_douban_playing_movies
把全部正在上映的電影數據從豆瓣上拉取下來並解析好,生成一個個 events。而後咱們第二個 agent 就須要從這些 events 裏面進行過濾篩選出全部分數大於 7.8
(具體的標準能夠本身定) 的電影。至關於 RxJava 的 filter 操做符吧。
一樣建立 agent,選擇爲 TriggerAgent
,名字爲 step2_pick_high_score_movies
。這是把 Sources 填寫爲第一個 agent 的名字,即 step1_get_douban_playing_movies
,表示我要建立的 agent 處理的數據(events)是從 step1_get_douban_playing_movies
來的。
而後重點仍是在 Options 中
step1_get_douban_playing_movies
這個 agent 收到的 events 原封不動地再傳給下一個 agent(下一個 agent 咱們還沒建立),咱們設置爲 true。由於下一個 agent 咱們是用來把數據經過 slack 發送到給咱們本身的,那確定須要第一個 agent 中獲取到的例如電影名字、分數等信息。最後 Options 的配置以下:
{ "expected_receive_period_in_days": "2", "keep_event": "true", "rules": [ { "type": "field>=value", "value": "7.8", "path": "$.score" } ], "message": "Looks like your pattern matched in '{{value}}'!" }
如上,在 rules 中添加一個規則,type 表示匹配規則,field>=value
:
$.score
,因此表示的是電影的分數;value
字段的值,這裏爲 7.8
。經過簡單的表達式 field>=value
來設定匹配規則:電影分數 >= 7.8分。
至此,第二個 agent 建立完畢。
你一樣能夠經過下面的 Dry Run
來進行測試,測試時由於有 Sources
,須要你構造一些假數據做爲輸入來運行。
step2_pick_high_score_movies
用來把 step1_get_douban_playing_movies
中從豆瓣官網獲取的電影信息進行高分的過濾(分數>=7.8)。
咱們還須要建立一個去重的 agent,來避免重複給咱們本身推送高分電影(由於咱們如今獲取的頻率是天天進行獲取檢測,可是電影總不多是每部電影只上映一天吧,次日獲取的時候確定有第一天獲取的數據)。
這裏你們可能會有個問題,由於咱們在配置第一個 agent 的時候,已經把 mode
已經設置爲 on_change
了,爲何仍是會有重複數據呢?由於這裏的電影信息中,有諸如 分數
這類的數據,這些數據是隨時可能會有變化的,雖然是同一個電影,可是分數從 8.1
上升到 8.2
,那 Huginn 也會認爲知足了 on_change
條件,因此會形成重複推送。因此,咱們還須要單獨作去重處理。
注意: 以前提到過
on_change
等設置在第一個 agent 其實用處不大,一樣也是因爲上面說的緣由,咱們也不知道一樣的電影何時分數會發生變化,就算用了on_change
,也可能會把以前獲取過的數據拿到。因此第一個 agent 的 keep_event 設置的時間比較短,由於這些 events 提供給on_change
匹配意義不大,因此仍是節省空間,設置短一點。
建立 agent,type 選擇 DeDuplicationAgent
,名字取爲 step2_1_deduplication_high_score_movies
,Sources 填寫爲上一個 agent 的名字,即 step2_pick_high_score_movies
。
注意:這裏 keep_event 設置了90天,由於一旦通過咱們這個 agent 去重後,events 假設保留1小時,那下一天我再去獲取全部上映的電影並高分過濾後,由於昨天的數據(events)已經被清空了,因此就沒辦法作比較去重了,因此會致使重複數據。因此這裏保存時間應該要>=電影上映的時長,因此這裏設置爲90天,即3個月左右。
DeDuplicationAgent 的 Options 填寫就比較簡單了
title
。Huginn 自帶有一個 SlackAgent
,用來發送 slack 消息。
它使用了 incoming-webhooks 來實現消息的發送。
可是爲了有更多的可玩性,咱們這裏選擇,本身建立一個 slack app
,而後經過它的 open api 實現。
所以,咱們須要建立一個 PostAgent
。可是在此以前咱們先來配置好 Slack 環境。
安裝 Slack:https://slack.com
建立本身的 workspace(單首創建一個本身私有的,注意不要使用公司、團隊的 workspace),好比個人是 https://wangjie.slack.com
。
在本身私有的 workspace 中建立一個私有的 channel:#huginn-movie
這個 channel 就是用來接收高分電影的數據了,固然你也可使用 #general
。
而後咱們建立一個本身的 app,用來發送電影信息。進入 https://api.slack.com/
點擊 Start Building
,
在 Add features and functionality
中點擊 Permissions
進入權限配置。
在 Scope
中添加以下權限:
添加完以上全部權限後,點擊保存,而後從新打開 Permissions
,點擊下面按鈕安裝咱們的這個 app 到 slack。
安裝完畢以後,再次進入 Permissions
,拷貝 OAuth Access Token
:
而後,咱們就可使用咱們的 token 來訪問 slack 的 open api 了,具體文檔在這裏:https://api.slack.com/web。
咱們須要的發送消息到 #huginn-movie
channel 的接口文檔:
https://api.slack.com/methods/chat.postMessage
有了 api 文檔,有了 token,一切就好辦了。
由上述文檔,咱們能夠經過 post 請求,把咱們要發送的電影信息封裝到 attachments
參數中執行請求便可。
並且 attachments
參數能夠參考文檔 https://api.slack.com/docs/message-attachments 來封裝信息。
Slack 環境一切就緒,接下來,回到 Huginn。
建立 PostAgent
(注意,不是 SlackAgent
),取名爲 step3_high_score_movies_to_slack_post
。Sources 填寫爲 step2_1_deduplication_high_score_movies
,由於這個 agent 須要把去重後的電影信息經過 slack 發送給咱們。
最終的 Options 配置以下:
{ "post_url": "{% credential slack_huginn_url_post_message %}", "expected_receive_period_in_days": "1", "content_type": "json", "method": "post", "payload": { "channel": "huginn-movie", "username": "Douban Movie", "icon_url": "https://img3.doubanio.com/pics/douban-icons/favicon_48x48.png", "attachments": [ { "fallback": "Required plain-text summary of the attachment.", "mrkdwn_in": [ "text", "pretext" ], "color": "#36a64f", "pretext": "Hi~ <@{% credential slack_at_user_id %}>, There is *high score* movie.", "author_name": "{{director}}", "author_link": "{{detail_url}}", "author_icon": "", "title": "《{{title}}》", "title_link": "{{detail_url}}", "text": "*Actors*: {{actors}}", "fields": [ { "title": "Score", "value": "{{score}}", "short": true }, { "title": "Star", "value": "{{star}}", "short": true }, { "title": "Region", "value": "{{region}}", "short": true }, { "title": "Release", "value": "{{release}}", "short": true } ], "image_url": "", "thumb_url": "{{image_url}}", "footer": "Slack", "footer_icon": "https://platform.slack-edge.com/img/default_application_icon.png", "ts": "{{\"now\" | date: \"%s\"}}" } ] }, "headers": { "Content-Type": "application/json", "Authorization": "{% credential slack_huginn_token %}" }, "emit_events": "false", "no_merge": "false", "output_mode": "clean" }
須要注意的是:
{\% credential slack_huginn_url_post_message %\}
:此類的表達式爲 Liquid-interpolated,具體的值配置在 Credentials
中,能夠理解爲全局定義,在 Credentials
中配置好 key-value
以後,能夠在其它地方以諸如 {\% credential key \%}
的方式來使用,這裏不作過多介紹了。
@
某人的功能時,須要拿到對應用戶的 ID,能夠的獲取方式能夠經過在 slack 中選中名字而後 Copy link
的方式拿到用戶連接,用戶鏈接的最後就是 ID。保存該 Agent,至此,所需的全部的 Agent 都已經建立完畢了。
整個 Scenario 的事件流程圖以下:
Huginn 還支持公開你建立的 Scenario,提供給其它人使用,以上的代碼也已經公開:
http://h.wangjiegulu.com/scenarios/8/export.json
你們能夠直接下載使用,不過須要在 Credentials
中配置以下參數:
https://slack.com/api/chat.postMessage
便可。除了以上例子,Huginn 還能夠完成更多奇思妙想,限制你的只有你的想象力。