💭

ZennのRSSから記事のリストを生成する

2021/06/02に公開

毎月、振り返りの記事を書いていて、その月に書いた記事のリストを載せています。Zennの記事一覧は非公式のAPIを叩いてリストを生成していました。
https://zenn.dev/yorifuji/articles/python2-get-zenn-articles

少し前からAPIが利用できなくなったので、代わりの方法としてRSSをパースして一覧を生成することにしました。下記は実際の出力です。

% make
2021-03-21T19:59:47+09:00,https://zenn.dev/yorifuji/articles/mediastream-channel,MediaStreamから音声ストリームを取り出して左右に振り分けたストリームを作成する
2021-03-22T23:57:29+09:00,https://zenn.dev/yorifuji/articles/swiftui-download-image-async,SwiftUIでダウンロードした画像を表示する
2021-03-24T18:48:58+09:00,https://zenn.dev/yorifuji/articles/ios-app-tracking-transparency,iOS14のAppTrackingTransparencyに対応するためにやったこと
2021-03-25T20:03:36+09:00,https://zenn.dev/yorifuji/articles/android-keystore-fingerprint,Keystoreとバイナリのフィンガープリントを確認する(Android)
2021-03-27T16:46:41+09:00,https://zenn.dev/yorifuji/articles/self-release-note-202102,2021年2月の振り返り
2021-03-28T19:39:08+09:00,https://zenn.dev/yorifuji/articles/swiftui-navigationviewstyle-stack,SwiftUIのNavigationViewでiPhoneとiPadの動作を合わせる
2021-04-01T20:45:12+09:00,https://zenn.dev/yorifuji/articles/swiftui-admob-ipad,SwiftUIのiPadアプリでAdMobを使うときに必要な設定
2021-04-02T19:28:56+09:00,https://zenn.dev/yorifuji/articles/swiftui-zucks,SwiftUIでZucksを利用してバナー広告を表示する
2021-04-03T12:40:16+09:00,https://zenn.dev/yorifuji/articles/zenn-makefile,Zenn CLIで執筆するためのMakefileの手始め
2021-04-04T12:27:35+09:00,https://zenn.dev/yorifuji/articles/python2-get-zenn-articles,自分が書いたZennの記事一覧をPython2で取得する
2021-04-06T22:01:56+09:00,https://zenn.dev/yorifuji/articles/self-release-note-202103,2021年3月の振り返り
2021-04-07T19:56:17+09:00,https://zenn.dev/yorifuji/articles/swiftui-progressview-design,SwiftUIのProgressViewのデザインを調整する
2021-04-09T00:50:49+09:00,https://zenn.dev/yorifuji/articles/swiftui-ipad-navigationview,SwiftUIのNavigationViewを使ってiPadでSplitViewを利用する場合の基本的
  コード
2021-04-11T22:12:30+09:00,https://zenn.dev/yorifuji/articles/ios-action-extension,iOSのAction Extensionについて
2021-04-18T17:02:32+09:00,https://zenn.dev/yorifuji/articles/xcode-code-coverage,XcodeのTestでCodeCoverageを有効にする
2021-05-03T16:12:29+09:00,https://zenn.dev/yorifuji/articles/ios-remove-storyboard,iOSでプロジェクトからMain.storyboardを削除してRootのViewControllerをコード
  生成する
2021-05-07T16:34:56+09:00,https://zenn.dev/yorifuji/articles/swiftui-multiple-sheet,iOS14.5でSwiftUIがmultiple sheetに対応したので確認した
2021-05-08T19:28:30+09:00,https://zenn.dev/yorifuji/articles/swiftui-action-extension,iOSのAction Extensionを使って共有から画像を受け取れるようにする
2021-05-23T15:52:02+09:00,https://zenn.dev/yorifuji/articles/swiftui-combine-corelocation,SwiftUIとCombineでCore Locationを扱う
2021-05-31T22:46:25+09:00,https://zenn.dev/yorifuji/articles/ios-swift-combine,iOSのCombine学習メモ

ZennのRSSフィードには直近の20記事が含まれているようでした。自分の用途では問題なさそうです。

ソースコードはこちらです
https://github.com/yorifuji/zenn-rss-parser

環境

  • Python 3

feedparser

ZennのRSSのURLは https://zenn.dev/yorifuji/feed のような形式です。

RSSのパーサはfeeedparserを使いました。
https://pypi.org/project/feedparser/

簡単な使い方

import feedparser
rss = feedparser.parse('https://zenn.dev/yorifuji/feed')
rss.entries[0]
>{'title': 'iOSのCombine学習メモ', 'title_detail': {'type': 'text/plain', 'language': None, 'base': 'https://zenn.dev/yorifuji/feed',...

feedparserでパースしたデータの一つをdumpすると以下のような形式になっていました。記事リストを生成するだけであればlinkを抽出すると良さそうです。

{
  "title": "iOSのCombine学習メモ",
  "title_detail": {
    "type": "text/plain",
    "language": null,
    "base": "https://zenn.dev/yorifuji/feed",
    "value": "iOSのCombine学習メモ"
  },
  "summary": "これはなに\niOSのCombineについてオフィシャルのドキュメントを読み解いたときのメモです。\nhttps://developer.apple.com/documentation/combine\n\n Combine\n\nThe Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. Combine declares publishers to expo...",
  "summary_detail": {
    "type": "text/html",
    "language": null,
    "base": "https://zenn.dev/yorifuji/feed",
    "value": "これはなに\niOSのCombineについてオフィシャルのドキュメントを読み解いたときのメモです。\nhttps://developer.apple.com/documentation/combine\n\n Combine\n\nThe Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events. Combine declares publishers to expo..."
  },
  "links": [
    {
      "rel": "alternate",
      "type": "text/html",
      "href": "https://zenn.dev/yorifuji/articles/ios-swift-combine"
    },
    {
      "length": "0",
      "type": "image/png",
      "href": "https://res.cloudinary.com/dlhzyuewr/image/upload/s--773rPwPh--/co_rgb:222%2Cg_south_west%2Cl_text:notosansjp-medium.otf_37_bold:yorifuji%2Cx_203%2Cy_98/c_fit%2Cco_rgb:222%2Cg_north_west%2Cl_text:notosansjp-medium.otf_80_bold:iOS%25E3%2581%25AECombine%25E5%25AD%25A6%25E7%25BF%2592%25E3%2583%25A1%25E3%2583%25A2%2Cw_1010%2Cx_90%2Cy_100/g_south_west%2Ch_90%2Cl_fetch:aHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL3plbm4tdXNlci11cGxvYWQvYXZhdGFyL2ljb25fM2ZlN2Y4N2M1NC5qcGVn%2Cr_max%2Cw_90%2Cx_87%2Cy_72/v1609308637/og/new_txlqub.png",
      "rel": "enclosure"
    }
  ],
  "link": "https://zenn.dev/yorifuji/articles/ios-swift-combine",
  "id": "https://zenn.dev/yorifuji/articles/ios-swift-combine",
  "guidislink": false,
  "published": "Mon, 31 May 2021 13:46:25 GMT",
  "published_parsed": [
    2021,
    5,
    31,
    13,
    46,
    25,
    0,
    151,
    0
  ],
  "authors": [
    {
      "name": "yorifuji"
    }
  ],
  "author": "yorifuji",
  "author_detail": {
    "name": "yorifuji"
  }
}

main.py

import json
import feedparser # pip install feedparser
import datetime
import time
import sys

class Article:
  def __init__(self, json):
    self.title      = json['title']
    self.link       = json['link']
    self.published  = datetime.datetime.fromtimestamp(time.mktime(json['published_parsed'])+3600*9).isoformat() + "+09:00"

def parse(username):
  articles_json = feedparser.parse('https://zenn.dev/' + username + '/feed').entries
  return sorted([Article(json) for json in articles_json], key=lambda article: article.published)

if __name__=='__main__':
  if len(sys.argv) > 0:
    articles = parse(sys.argv[1])
    for article in articles:
      print(article.published, article.link, article.title, sep=',')
  else:
    print("Usage: main.py [username]")

RSSの日時の情報から

  "published": "Mon, 31 May 2021 13:46:25 GMT",
  "published_parsed": [
    2021,
    5,
    31,
    13,
    46,
    25,
    0,
    151,
    0
  ],

を見慣れた日時の文字列に変換するため、

datetime.datetime.fromtimestamp(time.mktime(json['published_parsed'])+ 3600 * 9).isoformat() + "+09:00"

のような処理を行いました。published_parsedの配列から以下のような文字列を生成しています。

2021-05-31T22:46:25+09:00

https://ja.wikipedia.org/wiki/ISO_8601

Pythonの日付処理に不慣れなので、もっと良い方法があるかもしれません。

Docker / Makefile

自分の環境ではDockerを使って実行しています。

.PHONY: all

all:
        @docker run --rm -it -v `pwd`:/app python:slim sh -c "cd /app && pip install feedparser >/dev/null 2>&1 && python main.py yorifuji"

まとめ

実際にこのスクリプトを使って生成した記事リストを貼り付けた記事はこちらです
https://zenn.dev/yorifuji/articles/self-release-note-202104

Discussion