知識の枝

"All is well"

Pythonを使ってRSSから情報を取り出す

約12日前 2021年11月14日18:15
デジタル
Python

改訂履歴


2021/11/14 投稿

1. RSSとは?


お気に入りのブログやニュースサイトはありませんか?

毎日の日課のようにウェブページにアクセスし、新しい記事があれば読み、無ければブラウザを閉じる。

新しい記事があるかどうかは、そのページを開くまで分かりません。


そこで便利なのがRSSと呼ばれる機能です。

RSSが何の略なのかは置いておき、”情報を配信するもの” と考えて下さい。


お気に入りサイトにRSS機能がある場合、そのRSSをRSSリーダー(フィードリーダー)というツールに登録します。

すると "サイトが更新されたとき" にツールが更新があったことをお知らせしてくれます。


以下がRSSの中身の例です。
Googleトレンド のRSSの一部を抜粋しました。
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:ht="https://trends.google.co.jp/trends/trendingsearches/daily" version="2.0">
<channel>
<title>Daily Search Trends</title>
<description>Recent searches</description>
<link>https://trends.google.co.jp/trends/trendingsearches/daily?geo=JP</link>
<atom:link href="https://trends.google.co.jp/trends/trendingsearches/daily/rss?geo=JP" rel="self" type="application/rss+xml"/>
<item>
<title>エリザベス女王杯</title>
<ht:approx_traffic>200,000+</ht:approx_traffic>
<description>アカイイト</description>
<link>https://trends.google.co.jp/trends/trendingsearches/daily?geo=JP#%E3%82%A8%E3%83%AA%E3%82%B6%E3%83%99%E3%82%B9%E5%A5%B3%E7%8E%8B%E6%9D%AF</link>
<pubDate>Sun, 14 Nov 2021 13:00:00 +0900</pubDate>
<ht:picture>https://t2.gstatic.com/images?q=tbn:ANd9GcS3Zvi_GUCWjhcFsIblVAzBeLx89L9d5o3fbvwZTdI1dt9poVBbg2gPIWz0cn21DUiq5J6MBQfU</ht:picture>
<ht:picture_source>スポーツナビ</ht:picture_source>
<ht:news_item>
<ht:news_item_title>【エリザベス女王杯結果速報】アカイイトが勝利して大波乱! 2着は ...</ht:news_item_title>
<ht:news_item_snippet>11月14日、阪神競馬場で行われたエリザベス女王杯は幸英明騎手騎乗のアカイイトが1着で入線。2着にはステラリア、3着にはクラヴェルが入線した。</ht:news_item_snippet>
<ht:news_item_url>https://news.yahoo.co.jp/articles/f19ed089d311092d8f373c3d5a3828bd1cbe251a</ht:news_item_url>
<ht:news_item_source>スポーツナビ</ht:news_item_source>
</ht:news_item>
<ht:news_item>
<ht:news_item_title>【エリザベス女王杯】10番人気アカイイトがGI初挑戦で大金星 ...</ht:news_item_title>
<ht:news_item_snippet>14日、阪神競馬場で行われたエリザベス女王杯(3歳上・牝・GI・芝2200m)は、序盤は後方に位置した幸英明騎手騎乗の10番人気アカイイト(牝4、栗東・中竹和也厩舎)&nbsp;...</ht:news_item_snippet>
<ht:news_item_url>https://news.yahoo.co.jp/articles/d6d775d0a6f4eb906e2de1a708d127002fbb9df5</ht:news_item_url>
<ht:news_item_source>Yahoo!ニュース</ht:news_item_source>
</ht:news_item>
</item>
</channel>
</rss>
タグを使った書き方がhtmlに似ていますね。

RSSには「RSS1.0」「RSS2.0」「Atom」といった種類があるのですが、使われている言語は共通です。

XML(Extensible Markup Language)と呼ばれる言語が使用されており、"Markup Language" とあるように、hmtl(HyperText Markup Language)同じマークアップ言語ということが分かります。


このXMLで書かれたRSSを特殊なツールで読み込むことで、特定のサイトの情報を得ることができます。

それではPythonを使ってこの情報を取り出してみましょう。


2. RSSから情報を取得する


Pythonを使ってRSSから情報を取り出してみましょう。

Pythonには標準でXMLを解析する機能が搭載されています。

その機能が「ElementTree(エレメントツリー)」と呼ばれるものです。
Pythonの公式ドキュメント に解説ページがあります。

モジュール名は「xml.etree.ElementTree」で、下記のようにPythonプログラム内でインポートします。
import xml.etree.ElementTree as ET



XMLを解析するには、まず元となるXMLそのものを入手する必要があります。

今回はGoogleトレンドの急上昇ワードRSSから入手します。

入手先のURLは 「急上昇ワード」 です。

このURLからXMLを取得するにはPythonの標準機能の「urllib パッケージ」を利用します。


先程のElementTreeと同様にインポートしましょう。
import xml.etree.ElementTree as ET
import urllib.request


上記のインポートに続いてRSSのURLからXMLデータを取得します。
# 対象のURL
url = "https://trends.google.co.jp/trends/trendingsearches/daily/rss?geo=JP"

# URLに対してリクエストオブジェクトを生成
req = urllib.request.Request(url)

# レスポンスオブジェクトを取得し、中身(XMLの文字列)を読み込む
res = urllib.request.urlopen(req).read()

# XMLの構文解析&ツリー構造化
root = ET.fromstring(res)


rootというのはツリー構造そのもののルート、つまり一番上位に位置します。

ツリー1階層目のタグを出力してみましょう。
「.tag」でタグ情報を取得可能です。

print(root.tag)
> rss
この「rss」というのは1階層目に存在するタグです。
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:ht="https://trends.google.co.jp/trends/trendingsearches/daily" version="2.0">
</rss>


例えばこの<rss>タグの属性を取得したい場合は「.attrib」を実行します。
print(root.attrib)
{'version': '2.0'}
タグの中の「version属性」が表示されました。

他にも2つ属性があるのに表示されていませんね。
「xmlns:atom="http://www.w3.org/2005/Atom"」
「xmlns:ht="https://trends.google.co.jp/trends/trendingsearches/daily"」

これらは「xmlns属性」と呼ばれ、”名前空間” というものを定義しています。

.attribで取得できないのは仕様なのか、理由は分かりませんでした。
(もし理由をご存じの方がいれば教えていただきたいです。)


この ”名前空間” 、PythonでRSSを取得する上で重要な要素です。

後で使うので、そのときに詳しく説明します。



さて、本当に取得したい情報はここじゃなかったですね。

今回のメインは<item>タグの中身です。

ツリー構成上は下記のようになっています。
rss
└─channel
├─title
├─description
├─link
├─atom
├─item # この中身が重要
├─item # この中身が重要
├─item # この中身が重要
└─item # この中身が重要
<item>タグは複数ありますので、取り出してみましょう。

このように取得したいタグが同じ名前で複数ある場合は、以下の方法のいずれかで対処可能です。


.iter()を使った方法


root.iter("取得したいタグ名") とすることで、目的のタグをイテレータとして取得可能です。

取得したイテレータをfor文で回すことで、それぞれのタグに対して処理を実行可能です。

例えば各<item>タグ内の<title>タグの中身をすべて表示してみます。
for item in root.iter("item"):
print("タイトル: {0}".format(item.find("title").text))

> タイトル: エリザベス女王杯
> タイトル: 東日本女子駅伝
> タイトル: 松村北斗
> タイトル: モンストシャーマンキング
> タイトル: 小室眞子
> タイトル: ウインマリリン
> タイトル: アカイトリノムスメ
> タイトル: RISE
> タイトル: 藤井聡太
> タイトル: 土竜の唄
> タイトル: 地震
> タイトル: 原神
> タイトル: エリザベス女王杯予想
> タイトル: 手足口病
> タイトル: 生田斗真
> タイトル: 坂本花織
> タイトル: TXT
> タイトル: 阿佐ヶ谷姉妹
> タイトル: 竜王戦
> タイトル: Mステ
上手く取得できました。

各<item>タグごとに「.find("title").text」を実行して<title>タグの中身を取り出しています。

この「.iter()」を使った方法の良さは、

目的のタグがどの階層にあっても全て取得できる という点です。

ですが、これは悪さでもあります。

例えば今回のRSSで "<title>タグ" をiter()で取得してみましょう。
for item in root.iter("title"):
print("タイトル: {0}".format(item.text))

> タイトル: Daily Search Trends # 1つ増えた
> タイトル: エリザベス女王杯
> タイトル: 東日本女子駅伝
> タイトル: 松村北斗
...
上記のように "本来必要無い部分" の情報も取得してしまいます。
rss
└─channel
├─title # 必要無いのに取得してしまった
├─description
├─link
├─atom
├─item
│ └─title # 本来欲しいもの
├─item
│ └─title # 本来欲しいもの
├─item
│ └─title # 本来欲しいもの
└─item
└─title # 本来欲しいもの

このように別の階層でタグ名が被っていた場合、そちらのタグも取得してしまいます。

使い所が肝心ですね。


.findall()を使った方法


こちらの方法は先程とは異なり、

"対象の階層に存在する要素" のみが対象となります。

ツリーを深堀りする機能はありませんので、

root.findall("item") と検索しても、何も引っかかりません。

なぜならroot要素には<rss>タグしかないからです。


ではどのように目的の<item>タグを取得するのか?


最も簡単な方法は、直接階層を指定してあげることです。

具体的には下記のようにします。
for item in root.findall("./channel/item"):
print("タイトル: {0}".format(item.tag))

> タイトル: エリザベス女王杯
> タイトル: 東日本女子駅伝
> タイトル: 松村北斗
...
目的の<item>タグは<channel>タグの直下にありますので「"./channel/item"」と指定します。

この条件にマッチする結果に対してfor文で中身を出力しています。



ちなみにこんな方法でもいけます。
for item in root.iter("channel"):
for i in item.findall("item"):
print("output: {0}".format(i.tag))

> タイトル: エリザベス女王杯
> タイトル: 東日本女子駅伝
> タイトル: 松村北斗
...
<item>タグの親要素(この場合は<channel>)を.iter()で取得します。
その<channel>タグ内の階層に対して.findall()してあげることで、先程と同様に<item>タグを取得できます。

こちらの方法は、まず使うことはないでしょう。


3. 練習して使い慣れよう


次はRSSから「タイトル」と「サムネイル画像」を取り出してみます。

ここで注意してほしいのが「サムネイル画像」のタグです。
<ht:picture>●●●</ht:picture>
こんな感じのタグなのですが、これまでと異なり「picture」の手前に「ht:」という文字がくっついていますね。

こういったタグの場合、"picture" のようなタグ名の指定方法では選択することができません。


この「ht:」の部分が先程述べた ”名前空間” に当たります。

<rss>タグの中に下記のような名前空間の宣言がありましたよね。
xmlns:ht="https://trends.google.co.jp/trends/trendingsearches/daily"
この場合「ht」が名前空間の名前(プリフィックスと言います)

「"https://trends.google.co.jp/trends/trendingsearches/daily"」が名前空間URIとなります。


名前空間が付いたタグを選択したい場合は下記のように書きます。
# 変更前
"picture"

# 変更後
"{https://trends.google.co.jp/trends/trendingsearches/daily}picture"

指定するタグ名の前に {名前空間URI} を付けます。

それではコードを書いてみます。
for item in root.iter("item"):
title = item.find("title").text
try:
img_src = item.find("{https://trends.google.co.jp/trends/trendingsearches/daily}picture").text
except:
img_src = None

print("{0}:{1}".format(title, img_src))


> エリザベス女王杯:https://t1.gstatic.com/images?q=tbn:ANd9GcRHKzpGfUoepveBkPH5Ts1KsFaJun-3dNKP7y7g1rCn93WfaBUxfNLMYysoqfX5wcF8bSsiTTmP
> 東日本女子駅伝:https://t3.gstatic.com/images?q=tbn:ANd9GcRbcWt3ieo-Tke0jjP8xzpOzpAOMKMbfqWNfNoojHnzk_v9iOLtK0rNMzrowEqnkzN7hDaaQ6Wi
> 松村北斗:https://t3.gstatic.com/images?q=tbn:ANd9GcRaosV-WTmihyRWDpfpMvc6VeDVkONvVaejcZx85h43TJAvAi8sgvrtaisIrSrpKxiyv_A8hGpH
> 小室眞子:https://t2.gstatic.com/images?q=tbn:ANd9GcQ0Pul0FOWzNKtaJ8CNuOxvrBiYbsvTBViJB2xmh8AD_s4wjqtqHeRcPZwt19My5mLM4GiL7HsG
> シャーマンキング:https://t1.gstatic.com/images?q=tbn:ANd9GcRWQJRt1XVU_xMWPJhngmMcCOB-SqWcYlfJaww25eDpB9BMC5zBJ3WISsspSv-uc4elrA2BN-cX
> ヒコロヒー:https://t0.gstatic.com/images?q=tbn:ANd9GcRES3mxTbdgc5GIA1NlGuZJDKpW3sbpRWRdmTplcwfSZqHZcVDZA0CjED2EpAe8Ik6UGe2mrq4j
> RISE:https://t3.gstatic.com/images?q=tbn:ANd9GcS-dniHyf9ipSbCG6eMGsL1x4OcSuq0Vukw1VEPPwfqi3Maz3OayIsEdlSuCL7AtUQsBSOqvoff
> アカイトリノムスメ:https://t1.gstatic.com/images?q=tbn:ANd9GcR6Tl0e-BVyk7WhrgoSptUDBzp0ZJFiuVEI3n7wMiRaOZTQUqegId4pS3cOOwLidleb0kBkW8aR
> 藤井聡太:https://t1.gstatic.com/images?q=tbn:ANd9GcQmqkm4dTokUuPf6mVyzxuIShqLEjUvpg7iTFUSDd4Ar99Myh_8XSYzRaYTO5LiQ8Thbi8wsRcd
> 土竜の唄:https://t2.gstatic.com/images?q=tbn:ANd9GcSUM-kdxgYPWNqvt80VCSkmBzBREFqlTLnotcRXzZpaAII_htjYxhYyi1271aZvEg5_-6i2kzuS
> 地震:None
> 原神:https://t3.gstatic.com/images?q=tbn:ANd9GcR2eDvIHaPNWpYFj9W7u5UADVBfgB5Uw0G0KA6IrRSPnt-1TqR47W0MbbrChUDm2pR_p2umSCci
> エリザベス女王杯予想:https://t1.gstatic.com/images?q=tbn:ANd9GcQL4cWPsO-_cK1ER1j3D1e0Jkr6at_14QdMWo3aLehKrRD2kwW9gOy8apRfq_TLHYWV3kzJC-JO
> 手足口病:https://t0.gstatic.com/images?q=tbn:ANd9GcSfWUvG7eiwT5dPhjnQA-yTgPaQM9aQfsOzfEi91mRdDQK5-j_KlJ3BUAkR-tdRbT7z1W8y79Vf
> 生田斗真:https://t1.gstatic.com/images?q=tbn:ANd9GcRoI9paok9v8KfRLY7v4VI_kC53dtsZDMG4a6ZZRqh5hBM4QtgUAav6z_ef4DRsLWen4dZ8uH7F
> 坂本花織:https://t1.gstatic.com/images?q=tbn:ANd9GcQi1OxWS6qvEwvlpiyKUVAgzxo6LKvcaClJgJoZ7tW3-OhqaP0irivVZlhTkc0KcghAgGByTa-N
> TXT:https://t3.gstatic.com/images?q=tbn:ANd9GcS2pFllPh9a3w1ggj7LZ5EbqDiz2u9xbBdYqVLcGBY9ZtZP-obZGmJdebSZOpwLFlpyUk_Eg3if
> 阿佐ヶ谷姉妹:https://t1.gstatic.com/images?q=tbn:ANd9GcSiMVpgk-oWH6s-RpQ1NJpsXWQIqJSzkgyUVShdczoCvNeXEvHk3zxKgBghK9jY-yL2P8mUqciS
> 竜王戦:https://t3.gstatic.com/images?q=tbn:ANd9GcT_dH8Vi1QGlkK2GHp9FJcrmTdMMhNI2gq0iyUI9_nNQr3W04tJGVVncKu4Q1anx5UikSDAb8ft
> Mステ:https://t1.gstatic.com/images?q=tbn:ANd9GcTDBJ1f4xkXY7k-tMb6HnXx5_BhdUOjifJuY9fDu-v2lCFcuNHfc5uuNAsytUPnhCcpbY88Loa8
※ <item>タグの中には<ht:picture>タグが無いものも存在する為、上記では try/except を使用しています。

エリザベス女王杯:エリザベス女王杯

東日本女子駅伝:東日本女子駅伝

松村北斗:松村北斗

...

こんな感じで取り出せます。


4. まずはRSSを見つけよう


色々なサイトから情報を集めたいとき、スクレイピングという手段を用いることがあると思います。

スクレイピングは複雑なウェブページをサイトごとに解析する必要があり、中々骨が折れます。

もし目的のサイトにRSSがあり、その内容があなたにとって十分なものであれば、RSSを使わない手はありません!

構造がシンプルですし、解析に掛かる時間もスクレイピングと比較して段違いに早いです。

まずはRSSを見つける。

これに付きます!