Files
Colinx-Blog/content/posts/Huginn指南:为任意网站制作RSS.md
2022-05-08 22:08:24 +08:00

308 lines
13 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Huginn指南为任意网站制作RSS
date: 2022-05-08
lastmod: 2022-05-08
description: 又一篇介绍使用Huginn制作RSS的教程🕶
categories:
- 技术
- 指南
tags:
- Huginn
- RSS
- 爬虫
- CSS
- 技术
---
<!-- # Huginn指南为任意网站制作RSS -->
Huginn使用多个不同功能的Agent组合搭配来实现一系列功能一个Agent可以执行特定的操作并产生一个Event你可以指定他产生的Event由哪个Agent接收处理。
比如我们需要使用WebSite Agent来爬取某个网站上的列表内容爬取后列表的每一项会生成一个Event我们可以指定一个Output Agent来接收这些Event并将其转换为RSS的格式进行输出复制Output Agent返回给你的URL就可以进行订阅啦Huginn爬取网站生成RSS的基本原理就是这样。
## Huginn爬取普通网页制作RSS
右键网页打开开发者工具屏幕会分出一部分空间显示开发者工具窗口点击左上角的按钮再把鼠标移动到页面上可以选择页面的某一个元素比如这里我们要爬取推荐文章列表推荐文章列表的每一项都有同样的样式我们可以使用CSS选择器来指定爬取该项
![image-20220508160659378](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207138.png)
该元素可以用CSS选择器`.meiwen`选择到。
**Tips:** class=xxx则使用`.xxx`,如果为id=xxx则使用`#xxx`
该标签是a标签我们可以继续限定一下条件使用`a.meiwen`
如果要限制必须是h2 标签下的带有meiwen的class属性的a标签可以使用`h2 a.meiwen`
如果要再加一个限制是列表元素li下面的h2标签里带有meiwen的class属性的a标签可以使用`li h2 a.meiwen`
添加其他附加限定条件以此类推
你也可以在右侧Style面板里点击加号添加一个自定义样式输入CSS选择器浏览器会自动高亮符合条件的网页元素你可以使用这个功能来检验你写的CSS选择器是否正确以及是不是提取的你想要的内容
![image-20220508163359806](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207716.png)
接下来在Huginn里创建一个Scenario然后点击新建一个Agent类型选择WebSite Agent填写一个名称并在最下面的配置处指定你要爬取的网页元素
> ###### **Website Agent**
>
> The Website Agent scrapes a website, XML document, or JSON feed and creates Events based on the results.
```json
url: {
css: li h2 a.meiwen
value: @href
}
```
以爬取url为例css表示使用css选择器选择网页元素value表示从哪里获取对应的值对于刚才我们选取的一个元素来说
```html
<a class="meiwen" href="/article/58030.html" title="缘起则聚,缘灭则散">缘起则聚,缘灭则散</a>
```
href属性里面的内容是他的链接title属性里面的内容则是他的标题。接下来在value中填写`@href`指定从href属性提取内容就可以取到链接地址了
> **Pro Tips:**
>
> 如果是纯文本节点或是想提取标签内嵌的文本value里可以填写`string(.)`
>
> 如果想删除多余的空格,可以使用`normalize-space(.)`
> **Pro Tips 2:**
>
> 字符串处理函数和标签属性值变量可以一起使用,如`normalize-space(@title)`可以获取该标签的title属性值并删除多余的空白字符
![image-20220508163714342](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207717.png)
接下来点击Dry Run按钮进行测试不出意外我们会得到一个json的输出里面包括我们爬取到的每一项他的url和title。
如果没有成功你可能需要删掉上面没有使用到的hovertext节点因为该项指定的内容在我们刚才的网页中并不存在。
![image-20220508164259295](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207718.png)
点击Save保存我们开始下一步输出RSS。在刚才的Scenario中再新建一个Agent选择Data Output Agent并设置他的名字和Source Agent
> ###### Data output Agent
>
> The Data Output Agent outputs received events as either RSS or JSON. Use it to output a public or private stream of Huginn data.
![image-20220508164941647](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207719.png)
**Propagate immediately**是指即时处理来自Source Agent的Event启用他方便我们调试但会略微增加服务器负载你可以自行决定是否使用。
![image-20220508165345792](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207720.png)
在secret字段中为你的这个RSS标注一个英文的名字修改title字段标注你的RSS的名字。item字段是每条文章会有的属性一般来说主要就title和link分别设置为上文我们提取的值的变量名。这里添加一个guid字段这是一篇文章的唯一标识符避免RSS阅读器读到的文章标题不同但是内容相同常见于某篇文章的标题被修改这会导致RSS阅读器内出现多篇重复文章。
此外建议增加一个link字段值设置为与爬取的网站的主域名一致避免网站内使用相对链接开头的资源无法正常加载。
![image-20220508170038722](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207721.png)
![image-20220508170115192](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207722.png)
点击Save保存回到Scenario界面第一次需要手动点击运行一下刚才的Website Agent。稍等片刻后台会进行爬取右上角会显示产生了多少个Event。再点开刚才设置的Data Output Agent查看详情vola右侧就会显示生成的RSS链接了复制以xml结尾的链接到RSS阅读器中就可以订阅啦🎉
![image-20220508170212112](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207723.png)
如果你的配置正确但是WebSite Agent又没有输出任何内容的话那么你要爬取的站点的内容应该是通过JavaScript在你带开浏览器时动态生成的。
> **Pro Tips:**
>
> 你可以右键网页在开发人员菜单中点击显示源代码然后用Ctrl+F搜索你在页面上见到的想爬取的某一条内容如果有说明该网站的页面时静态的否则说明你要爬取的内容并不是静态的。
要爬取这样的站点我们需要模拟浏览器操作执行页面上的JavaScript脚本待数据生成完毕后再使用上面的方法去爬取内容。要模拟浏览器操作这里介绍两个方案一个是利用browserless的无头浏览器另一个是使用在线PhatomJS服务。
## 使用Huginn+Browserless为动态网页生成RSS
部署了[RSS Man X](https://github.com/Colin-XKL/RSSmanX)的完整版的话默认已经包含huginn和browserless服务无需重复安装。且huginn可以通过`service.browserless:3000`访问到browserless服务。
总体步骤与上文一样不过因为被抓取的内容是通过JavaScript动态生成的所以直接使用WebSite Agent是抓取不到东西的我们需要先使用Post Agent向browserless服务发送指令让他帮我们模拟浏览器操作再将最终完整的网页返回给我们再使用WebSite Agent进行后续处理。
> ###### **Post Agent**
>
> A Post Agent receives events from other agents (or runs periodically), merges those events with the Liquid-interpolated contents of payload, and sends the results as POST (or GET) requests to a specified url. To skip merging in the incoming event, but still send the interpolated payload, set no_merge to true.
接下来以[这个网站](https://pccz.court.gov.cn/pcajxxw/pcgg/ggdh?lx=0)为例介绍一下这个列表页的内容是由JavaScript动态生成的
![image-20220508205159783](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207724.png)
按照以下内容设置你的Post Agent
```json
{
"post_url": "http://service.browserless:3000/content",
"expected_receive_period_in_days": "1",
"content_type": "json",
"method": "post",
"payload": {
"url": "https://pccz.court.gov.cn/pcajxxw/pcgg/ggdh?lx=0"
},
"headers": {
"Cache-Control": "no-cache",
"Content-Type": "application/json"
},
"emit_events": "true",
"no_merge": "false",
"output_mode": "clean"
}
```
其中`post_url`为你的browserless实例地址如果你使用了RSS MAN X里的Huginn可以直接向上面一样填写。`payload`中的`url`字段填写你需要的网页地址。注意emit_events要设置为`true`这样才方便我们后续使用WebSite Agent操作。
点击Dry Run如果能返回一个带有`body`字段且里面有文本内容说明调用成功
![image-20220508195731732](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207725.png)
接下来点击Save保存再新建一个Website AgentSource设置为刚才的Post Agent。
```json
{
"expected_update_period_in_days": "2",
"data_from_event": "{{ body }}",
"type": "html",
"mode": "on_change",
"extract": {
"url": {
"css": "ul#wslb li a",
"value": "normalize-space(@href)"
},
"title": {
"css": "ul#wslb li a",
"value": "@title"
},
"author": {
"css": "ul#wslb li div.center",
"value": "normalize-space(.)"
}
}
}
```
注意修改`data_from_event`的值其他地方与爬取普通网站一样。再新建并配置一下Output AgentRSS的链接就出来了
![image-20220508205856407](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207726.png)
## 使用PhantomJS为动态页面生成RSS
首先需要到[Phantom Js Cloud的官网](https://phantomjscloud.com)注册一个账户每个账户官方提供了一定的免费额度每天大概可以爬取500个页面对于做RSS来说妥妥的够用了。注册之后可以得到一个API KEY这个待会会使用到。
接下来新建一个Phantom JS Cloud Agent填写基本的名称、API KEY和目标URLrender type选择html。底下User Agent建议改用真实的浏览器UA减少触发反爬虫的可能性。如果不知道自己的UA可以到这个网站查看当前浏览器的UA复制字符串即可。如果懒得查也可以用这个
```
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.81 Safari/537.36
```
> ###### Phantom JS Cloud Agent
>
> This Agent generates PhantomJs Cloud URLs that can be used to render JavaScript-heavy webpages for content extraction.
>
> URLs generated by this Agent are formulated in accordance with the PhantomJs Cloud API. The generated URLs can then be supplied to a Website Agent to fetch and parse the content. Sign up to get an api key, and add it in Huginn credentials.
点击Dry Run后会返回一条url返回格式类似这样
```json
[
{
"url": "https://phantomjscloud.com/api/browser/xxxxxxx"
}
]
```
之后创建一个WebSite AgentSource Agent设置为上面的Phantom JS Cloud Agent然后稍微改一下url的部分
```
{
"expected_update_period_in_days": "2",
"url_from_event": "{{url}}",
"type": "html",
"mode": "on_change",
"extract": {...}
}
```
注意将原来的`url:??`的部分更改为`"url_from_event": "{{url}}"`,这样就指定使用Phantom JS Cloud为我们获取的完整网页接下来的操作就大同小异了。配置好要爬取的字段和规则后点击Dry Run就可以看到结果
![image-20220508215636780](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207727.png)
这里是刚才的Agent产生的一个Event。这里除了url和title我还提取了date字段。在Output Agent中可以配置为每一条文章输出发布时间的信息。对于这种规整的日期字母串可以使用`{{ date | date: %F }}`进行解析并格式化输出为rss规范的时间格式如果没有配置的话每篇文章的发布时间显示的都是一样的为Huginn爬虫更新的时间。
```json
"item": {
"title": "{{title}}",
"link": "{{url}}",
"pubDate": "{{ date | date: %F }}"
},
```
> **Pro Tips:**
>
> Huginn解析日期
>
> 注意`{{ date | date: %F }}`里面,第一个`date`是上面的Website Agent产生的Event里面的变量名第二个`date`是Huginn使用的Liquid模版引擎语法里内置的一个filter可以理解为有特定功能的函数
![image-20220508220438696](https://blog-1301127393.cos.ap-shanghai.myqcloud.com/BlogImgs/202205082207637.png)
## FAQ
* 为什么启动docker容器后访问Huginn显示网络错误
Huignn冷启动较慢需要等待三五分钟。如果还是不行检查端口映射和防火墙设置
* 为什么抓取到的包含相对路径的结果网页上可以点击访问但是生成的RSS不能正常使用
检查link的设置有些网站只是域名有些网站有子目录具体查看该网页源码中head节点里base url的设置
* 如何对爬取到的某一项的字符串做更高级更复杂的处理?
* 可以参考[Hugnn官方对Liquid语法的文档](https://github.com/huginn/huginn/wiki/Formatting-Events-using-Liquid)以及[Shopify官方关于Liquid模板的语法文档](https://shopify.dev/api/liquid)
## 扩展阅读
* [Huginn官方文档](https://github.com/huginn/huginn/wiki/)
* [Liquid模板引擎语法官方文档](https://shopify.dev/api/liquid/filters/)
* RSS Man X部署指南