Bloggerのフィードからブログカードを一気に作成 - Python編

2021年9月5日日曜日

BeautifulSoup Blogger Bloggerカスタマイズ CSS HTML pandas Python ブログカード

t f B! P L
title-image

以前に JavaScript を使い Blogger のフィードから各記事のブログカードを作成する方法をご紹介しました。

ブログカードの HTML を出力することはできたのですが、その後コピペを考えると一度エクセルかGoogleスプレッドシートに読み込めると便利だなと感じていました。

そこで今回はブログカードを CSV 出力して Googleスプレッドシート に読み込むやり方をご紹介します。

Python を使う

JavaScript で CSV 出力するのが面倒そうなので、Python で書いてみます。以下、Python3.9.1 と、外部ライブラリ BeautifulSoup と Pandas がインストールされていることを前提に書いています。

ライブラリ

ライブラリは下記を使用します。

  • requests: URLからフィードのデータを取得する
  • BeautifulSoup: フィードの XML や記事の中身 HTML からデータを抜き出す
  • re: 記事内の画像URLを文字列置換するための正規表現ライブラリ
  • pandas: CSV出力するためのライブラリ
import requests
from bs4 import BeautifulSoup
import re
import pandas as pd

URLからフィードを取得

Blogger の場合、以下のような URL からフィードを取得できます。期間 published-min, published-max と、取得する最大記事数 max-results を URL パラメータで渡します。

# フィードを取得するURL
feedURL = "https://www.blogger.com/feeds/6266051941055901697/posts/default?published-min=2018-09-16T00:00:00&published-max=2021-12-14T23:59:59&max-results=300"

フィードのURLは下記記事をご参考ください。

ブログカードのひな型を準備

ブログカードのひな型は、下記のようにします。後で内容を置き換えるためフォーマット文字列で置き換える部分を {0}, {1}, … としています。

# ブログカードのひな型
card_template = """
<article class="blog-card">
  <a href="{0}"  aria-label="{2}">
    <img src="{1}" width="160" height="90" alt="thumbnail" loading="lazy"/>
  </a>
  <div class="text-contents">
    <h3 class="title">
      <a href="{0}">
    {2}
      </a>
    </h3>
    <p>
      {3} ...
    </p>
  </div>
</article>
"""

フィードをURLから取得して BeauifulSoup へ読み込む

requests.get() で URL からフィードデータを取得します。取得したデータをこんどは BeautifulSoup に読み込み、DOM 要素にアクセスできるようにします。

# フィードをURLから取得して BeauifulSoup へ読み込む
res = requests.get(feedURL)
soup = BeautifulSoup(res.text, 'lxml')

entry タグに囲まれた各記事を取得

各記事は、entry タグに囲まれているので、entry タグを全て抜き出し、items変数に入れます。この変数は配列になり、配列の要素に記事が1つずつ入っています。これを処理した結果を新しい配列に入れていくため、空の配列 feed_items も定義します。

# 各記事 (entry タグの中身) を配列 items に入れる
items = soup.findAll('entry')
# 前記事のブログカードに必要なデータを入れる配列
feed_items = []

各記事に対し、必要な情報を取得

記事が配列に格納された items 変数に対し、for ループで1つ1つDOM要素の中身を取り出し、タイトル、URLなど必要な情報を feed_item 変数に入れていきます。

for item in items:
    feed_item = {}
    feed_item['title'] = item.title.text
    print(feed_item['title'])
    feed_item['uri'] = item.find('link', rel='alternate')['href']
    feed_item['content'] = item.content.text
    feed_item['published'] = item.published.text

各記事内の、記事本文 (content タグ内) に対し、さらに BeautifulSoup で DOM を構築

ブログ記事の本文は、item 変数に抜き出した entry タグの中の、さらに内側の content タグ内に入っています。なので、記事本文の DOM 要素を取得するには、content タグ内の文字列を、さらにもう一度 BeautifulSoup で読み込んでやる必要があります。

変数 soup_a に記事本文の DOM を読み込んだら、記事内の img タグや本文などブログカードに必要な部分を feed_item 変数に入れていきます。

    # 各記事の本文 (feed_item 変数に格納された entry タグの中の、contentタグの中身)
    # に対して、さらに BeauifulSoup に読み込み 
    soup_a = BeautifulSoup(feed_item['content'], 'lxml')
    # 本文内の画像の URL を抜き出す。Bloggerでは img タグの src 属性に画像サイズを指定する文字列があるため
    # 文字列置換で、幅 160px の画像URLを feed_item['img'] に入れる
    if soup_a.img.has_attr('src'):
        feed_item['img'] = re.sub('/[whs]\d{1,4}-?.+/','/w160/',soup_a.img['src'])
    # 本文の p タグ内の最初の50文字をスニペットとして feed_item['snippet'] に格納
    feed_item['snippet'] = soup_a.p.text[:50]

Blogger の特徴として、アップロードした画像は、URL内でサイズを指定することができます。例えば、/s400/ という文字列があれば、画像の長編が 400px になります。ブログカードでは大きな画像は不要で、小さな画像で十分なので、文字列操作で幅を 160px に縮小しています。/s400/ → /w160/ などとなるように正規表現を使い文字列置換しているのがポイントです。

また、スニペットは p タグの最初の 50 文字までを取得しています。

BeautifulSoup を使い抜き出した値をブログカードのひな型に当てはめる

以上で取得した値をフォーマット文字列に当てはめ、各記事のブログカードを feed_items 変数に次々と追加していきます。

    # 上記取得したデータをひな型に入れてブログカードのHTMLを作成、feed_item['card'] に格納
    feed_item['card'] = card_template.format(feed_item['uri'],feed_item['img'],feed_item['title'],feed_item['snippet'])
    # feed_items 変数に追加していく
    feed_items.append(feed_item)

pandas で CSV へ出力

最後に panas で out.csv のファイル名で CSV ファイルを書き出して終了です。

# feed_items に格納されたデータを pandsc で CSV 出力、out.csv に書き込み
df = pd.DataFrame(feed_items, columns=['title','uri','content','published','img','snippet','card'])
df.head()
df.to_csv('out.csv',index=False,encoding='utf-8')

上記を blog-card.py に保存します。念の為全コードを下記に載せておきます。

import requests
from bs4 import BeautifulSoup
import re
import pandas as pd

# フィードを取得するURL
feedURL = "https://www.blogger.com/feeds/6266051941055901697/posts/default?published-min=2018-09-16T00:00:00&published-max=2021-12-14T23:59:59&max-results=300"

# ブログカードのひな型
card_template = """
<article class="blog-card">
  <a href="{0}"  aria-label="{2}">
    <img src="{1}" width="160" height="90" alt="thumbnail" loading="lazy"/>
  </a>
  <div class="text-contents">
    <h3 class="title">
      <a href="{0}">
    {2}
      </a>
    </h3>
    <p>
      {3} ...
    </p>
  </div>
</article>
"""

# フィードをURLから取得して BeauifulSoup へ読み込む
res = requests.get(feedURL)
soup = BeautifulSoup(res.text, 'lxml')

# 各記事 (entry タグの中身) を配列 items に入れる
items = soup.findAll('entry')

# 前記事のブログカードに必要なデータを入れる配列
feed_items = []

# BeautifulSoup に読み込んだ各記事について、それぞれタイトル等を feed_item に入れる
for item in items:
    feed_item = {}
    feed_item['title'] = item.title.text
    print(feed_item['title'])
    feed_item['uri'] = item.find('link', rel='alternate')['href']
    feed_item['content'] = item.content.text
    feed_item['published'] = item.published.text

    # 各記事の本文 (feed_item 変数に格納された entry タグの中の、contentタグの中身)
    # に対して、さらに BeauifulSoup に読み込み 
    soup_a = BeautifulSoup(feed_item['content'], 'lxml')
    # 本文内の画像の URL を抜き出す。Bloggerでは img タグの src 属性に画像サイズを指定する文字列があるため
    # 文字列置換で、幅 160px の画像URLを feed_item['img'] に入れる
    if soup_a.img.has_attr('src'):
        feed_item['img'] = re.sub('/[whs]\d{1,4}-?.+/','/w160/',soup_a.img['src'])
    # 本文の p タグ内の最初の50文字をスニペットとして feed_item['snippet'] に格納
    feed_item['snippet'] = soup_a.p.text[:50]
    # 上記取得したデータをひな型に入れてブログカードのHTMLを作成、feed_item['card'] に格納
    feed_item['card'] = card_template.format(feed_item['uri'],feed_item['img'],feed_item['title'],feed_item['snippet'])
    # feed_items 変数に追加していく
    feed_items.append(feed_item)

# feed_items に格納されたデータを pandas で CSV 出力、out.csv に書き込み
df = pd.DataFrame(feed_items, columns=['title','uri','content','published','img','snippet','card'])
df.head()
df.to_csv('out.csv',index=False,encoding='utf-8')

blog-card.py の実行

さて、使ってみましょう。ライブラリが無い場合は pip install requests bs4 pandas lxml など、インストールしておいてください。

また、フィードを取得するURLと期間、記事数はあらかじめ書き換えておいてください。

Python のコマンドは Windows だと py とかになるので適当に置き換えてください。成功すると、記事の題名が出力されるはずです。

$ python3 blog-card.py
CloudReady(ChromeOS) linux のバーションアップグレード
【第二種電気工事士】VVRケーブルの剥き方 5つの方法を試してみた結果は!?
Stravaのブログ埋め込みで幅が切れる・はみ出すときの対処法
機械製図検定 実施要項と合格の手引き
2021年版(令和3年度) 第二種電気工事士 後期試験の合格発表日と確認方法
高槻市の駅チカに弥生時代の遺跡が眠る-安満遺跡公園-
【機械・プラント製図】ウチダ ヘキサスケールが機械製図に便利!
無人島の蔦島で海水浴と鳴門海峡うずしおクルーズ
【第二種電気工事士】VVFストリッパー比較!技能試験におすすめなのは?
キャンプ用品レビュー:キャプテンスタッグ 雷神 COB ランタン
キャンプ用品レビュー:スノーピーク(snow peak) パイルドライバー
家族でお出かけ せんなん里海公園
南紀串本リゾート大島でキャンプと本州最南端めぐり
忍頂寺ヒルクライム(万博記念公園~忍頂寺)
高嶋海水浴場&オートキャンプ場で海キャンプ
川遊びスポット 丹生川上神社
初心者がGoogleアドセンスを初めて2回目の振り込みと買ったもの
琴引浜海水浴場 -日本海のきれいな海、琴引浜で小魚を見て遊ぶ
...

カレントディレクトリに out.csv というファイルができています。

$ ls
blog-card.py  out.csv

Google スプレッドシートへ読み込み

Googleスプレッドシートへ読み込んで見ましょう。まずは新しいスプレッドシートを作成します。

Googleスプレッドシートで新規作成

次に、「ファイル」→「インポート」を選択し、「アップロード」で先程できた out.csv を読み込みます。

アップロード

シートを置換するか聞かれますが最初はどれでも良いでしょう。2回めからは既存シートを置換を選択すると、新しいデータに上書きされます。

ダイアログ

こんな感じで読み込むことができました。あとは題名などでフィルタしたり好きなように記事を選択し、コピペしてブログへ貼り付けることができます!

GoogleスプレッドシートにCSVファイルを読み込んだ

まとめ

Pythonを使ってBlogger のフィードからブログカードを作ることができました。CSVへ出力できたのでGoogleスプレッドシートやExcelを使って検索やコピペが楽にできます。

タグの列を追加したりするとさらに便利に使えるかもしれません。

関連記事

Blog Archive

QooQ