Huginn實現自動通過slack推送豆瓣高分電影


博客搬遷至https://blog.wangjiegulu.com

RSS訂閱:https://blog.wangjiegulu.com/feed.xml

原文鏈接https://blog.wangjiegulu.com/2018/04/03/huginn_douban_high_score_movies_and_slack/

Huginn實現自動通過slack推送豆瓣高分電影

如果尚未安裝 Huginn,可以參考這里

想象下以下場景:每當有正在上映的電影在豆瓣上的評分超過7.8分,則 huginn 自動編輯一條信息並通過 Slack (當然也可以用 telegram 等app)通知到我電腦或者手機上。收到信息后,點擊不喜歡忽略,或者點擊購票按鈕直接進入到購票頁面。甚至 Huginn 可以結合 Google Calendar 查詢你這幾天的行程安排,推送高分電影信息的同時給你選擇一個比較合適觀看電影的時間點,購買好電影票后,huginn 又自動幫你把日程事件寫入到 Google Calendar 中,並設置提醒。是不是很酷?!

Huginn 就如你的貼心管家,按照你的意願自動幫你完成很多事情。

我們先來實現 每當有正在上映的電影在豆瓣上的評分超過7.8分,則給我推送 Slack 信息 這一部分需求。

最后達到的效果如下:


手機端效果

PC端效果

創建 Agents

首先進入 Huginn 首頁(默認localhost:3000),左上角進入 Scenarios

我的理解:Scenario 代表一種場景,一般會包含多個 agent,一個 agent 表示進行一次事件的處理或者變換。拿我們現在的例子來說,自動通過slack推送豆瓣高分電影 這一整個就是一個 Scenario,但是這個 Scenario 會有很多的 agents 組成,比如:

  • 有一個 agent 是用來從豆瓣網頁獲取當前上映中的所有電影和它們的分數等信息;
  • 一個 agent 是用來從第一個 agent 里面拿到的所有電影進行過濾,過濾的標准就是 score > 7.8
  • 還有一個 agent 是用來把過濾后的電影通過 slack 推送到我們手機上。

看着跟 RxJava 的觀察者模式是不是很像?第一個從豆瓣頁面拉取數據的過程就像是 Observable,然后其它的 agent 就像很多的 operator 用來把數據進行轉換和變化,最終通知到 subscriber,這里的 subscriber 就是我們自己。我們通過 huginn 訂閱了 豆瓣高分電影,就是這么簡單。

點擊左下角的 New Scenario 創建一個名為 douban_high_score_movie 的 Scenario。

創建獲取數據 agent

第一個 agent 用來從豆瓣官網獲取所有正在上映的電影

douban_high_score_movie 的 Scenario 中點擊 + New Agent 來創建第一個 Agent。

如上圖,你需要去決定你要創建的 agent 的類型(這里是目前 Huginn 支持的所有的類型)。

我們通過輸入 "web" 來進行過濾選擇 Website Agent

上圖,左邊是我們需要去配置的地方;右邊是每個設置對應的說明。

  • Name:給這個 agent 取個名字,我們這里取名為 step1_get_douban_playing_movies,表示這個 agent 是 douban_high_score_movie 這個 Scenario 的第一步,是用來從豆瓣獲取當前正在上映的所有電影。
  • Schedule:表示調度周期,表示在什么時候自動執行這個 agent,比如 Every 1d 表示每一天執行一次、Every 2h 表示每2小時執行一次、8pm 表示每天下午8點執行等等;這里我們選擇 3pm,每天下午3點執行一次。
  • Keep events:表示事件保留的時間;比如我們從豆瓣上獲取到所有上映的電影,每一部電影信息都是一個 event,Huginn會把這些 event 保留在本地,你可以通過這個參數來設置這些 events 在本地保留多少時間,超過這個時間,Huginn會把數據清除。我們這里設置1小時(為什么只設置為1小時,下面我們會再討論)。
  • Sources:表示這個 agent 處理的數據來源是哪個 agent。我們現在創建的 agent 是第一個 agent,是從豆瓣網站上獲取正在上映的所有電影,所以不需要從其他 agent 傳遞數據(也就是上面說的 events)過來,所以這個留空。
  • Receivers:表示這個 agent 處理完數據之后把這些數據傳入到哪個 agent。還是用 RxJava 做類比,因為每個 agnet 都有可能只是整個觀察者模式中的一個操作符,用來轉化數據,數據轉化完之后,可能還需要其他 agent 把這些數據做進一步的轉化。
  • Scenarios:表示這個 agent 是數據哪個 Scenario 的。
  • Options:這個非常關鍵,就是通過這個配置文件(JSON)來進行網絡請求和豆瓣電影數據解析相關的操作的,這個我們重點講下。

注意:以上沒提到的配置可以留空

Options 配置

Options 配置其實就是一個 JSON 文件。Website Agent 的 Options 主要的元素有如下:

  • url:網站地址,表示我需要從哪個網站獲取數據,現在我們是從豆瓣,所以需要輸入豆瓣正在上映的網址,這里我們輸入 https://movie.douban.com/cinema/nowplaying/hangzhou/,當然最后一個地點可以根據你的常駐地點做相應的修改。
  • type:數據解析的類型,支持的類型有 xmlhtmljsontext 四種,當前豆瓣網址返回的當然是 html 了,所以這里我們填寫 html。如果其他場景,比如 調用第三方開放的 api,返回的類型可能就是 json 或者 xml了。
  • mode:表示獲取數據的模式,我們這里選擇 on_change
    • on_change:在數據有更改時才會獲取作為 events。
    • merge:把新數據和輸入的數據進行合並。
    • all:獲取所有數據。
  • extract:用來配置(JSON)從這個網站解析出真正我們想要的數據。如果 typehtml,則每個數據通過 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"
    }
  }
}

以上可以看出,我們從豆瓣的每部電影中獲取了以下信息:

  • title:電影名字
  • score:電影分數,滿分10分
  • star:電影分數,滿分50分
  • release:上映日期
  • region:地區
  • actors:演員
  • director:導演
  • detail_url:詳細 url
  • image_url:電影封面

注意:獲取具體 xpath 比較簡單的方法:通過 chrome 右鍵的 inspect 來復制拿到。

以上配置完畢后,點擊下面的 Dry Run,應該就會出現以下頁面

最后進行保存。第一個 agent 就創建完畢了。

同時,這個 agent 在運行的過程中會生成以下 events:

創建過濾 agnet

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 中

  • keep_event:表示是否把我從 step1_get_douban_playing_movies 這個 agent 收到的 events 原封不動地再傳給下一個 agent(下一個 agent 我們還沒創建),我們設置為 true。因為下一個 agent 我們是用來把數據通過 slack 發送到給我們自己的,那肯定需要第一個 agent 中獲取到的例如電影名字、分數等信息。
  • rules:表示我們過濾的規則,可以多個,具體下面說。
  • must_match:表示 rules 中我必須要滿足幾個規則,如果是1,則意味着 rules 中所有的規則是或關系(只要滿足 rules 中的1個規則即可);默認不填寫的話是必須要滿足 rules 中所有的規則。,因為我們這里只需要滿足一個分數大於7.8就可以,所以可以不填寫。

最后 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

  • field: 通過下面 path 從 events 匹配出來的數據,這里是 $.score,所以表示的是電影的分數;
  • value:表示下面 json 的 value 字段的值,這里為 7.8

通過簡單的表達式 field>=value 來設定匹配規則:電影分數 >= 7.8分。

至此,第二個 agent 創建完畢。

你同樣可以通過下面的 Dry Run 來進行測試,測試時因為有 Sources,需要你構造一些假數據作為輸入來運行。

創建去重 agnet

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_moviesSources 填寫為上一個 agent 的名字,即 step2_pick_high_score_movies

注意:這里 keep_event 設置了90天,因為一旦經過我們這個 agent 去重后,events 假設保留1小時,那下一天我再去獲取所有上映的電影並高分過濾后,因為昨天的數據(events)已經被清空了,所以就沒辦法做比較去重了,所以會導致重復數據。所以這里保存時間應該要>=電影上映的時長,所以這里設置為90天,即3個月左右。

DeDuplicationAgent 的 Options 填寫就比較簡單了

  • Property:填寫你要去重依據的字段,我們這里根據電影名字來去重,也就是 title
  • Lookback:表示去重的時候跟之前的多少條歷史 events 做比較,同一時期一起上映的電影應該不會超過100部,所以設置為100了。

創建 slack 通知的 agent

Huginn 自帶有一個 SlackAgent,用來發送 slack 消息。

它使用了 incoming-webhooks 來實現消息的發送。

但是為了有更多的可玩性,我們這里選擇,自己創建一個 slack app,然后通過它的 open api 實現。

因此,我們需要創建一個 PostAgent。但是在此之前我們先來配置好 Slack 環境。

配置 Slack 環境

安裝 Slack:https://slack.com

創建自己的 workspace(單獨創建一個自己私有的,注意不要使用公司、團隊的 workspace),比如我的是 https://wangjie.slack.com

在自己私有的 workspace 中創建一個私有的 channel:#huginn-movie

這個 channel 就是用來接收高分電影的數據了,當然你也可以使用 #general

然后我們創建一個自己的 app,用來發送電影信息。進入 https://api.slack.com/

點擊 Start Building

  • App Name:可以隨意填寫
  • Development Slack Workspace:選擇你剛剛創建的私有的 workspace

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。

創建 Agent 發送 Slack 消息

創建 PostAgent(注意,不是 SlackAgent),取名為 step3_high_score_movies_to_slack_postSources 填寫為 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 \%} 的方式來使用,這里不做過多介紹了。
- 在消息中使用Slack 中的 `@` 某人的功能時,需要拿到對應用戶的 ID,可以的獲取方式可以通過在 slack 中選中名字然后 `Copy link` 的方式拿到用戶鏈接,用戶連接的最后就是 ID。

保存該 Agent,至此,所需的所有的 Agent 都已經創建完畢了。

總結

整個 Scenario 的事件流程圖如下:

Huginn 還支持公開你創建的 Scenario,提供給其它人使用,以上的代碼也已經公開:

http://h.wangjiegulu.com/scenarios/8/export.json

大家可以直接下載使用,不過需要在 Credentials 中配置如下參數:

  • slack_huginn_token:你創建的 Slack App 的 OAuth Access Token,具體方式可以參考這里
  • slack_at_user_id:你需要 @ 的 slack 用戶 ID,填寫你自己的,拿到你 ID 的方式可以參考這里
  • slack_huginn_url_post_message:填寫 https://slack.com/api/chat.postMessage 即可。

除了以上例子,Huginn 還可以完成更多奇思妙想,限制你的只有你的想象力。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM