解放双手,每天主动把博客信息更新至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 feedparserblog_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 feedparserimport jsonimport pathlibimport reimport osimport datetimeblog_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 READMEon:  workflow_dispatch:  schedule:    - cron: "30 0 * * *" # 每天 0:30 时运行,北京工夫须要 + 8jobs:  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