天天看點

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 資訊

這一部分需求。

最後達到的效果如下:

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

手機端效果

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

PC端效果

建立 Agents

首先進入 Huginn 首頁(預設

localhost:3000

),左上角進入

Scenarios

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

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

Scenario

,但是這個

Scenario

會有很多的

agent

s 組成,比如:

  • 有一個 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。

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

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

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

Website Agent

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

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

  • 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 主要的元素有如下:

Huginn實作自動通過slack推送豆瓣高分電影
  • url:網站位址,表示我需要從哪個網站擷取資料,現在我們是從豆瓣,是以需要輸入豆瓣正在上映的網址,這裡我們輸入

    https://movie.douban.com/cinema/nowplaying/hangzhou/

    ,當然最後一個地點可以根據你的常駐地點做相應的修改。
  • type:資料解析的類型,支援的類型有

    xml

    html

    json

    text

    四種,目前豆瓣網址傳回的當然是 html 了,是以這裡我們填寫

    html

    。如果其他場景,比如 調用第三方開放的 api,傳回的類型可能就是

    json

    或者

    xml

    了。
  • mode:表示擷取資料的模式,我們這裡選擇

    on_change

    • on_change:在資料有更改時才會擷取作為 events。
    • merge:把新資料和輸入的資料進行合并。
    • all:擷取所有資料。
  • extract:用來配置(JSON)從這個網站解析出真正我們想要的資料。如果

    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"
    }
  }
}
           

以上可以看出,我們從豆瓣的每部電影中擷取了以下資訊:

  • title:電影名字
  • score:電影分數,滿分10分
  • star:電影分數,滿分50分
  • release:上映日期
  • region:地區
  • actors:演員
  • director:導演
  • detail_url:詳細 url
  • image_url:電影封面
注意:擷取具體 xpath 比較簡單的方法:通過 chrome 右鍵的

inspect

來複制拿到。

以上配置完畢後,點選下面的

Dry Run

,應該就會出現以下頁面

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

最後進行儲存。第一個 agent 就建立完畢了。

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

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

建立過濾 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_movies

,Sources 填寫為上一個 agent 的名字,即

step2_pick_high_score_movies

注意:這裡 keep_event 設定了90天,因為一旦經過我們這個 agent 去重後,events 假設保留1小時,那下一天我再去擷取所有上映的電影并高分過濾後,因為昨天的資料(events)已經被清空了,是以就沒辦法做比較去重了,是以會導緻重複資料。是以這裡儲存時間應該要>=電影上映的時長,是以這裡設定為90天,即3個月左右。

DeDuplicationAgent 的 Options 填寫就比較簡單了

Huginn實作自動通過slack推送豆瓣高分電影
  • Property:填寫你要去重依據的字段,我們這裡根據電影名字來去重,也就是

    title

  • Lookback:表示去重的時候跟之前的多少條曆史 events 做比較,同一時期一起上映的電影應該不會超過100部,是以設定為100了。

建立 slack 通知的 agent

Huginn 自帶有一個

SlackAgent

,用來發送 slack 消息。

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

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

但是為了有更多的可玩性,我們這裡選擇,自己建立一個

slack app

,然後通過它的 open api 實作。

是以,我們需要建立一個

PostAgent

。但是在此之前我們先來配置好 Slack 環境。

配置 Slack 環境

安裝 Slack:https://slack.com

  • Google Play for Android:https://play.google.com/store/apps/details?id=com.Slack

建立自己的 workspace(單獨建立一個自己私有的,注意不要使用公司、團隊的 workspace),比如我的是

https://wangjie.slack.com

在自己私有的 workspace 中建立一個私有的 channel:

#huginn-movie

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

這個 channel 就是用來接收高分電影的資料了,當然你也可以使用

#general

然後我們建立一個自己的 app,用來發送電影資訊。進入 https://api.slack.com/

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

點選

Start Building

Huginn實作自動通過slack推送豆瓣高分電影
  • App Name:可以随意填寫
  • Development Slack Workspace:選擇你剛剛建立的私有的 workspace

Add features and functionality

中點選

Permissions

進入權限配置。

Scope

中添加如下權限:

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

添加完以上所有權限後,點選儲存,然後重新打開

Permissions

,點選下面按鈕安裝我們的這個 app 到 slack。

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

安裝完畢之後,再次進入 `Permissions`,拷貝 `OAuth Access Token`:

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

然後,我們就可以使用我們的 token 來通路 slack 的 open api 了,具體文檔在這裡:https://api.slack.com/web。

我們需要的發送消息到

#huginn-movie

channel 的接口文檔:

https://api.slack.com/methods/chat.postMessage

有了 api 文檔,有了 token,一切就好辦了。

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

由上述文檔,我們可以通過 post 請求,把我們要發送的電影資訊封裝到

attachments

參數中執行請求即可。

而且

attachments

參數可以參考文檔 https://api.slack.com/docs/message-attachments 來封裝資訊。

Slack 環境一切就緒,接下來,回到 Huginn。

建立 Agent 發送 Slack 消息

建立

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 \%}

    的方式來使用,這裡不做過多介紹了。

- 在消息中使用Slack 中的 `@` 某人的功能時,需要拿到對應使用者的 ID,可以的擷取方式可以通過在 slack 中選中名字然後 `Copy link` 的方式拿到使用者連結,使用者連接配接的最後就是 ID。

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

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

總結

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

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

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 還可以完成更多奇思妙想,限制你的隻有你的想象力。

繼續閱讀