博客搬迁至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 还可以完成更多奇思妙想,限制你的只有你的想象力。

Huginn实现自动通过slack推送豆瓣高分电影的更多相关文章

  1. Github自动打包并推送Nuget版本

    如何将自己的类库,自动打包并自动发布到Nuget? 1. 项目csproject属性修改 新建一个项目GitToNugetPackageTest 不用添加任何类,我们修改csproject属性. 替换 ...

  2. 利用python对微信自动进行消息推送

    from wxpy import * #该库主要是用来模拟与对接微信操作的 import requests from datetime import datetime import time impo ...

  3. 简易数据分析 04 | Web Scraper 初尝--抓取豆瓣高分电影

    这是简易数据分析系列的第 4 篇文章. 今天我们开始数据抓取的第一课,完成我们的第一个爬虫.因为是刚刚开始,操作我会讲的非常详细,可能会有些啰嗦,希望各位不要嫌弃啊:) 有人之前可能学过一些爬虫知识, ...

  4. IOS之推送通知(本地推送和远程推送)

    推送通知和NSNotification是有区别的: NSNotification:是看不到的 推送通知:是可以看到的 IOS中提供了两种推送通知 本地推送通知:(Local Notification) ...

  5. iOS推送通知

    推送通知 此通知非彼通知. NSNotification是抽象的,看不见的,但是可以监听,属于观察者模式的一种设计模式. 推送通知是可见的,能用肉眼看见的,是真正的和用户打交道的通知. 推送通知分为两 ...

  6. CCNET+ProGet+Windows Batch搭建全自动的内部包打包和推送及管理平台

    所要用的工具: 1.CCNET(用于检测SVN有改动提交时自动构建,并运行nuget的自动打包和推送批处理) 2.ProGet(目前见到最好用的nuget内部包管理平台) 3.Windows Batc ...

  7. web推送

    WEB消息推送框架 web-msg-sender是一款web长连接推送框架,采用PHPSocket.IO开发,基于WebSocket长连接通讯,如果浏览器不支持WebSocket则自动转用comet推 ...

  8. 【52ABP实战教程】0.3-- 从github推送代码回vsts实现双向同步

    需求 在之前的文章中"[DevOps]如何用VSTS持续集成到Github仓库" 我们有讲述如何将vsts中的代码编译推送到github中,这一篇我们来完善,如果有人给你开源项目推 ...

  9. 友盟推送SDK集成测试、常见问题以及注意事项总结

    最近为了解决公司APP在一些手机出现的推送问题重新集成了最新版的友盟推送SDK,花费了几天时间终于把集成和测试工作完成,最终在华为,Nexus,三星,小米,HTC,魅族等10多部手机上测试并达到了预想 ...

随机推荐

  1. [BZOJ1707] [Usaco2007 Nov] tanning分配防晒霜 (贪心)

    Description 奶牛们计划着去海滩上享受日光浴.为了避免皮肤被阳光灼伤,所有C(1 <= C <= 2500)头奶牛必须在出门之前在身上抹防晒霜.第i头奶牛适合的最小和最 大的SP ...

  2. C# Redis实战(七)

    七.修改数据 在上一篇 C# Redis实战(六)中介绍了如何查询Redis中数据,本篇将介绍如何修改Redis中相关数据.大家都知道Redis是key-value型存储系统,所以应该可以修改key, ...

  3. session、cookie与“记住我的登录状态”的功能的实现

    Cookie的机制 Cookie是浏览器(User Agent)访问一些网站后,这些网站存放在客户端的一组数据,用于使网站等跟踪用户,实现用户自定义功能. Cookie的Domain和Path属性标识 ...

  4. IO查找文件复制到指定路径

    public class IOTest { // 存储获取的文件绝对路径 private static List<String> list = new ArrayList<Strin ...

  5. ELK重难点总结和整体优化配置

    本文收录在Linux运维企业架构实战系列 做了几周的测试,踩了无数的坑,总结一下,全是干货,给大家分享~ 一.elk 实用知识点总结 1.编码转换问题(主要就是中文乱码) (1)input 中的cod ...

  6. MySQL解决方案

        主从复制与主主复制怎么自动切换:使用Keepalived     日常如何导出数据:mysqldump.xtrabackup 主库宕机解决方案(一主多从) 登陆从库>show proce ...

  7. jQuery 3.0最终版发布,十大新特性眼前一亮

    jQuery 3.0在日前发布了最终的全新版本.从2014年10月,jQuery团队对这个主要大版本进行维护开始,web开发者社区便一直在期待着这一刻的到来,终于在2016年6月他们迎来了这一个最终板 ...

  8. 微信扫一扫JSSDK 扫一扫报错 invalid signature 问题

    交代一下业务场景 在在四个页面都需要用到扫一扫去扫二维码.然而在图三-我的订单 下单中这个页面扫一扫不起效,当时就郁闷了为啥其他页面有用,这里却没用,开始调试吧. 报错信息是签名验证不成功. 自己去打 ...

  9. 笔记:Jersey REST 传输格式-JSON

    JSON 类型已经成为Ajax技术中数据传输的实际标准,Jersey 提供了多种处理JSON数据的包和解析方式,下表展示了JSON包和解析方式: 解析方式\JSON支持包 MOXy JSON-P Ja ...

  10. Java DualPivotQuickSort 双轴快速排序 源码 笔记

    DualPivotQuicksort source code 这个算法是Arrays.java中给基本类型的数据排序使用的具体实现.它针对每种基本类型都做了实现,实现的方式有稍微的差异,但是思路都是相 ...