关于云服务:Python库FeedparserAtom订阅源的妙用

42次阅读

共计 8229 个字符,预计需要花费 21 分钟才能阅读完成。

解放双手,每天主动把博客信息更新至 GitHub 主页

背景

最近在弄 GitHub 主页丑化的时候,搞了一些感觉比拟好玩乏味的货色,有趣味的敌人能够看看

这里贴个我的主页地址:https://github.com/JS-banana,有趣味的能够看看~

过后在编辑个人信息介绍的时候,产生了一个想法:能够在我的 GitHub 主页 同步我的博客更新状态吗?

当我更新博客的时候,我的 GitHub 主页 会主动把我博客最新更新的内容同步过来,很棒啊有没有~

这是过后产生的一个想法,起初就钻研了一下。最开始是想用 nodejs 写个爬虫搞一搞的,也没啥问题,不过这样搞会有很多缺点,我本人也只能搞个半成品,也不具备肯定的复用性,就排除了~

起初看到了 Pythonfeedparser库,感觉十分适合有没有啊。(feedparserpython 中最罕用的 RSS 程序库,应用它咱们可轻松地实现从任何 RSSAtom 订阅源失去题目、链接和文章的条目。)

也看了下成果,感觉很不错,这样咱们只有做两件事即可:

  • 实现 Atom 订阅源 (供feedparser 库应用)
  • 实现 README.md 文件的动静更新(获取到订阅信息后更新主页)

RSS、Atom 订阅源

RSS 订阅 咱们应该不生疏,咱们在浏览很多大佬博客的时候、出名网站和服务时会发现他们都提供有 RSS/Atom 订阅,那么什么是 RSS?什么是Atom 呢?

什么是 RSS?

  • Really Simple Syndication(真正繁难联结)
  • 使您有能力聚合(syndicate)网站的内容
  • 定义了非常简单的办法来共享和查看题目和内容
  • 文件可被自动更新
  • 容许为不同的网站进行视图的个性化
  • 应用 XML 编写

为什么应用 RSS?

RSS 被设计用来展现选定的数据。

如果没有 RSS,用户就不得不每日都来您的网站查看新的内容。对许多用户来说这样太费时了。通过 RSS feedRSS 通常被称为 News feedRSS feed),用户们能够应用 RSS 聚合器 来更快地查看您的网站更新(RSS 聚合器 是用来汇集并分类 RSS feed 的网站或软件)。

RSS 的将来倒退(Atom 的诞生)

因为 RSS 2.0 的版权问题,该协定前途未卜

因为 RSS 前途未卜,而且 RSS 规范 倒退存在诸多问题或有余,于是 ATOM 横空出世,能够先简略的了解为RSS 的替代品

FEED 是什么

FEED其实就是 RSS(或ATOM)和订阅用户之间的“中间商”,起到帮忙零售传递信息的作用。所以,FEED 的常见格局就是 RSSATOM,网络上说的 FEED 订阅,更确切的说法应该依然是RSSATOM订阅。

什么是订阅

订阅跟一般大家订阅报刊相似,不过简直所有网站的 RSS/ATOM 订阅都是收费的,也有一些“非主流”一族要免费订阅的,当然 FEED 订阅 只是网络上的信息传递,个别不波及实体材料传递,所以大家遇到喜爱的网站,并且也喜爱应用在线或离线浏览,尽可订阅,而且能够随时退订。

总结

RSSAtom 具备类似的基于 XML 的格局。它们的根本构造是雷同的,只是在节点的表达式上有一点区别。咱们只有理解 ATOM 是对 RSS2.0 的改良就能够了。

生成本人网站的 Atom 订阅源

Atom 订阅源 根本构造

理解 atom.xml 的根本格局和语法,看个最简略的 demo

<!-- 头信息 -->
<?xml version="1.0" encoding="utf-8"?>

<!-- 主体 -->
<feed xmlns="http://www.w3.org/2005/Atom">
  <!-- 根本信息 -->
  <title> 小帅の技术博客 </title>
  <link href="https://ssscode.com/atom.xml" rel="self"/>
  <link href="https://ssscode.com/"/>
  <updated>2021-08-28 16:25:56</updated>
  <id>https://ssscode.com/</id>
  <author>
    <name>JS-banana</name>
    <email>sss213018@163.com</email>
  </author>

  <!-- 内容区 -->
  <entry>
    <title>Webpack + React + TypeScript 构建一个标准化利用 </title>
    <link href="https://ssscode.com/pages/c3ea73/" />
    <id>https://ssscode.com/pages/c3ea73/</id>
    <published>2021-08-28 16:25:56</published>
    <update>2021-08-28 16:25:56</update>
    <content type="html"></content>
    <summary type="html"></summary>
    <category term="webpack" scheme="https://ssscode.com/categories/?category=JavaScript"/>
  </entry>

  <entry>
    ...
  </entry>

    ...

</feed>

根本信息 那一块齐全能够本人自定义配置好,而后,再去头去尾之后,能够发现咱们只有关怀 <entry> ... </entry> 标签内容即可,也就是每条博客文章的根本信息~

因而,咱们只有依照这个标准、格局、语法,齐全能够本人生成atom.xml,nice😎~

不想本人写的能够试试这个 feed

编写 atom.xml 文件生成函数

因为我的博客是以 vuepress 搭建的(webpack + vue2.x),这里就以 nodejs 为例

读取 所有 markdwon 文件 就不细说了,咱们拿到所有的列表数据,进行一下简略的解决,这里只填写一些咱们须要的数据即可,如果想浏览订阅源应用,也能够本人丰盛信息内容~

const DATA_FORMAT = 'YYYY-MM-DD HH:mm:ss';

// posts 是所有的博客文章信息
// xml 中的 & 符号须要替换为 &amp; 否则会有语法错误
function toXml(posts) {
  const feed = `<?xml version="1.0" encoding="utf-8"?>
  <feed xmlns="http://www.w3.org/2005/Atom">
    <title> 小帅の技术博客 </title>
    <link href="https://ssscode.com/atom.xml" rel="self"/>
    <link href="https://ssscode.com/"/>
    <updated>${dayjs().format(DATA_FORMAT)}</updated>
    <id>https://ssscode.com/</id>
    <author>
      <name>JS-banana</name>
      <email>sss213018@163.com</email>
    </author>
    ${posts
      .map(item => {
        return `
        <entry>
          <title>${item.title.replace(/(&)/g, '&amp;')}</title>
          <link href="https://ssscode.com${item.permalink}" />
          <id>https://ssscode.com${item.permalink}</id>
          <published>${item.date.slice(0, 10)}</published>
          <update>${item.date}</update>
        </entry>`;
      })
      .join('\n')}
  </feed>`;

  fs.writeFile(path.resolve(process.cwd(), './atom.xml'), feed, function(err) {if (err) return console.log(err);
    console.log('文件写入胜利!');
  });
}

node执行该文件,应该会在同级目录下生成一个 atom.xml 文件,能够看到

ok,atom 订阅源搞定~

feedparser 的简略用法

python feedparser,网上仿佛也有 node 版本的,这里就先不关怀了

把方才的 demo 内容片段复制到 atom.xml 文件,简略测试下用法,看下返回值格局,为了更清晰的看构造,我把 python 执行的后果解决了一下

atom.xml源文件

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title> 小帅の技术博客 </title>
  <link href="https://ssscode.com/atom.xml" rel="self"/>
  <link href="https://ssscode.com/"/>
  <updated>2021-08-28 16:25:56</updated>
  <id>https://ssscode.com/</id>
  <author>
    <name>JS-banana</name>
    <email>sss213018@163.com</email>
  </author>
  <entry>
    <title>Webpack + React + TypeScript 构建一个标准化利用 </title>
    <link href="https://ssscode.com/pages/c3ea73/" />
    <id>https://ssscode.com/pages/c3ea73/</id>
    <published>2021-08-28 16:25:56</published>
    <update>2021-08-28 16:25:56</update>
  </entry>
</feed>

main.py脚本

import feedparser

blog_feed_url = "./atom.xml"

feeds = feedparser.parse(blog_feed_url)

print (feeds)

输入后果大抵构造如下

{
  bozo: 1,
  // entries
  entries: [
    {
      title: "Webpack + React + TypeScript 构建一个标准化利用",
      title_detail: {
        type: "text/dplain",
        language: None,
        base: "",
        value: "Webpack + React + TypeScript 构建一个标准化利用",
      },
      links: [{href: "https://ssscode.com/pages/c3ea73/", rel: "alternate", type: "text/html"}],
      link: "https://ssscode.com/pages2/c3ea73/",
      id: "https://ssscode.com/pages/c3ea73/",
      guidislink: False,
      published: "2021-08-28 16:25:56",
      publoished_parsed: time.struct_time(), // 一个日期处理函数,参数比拟多,我删掉了,只看代码构造
      update: "2021-08-28 16:25:56",
    },
  ],
  // feed
  feed: {
    title: "小帅の技术博客",
    title_detail: {type: "text/plain", language: None, base: "", value:" 小帅の技术博客 "},
    links: [{ href: "https://ssscode.com/atom.xml", rel: "self", type: "application/atom+xml"},
      {href: "https://ssscode.com/", rel: "alternate", type: "text/html"},
    ],
    link: "https://ssscode.com/",
    updated: "2021-08-28 16:25:56",
    updated_parsed: time.struct_time(),
    id: "https://ssscode.com/",
    guidislink: False,
    authors: [{name: "JS-banana", email: "sss213018@163.com"}],
    author_detail: {name: "JS-banana", email: "sss213018@163.com"},
    author: "JS-banana (sss213018@163.com)",
  },
  headers: {},
  encoding: "utf-8",
  version: "atom10",
  bozo_exception: SAXParseException("XML or text declaration not at start of entity"),
  namespaces: {"":"http://www.w3.org/2005/Atom"},
}

能够看到,拿到所有的 entries 即可,编写个函数,取一些咱们须要的内容

def fetch_blog_entries():
    entries = feedparser.parse(blog_feed_url)["entries"]
    return [
        {"title": entry["title"],
            "url": entry["link"].split("#")[0],
            "published": entry["published"].split("T")[0],
        }
        for entry in entries
    ]

替换 markdown 文件指定区域内容

剩下最初一步就是:怎么把咱们 README.md 主页文件中指定的区域内容替换掉,而后在推送到 GitHub 实现更新即可

### Hello, 我是小帅! 👋

...
...
其余信息

<!-- start -->
  这里显示博客信息
<!-- end -->

如上,除了指定的区域须要更新,其余中央是不须要变动的

这时就能够通过 Python 能够读取正文,而后应用正则解决替换,即可

咱们在 README.md 中标记正文

<!-- blog starts -->
  ...
<!-- blog ends -->

代码:

def replace_chunk(content, marker, chunk, inline=False):
    r = re.compile(r"<!\-\- {} starts \-\->.*<!\-\- {} ends \-\->".format(marker, marker),
        re.DOTALL,
    )
    if not inline:
        chunk = "\n{}\n".format(chunk)
    chunk = "<!-- {} starts -->{}<!-- {} ends -->".format(marker, chunk, marker)
    return r.sub(chunk, content)

最初,再联合接口申请、文件读取等,残缺代码如下

import feedparser
import json
import pathlib
import re
import os
import datetime

blog_feed_url = "https://ssscode.com/atom.xml"

root = pathlib.Path(__file__).parent.resolve()

def replace_chunk(content, marker, chunk, inline=False):
    r = re.compile(r"<!\-\- {} starts \-\->.*<!\-\- {} ends \-\->".format(marker, marker),
        re.DOTALL,
    )
    if not inline:
        chunk = "\n{}\n".format(chunk)
    chunk = "<!-- {} starts -->{}<!-- {} ends -->".format(marker, chunk, marker)
    return r.sub(chunk, content)

def fetch_blog_entries():
    entries = feedparser.parse(blog_feed_url)["entries"]
    return [
        {"title": entry["title"],
            "url": entry["link"].split("#")[0],
            "published": entry["published"].split("T")[0],
        }
        for entry in entries
    ]

if __name__ == "__main__":
    readme = root / "README.md"
    readme_contents = readme.open(encoding='UTF-8').read()

    entries = fetch_blog_entries()[:5]
    entries_md = "\n".join(["* <a href='{url}'target='_blank'>{title}</a> - {published}".format(**entry) for entry in entries]
    )
    rewritten = replace_chunk(readme_contents, "blog", entries_md)

    readme.open("w", encoding='UTF-8').write(rewritten)

我对 Python 也不熟,不过跟着前人的脚步,模拟着应用也能达到预期成果,还行~

最近略微接触了一些 Python 相干的脚本库,发现还挺有意思的,感觉还是很有必要学习学习,日常应用中还是很有帮忙的,毕竟当初 Python 也是很炽热的嘛,就算当工具用,感觉也很强力~

配置 GitHub Action 定时工作

实现性能的脚本曾经搞定了,当初就是心愿在咱们实现博客更新后,脚本能够主动执行

这里咱们间接应用 GitHub Action 的定时工作即可

我的项目里增加文件 .github/workflows/ci.yml

name: Build README

on:
  workflow_dispatch:
  schedule:
    - cron: "30 0 * * *" # 每天 0:30 时运行,北京工夫须要 + 8

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo # 获取代码分支
        uses: actions/checkout@v2

      - name: Set up Python # python 环境
        uses: actions/setup-python@v2
        with:
          python-version: 3.8

      - uses: actions/cache@v2 # 依赖缓存
        name: Configure pip caching
        with:
          path: ~/.cache/pip
          key: ${{runner.os}}-pip-${{hashFiles('**/requirements.txt') }}
          restore-keys: |
            ${{runner.os}}-pip-

      - name: Install Python dependencies # 装置依赖
        run: |
          python -m pip install -r requirements.txt

      - name: Update README # 执行脚本
        run: |-
          python build_readme.py
          cat README.md

      - name: Commit and push if changed # Git 提交
        run: |-
          git diff
          git config --global user.email "sss213018@163.com"
          git config --global user.name "JS-banana"
          git pull
          git add -A
          git commit -m "Updated README content" || exit 0
          git push

功败垂成~

看下成果:


这样脚本每天都会跑一次,同步博客相干信息~

结语

之前只晓得 RSS 订阅,齐全不分明还有这么些的细节,这次也算梳理搞清楚了一些,也尝试本人玩了一下,还是挺不错的~

感觉多会一门语言还是很棒的啊,有时会给你齐全不一样的思路,或者就会有更加好的计划~

扶我起来,我还能学~ 笑~

参考

  • 订阅根底:RSS、ATOM、FEED、聚合、供稿、合烧与订阅
  • RSS,ATOM,FEED 是什么有什么区别
  • feedparser
  • jasonkayzk

正文完
 0