结合 huginn 实现自动推送猫眼电影上线提醒

安装 huginn

网上关于如何安装 huginn 的文章太多了, 这里不做介绍, 我自己使用的是 huginn 的 docker 安装方式, docker-compose.yml 如下

version: '2'
services:
  mysql:
    image: mysql:5.7
    volumes:
    - /data/docker/huginn/mysql:/var/lib/mysql
    ports:
    - 3310:3306
    environment:
      MYSQL_ROOT_PASSWORD: <mysql_root_password>
      MYSQL_DATABASE: huginn
      MYSQL_USER: huginn
      MYSQL_PASSWORD: <mysql_password>
    restart: always
  huginn:
    image: huginn/huginn
    restart: always
    environment:
      HUGINN_DATABASE_NAME: huginn
      HUGINN_DATABASE_USERNAME: huginn
      HUGINN_DATABASE_PASSWORD: <mysql_password>
      INTENTIONALLY_SLEEP: 10
      PORT: 3000
      MYSQL_PORT_3306_TCP_ADDR: mysql
      MYSQL_PORT_3306_TCP_PORT: 3306
    ports:
    - 3000:3000
    links:
    - mysql

启动后, 配置 nginx 使用 ssl 时发现, 每次登录总是跳转到首页, 变为未登录状态, 解决方式是参考 nginx-ssl, 添加必要的 proxy_set_header:

proxy_set_header  Host                $http_host;
proxy_set_header  X-Real-IP           $remote_addr;
proxy_set_header  X-Forwarded-Ssl     on;
proxy_set_header  X-Forwarded-For     $proxy_add_x_forwarded_for;
proxy_set_header  X-Forwarded-Proto   $scheme;
proxy_set_header  X-Frame-Options     SAMEORIGIN;

安装完成后, 通过默认用户名密码 admin / password 即可登录.

配置 huginn

huginn 中的几个关键术语解释一下:

  • Agent

代理, 简单理解就是一个操作, 比如发送消息, 解析网页等等.

  • Scenarios

场景, 场景由多个 agent 组成, 通过 agent 构建一个有向图.

  • Event

事件, 即 agent 的执行的中间结果, 前一步的 agent 将处理后的信息以 event 的形式抛出去, 由下一步的 agent 去接着处理. 这里就涉及到 agent 中的两个概念, sources 和 receivers, 分别定义的是当前 agent 的前置和后置 agent.

以当前我们的任务为例. 我们要收集信息的页面为 https://maoyan.com/films:

 

可以看到, 我们能从这个页面获取到最新电影的 中文名称/缩略图/分数, 我们第一步就是要解析这个页面, 获取最基本的信息, 第二步去每部电影的详情页去获取电影的详细信息(介绍, 时间等等), 第三步将获取的信息格式化为 slack message 进行发送.

这里, 我们先创建一个名为 movie 的 Scenarios.

 

step 1

通过查看页面元素, 我们不难去定位每一个字段

 

我们新建一个 agent, 类型为 Website Agent, 这个 agent 用于处理页面元素.

 

{
  "expected_update_period_in_days": "2",
  "url": "https://maoyan.com/films?showType=1&sortId=1",
  "type": "html",
  "mode": "on_change",
  "extract": {
    "url": {
      "xpath": "//div[@class="movie-item"]/a/@href",
      "value": "."
    },
    "poster": {
      "xpath": "//div[@class="movie-poster"]/img[1]/@src",
      "value": "."
    },
    "title": {
      "xpath": "//div[@class="channel-detail movie-item-title"]/@title",
      "value": "."
    },
    "score": {
      "xpath": "//div[@class="channel-detail channel-detail-orange"]",
      "value": "string(.)"
    }
  }
}

通过点击 Dry Run 可以进行测试. 我们看到可以拉取到的数据如下:

[
  {
    "url": "/films/1217651",
    "poster": "//ms0.meituan.net/mywww/image/loading_2.e3d934bf.png",
    "title": "小公主艾薇拉与神秘王国",
    "score": "4.8"
  }
]

这个数组中的每个 {} 都将是一个 event 向外发布.

step 2

这里我们再创建一个 agent, url 使用上一步给出的 url 进行拼接.

另外还有一个细节是, 这里的 mode 为 merge, 意思是, 将上一步的 event 带过了的字段和当前 event 进行合并, 往下一步传.

 

{
  "expected_update_period_in_days": "2",
  "url": "https://maoyan.com{{url}}",
  "type": "html",
  "mode": "merge",
  "extract": {
    "titleCn": {
      "css": ".movie-brief-container .name",
      "value": "normalize-space(.)"
    },
    "titleEn": {
      "css": ".movie-brief-container .ename",
      "value": "normalize-space(.)"
    },
    "cover": {
      "css": ".avatar-shadow .avatar",
      "value": "@src"
    },
    "ellipsis": {
      "css": ".movie-brief-container ul",
      "value": "normalize-space(.)"
    },
    "description": {
      "css": ".tab-desc .dra",
      "value": "normalize-space(.)"
    }
  }
}

点击 Dry Run, 在 Event to Send 中填入一个上一步的 event. 如:

{
  "url": "/films/1213193",
  "poster": "//ms0.meituan.net/mywww/image/loading_2.e3d934bf.png",
  "title": "猫与桃花源",
  "score": "8.5"
}

可以看到我们获取到了更详细的影片信息

[
  {
    "url": "/films/1213193",
    "poster": "//ms0.meituan.net/mywww/image/loading_2.e3d934bf.png",
    "title": "猫与桃花源",
    "score": "8.5",
    "titleCn": "猫与桃花源",
    "titleEn": "Cats and Peachtopia",
    "cover": "http://p0.meituan.net/movie/aadd3c954b697c16455e56f35f12c4be336103.jpg@464w_644h_1e_1c",
    "ellipsis": "动画,冒险,家庭 中国大陆 / 105分钟 2018-04-05大陆上映",
    "description": "一只常常在窗台发呆的家猫毯子(李宇峰 配音),一直以来和儿子斗篷(杨砚铎 配音)安逸地生活在城市的一座高楼公寓中。有一天毯子不得不和斗篷分别踏上冒险旅程,去寻找传说中的猫的桃花源。与此同时,毯子必须面对心中一段不愿提起的往事,或许这也是毯子害怕外面的世界,不愿意离开家的重要原因......"
  }
]

step 3

先要为 slack 创建一个 app, 用于接受消息. Create New App.

为这个 app 创建一个 incoming_webhook. 格式应该是

https://hooks.slack.com/services/XXXXXXXXXX/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxxxx

 

关于发送 slack, 虽然 huginn 提供了一个 slack agent, 但使用起来仅仅发送最简单的 message 可以使用, 无法构建复杂的格式. 我们改用 post agent.

 

{
  "post_url": "<slack incoming webhook>",
  "expected_receive_period_in_days": "1",
  "content_type": "json",
  "method": "post",
  "payload": {
    "username": "猫眼电影",
    "icon_url": "https://img3.doubanio.com/pics/douban-icons/favicon_48x48.png",
    "attachments": [
      {
        "fallback": "{{ titleCn }} is online NOW!!",
        "mrkdwn_in": [
          "text",
          "pretext"
        ],
        "color": "#36a64f",
        "pretext": "Hi~ Dan. There is a new movie.",
        "title": "{{titleCn}}",
        "title_link": "https://maoyan.com{{url}}",
        "text": "{{ ellipsis }}",
        "fields": [
          {
            "title": "Alias",
            "value": "{{ titleEn }} ",
            "short": true
          },
          {
            "title": "Score",
            "value": "{{ score }}",
            "short": true
          },
          {
            "title": "Description",
            "value": "{{ description }}",
            "short": false
          }
        ],
        "image_url": "{{ cover }}",
        "thumb_url": "{{ poster }}",
        "footer": "猫眼电影",
        "footer_icon": "http://www.xz7.com/dir/UploadPic/2014-11/2014111011382236405.jpg"
      }
    ]
  },
  "headers": {
    "Content-Type": "application/json"
  },
  "emit_events": "false",
  "no_merge": "false",
  "output_mode": "clean"
}

显示效果如下:

 

关于 slack 的消息格式, 不是本文的重点, 具体可以参考这里 An introduction to messages.

原文链接:https://www.shanhh.com/huginn_for_maoyan_movie/

发表回复