目標
Qiita の Feed から、直近の投稿記事のタイトル、URL、日付を取得する。それを、Python 標準モジュールだけで (つまり pip を使うことなく) 実現する。
URL 例: https://qiita.com/aKuad/feed
要素技術
Feed 取得
HTTP GET リクエストして、body を受け取りたいです。そこで urllib
が使えます。
from urllib.request import urlopen
feed = urlopen("https://qiita.com/aKuad/feed")
feed_text = feed.read().decode("utf-8")
print(feed_text)
ちなみに公式リファレンスでは、より高水準なインターフェースとして、pip から入れられる requests が推奨されています。ただ、ここではシンプルな HTTP GET しかしないので、これで十分です。
Feed パース & 要素取得
Feed は XML 形式で記述されています。XML ライブラリも標準にあります。
from xml.dom.minidom import parseString
feed_text = """
<feed>
<entry>
<title>Title1</title>
</entry>
<entry>
<title>Title2</title>
</entry>
</feed>"""
dom = parseString(feed_text)
items = dom.getElementsByTagName("entry")
for item in items:
title = item.getElementsByTagName("title")[0].firstChild.data
print(title)
# Title1
# Title2
最後の title タグの中を取得する部分がどうも不格好になってしまいましたが、これ以上キレイな手は自分には見つかりませんでした。orz
初めは xml.etree.ElementTree
を試したのですが、どうも上手くいかなかったので minidom を使う方法でやりました。
公式リファレンスで、この XML パーサには脆弱性があるとされています。ただ、ここでは決まった XML しか扱わないので問題ないとしています。もし、ユーザ任意の XML を入れる形を検討している場合は注意が必要です。
ついでに dataclass
C で言う構造体的なもの。
from dataclasses import dataclass
@dataclass
class ArticleInfo:
title: str
url: str
date: str
article = ArticleInfo("title text", "https://qiita.com", "2024-01-10T23:00:19+09:00")
print(article)
# ArticleInfo(title='title text', url='https://qiita.com', date='2024-01-10T23:00:19+09:00')
普通の Class と違うのは、print
すると値が表示されること、初期化関数で値を代入するコードがいらないことです。
ちなみに普通の Class だと
class ArticleInfo:
def __init__(self, title: str, url: str, date: str):
self.title = title
self.url = url
self.date = date
article = ArticleInfo("title text", "https://qiita.com", "2024-01-10T23:00:19+09:00")
print(article)
# <__main__.ArticleInfo object at 0x7fa72fe37970>
完成品
以上を組み合わせ、関数化とかしたものがこちら。
# coding: UTF-8
from urllib.request import urlopen
from xml.dom.minidom import parseString
from dataclasses import dataclass
@dataclass
class ArticleInfo:
title: str
url: str
date: str
def fetch_qiita_feed(user_id: str):
feed = urlopen(f"https://qiita.com/{user_id}/feed")
feed_text = feed.read().decode("utf-8")
dom = parseString(feed_text)
items = dom.getElementsByTagName("entry")
articles = []
for item in items:
title = item.getElementsByTagName("title" )[0].firstChild.data
url = item.getElementsByTagName("url" )[0].firstChild.data
date = item.getElementsByTagName("published")[0].firstChild.data
articles.append(ArticleInfo(title, url, date))
return articles
if __name__ == "__main__":
articles = fetch_qiita_feed("aKuad")
for article in articles:
print(article)
ArticleInfo(title='ユニットテストするならフレームワーク使おう (Python unittest)', url='https://qiita.com/aKuad/items/c01ef064603ac30e03fb', date='2024-01-10T23:00:19+09:00')
ArticleInfo(title='.service 自作して Python プログラムを systemd でデーモン化', url='https://qiita.com/aKuad/items/c702b508375a5d9c476b', date='2023-12-03T00:07:29+09:00')
ArticleInfo(title='Arduino Uno / atmega328p の SPI Slave ライブラリを自作する', url='https://qiita.com/aKuad/items/3d06998efc59daedd4c5', date='2023-11-12T16:02:11+09:00')
ArticleInfo(title='ESP32 マイコン を PlatformIO で動かす (on Linux Mint)', url='https://qiita.com/aKuad/items/eca015f2dcfd8bd57791', date='2023-11-08T22:47:41+09:00')
ところで何がしたかった?
本当に急な思いつきで、こんなことを考えました。
「GitHub Pages に自分の Web ページ作ったとして、そこに Qiita とか Zenn の直近の投稿を表示させられたりするかな?せや、Python で Feed 収集するのを GitHub Actions で定期実行したらできそうやん。」
まぁ、まだ Web ページ作成計画すら無いんですけど・・・。
あと Actions 上で pip 呼ぶのは何ら難しくないので、普通に拡張ライブラリ使い放題。標準モジュールにこだわる必要はほぼほぼありません。せいぜい実行時間が数秒ほど短くなるだけ。
おわり
えーっと、とりあえず、Python は標準モジュールだけでも割と色々できそうって話。
あと、Feed は古い技術ではありますが、単純で扱いやすいおかげで、こういう用途にとても適していると思います。