Python3.3で、はてなブログAtomPubを使ってみた
はてなブログの不満の一つが、エクスポート機能が無いということ。
他社のブログに移行したいわけじゃないが、そのうち引っ越しを考えるかもしれないし、そもそもWebサービスは無保証なんだから、PCにバックアップぐらいしておきたい。
幸い、AtomPubという機能を使えば、ブログ記事を取得したり更新したりできるので、Python3.3を使って、ブログの全記事をダウンロードしてみた。
AtomPubの説明はこちら。
はてなブログAtomPub - Hatena Developer Center
やりたいこと
- AtomPubを使って、自分のブログの全記事をダウンロードしたい。
- (できれば)他の形式に変換したい。
AtomPubでは、記事をXML形式でダウンロードすることになるので、他社のブログに移行するのなら、他の形式に変換する必要があるだろう。
今回は、とりあえず1のダウンロードまでやってみる。
開発環境は、Python3.3.3 (32bit) + Windows7
必要なモジュール:
- requests
- BeautifulSoup4
追記
別の環境で試したところ、エラーが出てうまくいかなかったが、lxml を入れることで解決した。
Windowsなら、下記サイトからバイナリ版をダウンロードしてインストールするのが簡単だ。
http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml
AtomPubを使う準備
はてなID、ブログID、APIキーの3つが必要。
ブログIDはブログのアドレスで、このブログなら "py3.hateblo.jp" となる。
APIキーはブログの設定>詳細設定に記載されている。
ブログ記事のリストを取得するには、コレクションURIを GET すればよい。
コレクションURI | https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entry |
認証とダウンロード
requests で GET
import requests
r = requests.get(url)
普通のサイトなら、これだけでダウンロードできるのだが、今回は認証が必要。
はてなのAtomPubでは、Basic認証、WSSE認証、OAuth認証 の3種類に対応している。
Basic認証
import requests from requests.auth import HTTPBasicAuth r = requests.get(url, auth=HTTPBasicAuth(hatena_id, api_key))
これだけ。簡単だ。
WSSE認証
X-WSSEヘッダを付けてGETする。こちらも、ユーザ名=はてなID、パスワード=APIキー。
import requests import datetime, random, hashlib, base64 def create_wsse(username, password): """ X-WSSEヘッダの値を作成 """ created = datetime.datetime.now().isoformat() + "Z" b_nonce = hashlib.sha1(str(random.random()).encode()).digest() b_digest = hashlib.sha1(b_nonce + created.encode() + password.encode()).digest() s = 'UsernameToken Username="{0}", PasswordDigest="{1}", Nonce="{2}", Created="{3}"' return s.format(username, base64.b64encode(b_digest).decode(), base64.b64encode(b_nonce).decode(), created) r = requests.get(url, headers={'X-WSSE': create_wsse(hatena_id, api_key)})
WSSE認証は下記サイトを参考にした。
はてなブログのAPIが公開されたらしいので簡単に触ってみた(Python) - 唯物是真 @Scaled_Wurm
そのままではPython3で動かなかったため、少し変更している。
OAuth認証
まずアプリケーションを登録?なんか、ややこしそうなのでパス。
WSSE認証から OAuth への移行 - Hatena Developer Center
記事の続きをダウンロード
ここまでで、最新記事10件分が含まれたXMLをダウンロードできた。
このXMLの中に、記事の続き(11件目以降)をダウンロードするためのURIが書いてある。
<link rel="next" href="https://(略)/atom/entry?page=1377575606" />
このhref="~"の部分を取り出して、GET する。これを繰り返す。
BeautifulSoup
XMLを解析するためにBeautifulSoupを使う。
BeautifulSoupは、HTMLやXMLを解析するクラスで、XMLを使うときにはコンストラクタの引数に features='xml' を付ければいい。
<link ref="next" href="URI"> というタグを探して、href=の値を取得してみる。
from bs4 import BeautifulSoup soup = BeautifulSoup(r.content, features='xml') tag = soup.find('link', rel='next') url = tag.get('href') if tag else None
これで、続きをダウンロードするためのURIが取得できる。取得できなくなったら、終了。
まとめてみた
import requests from requests.auth import HTTPBasicAuth from bs4 import BeautifulSoup def download_xml_files(): hatena_id = '(はてなID)' blog_id = '(ブログID)' api_key = '(APIキー)' url = 'https://blog.hatena.ne.jp/{}/{}/atom/entry'.format(hatena_id, blog_id) for i in range(1, 100): # XMLをダウンロード print('GET', url) r = requests.get(url, auth=HTTPBasicAuth(hatena_id, api_key)) if r.status_code != 200: break # XMLを保存 (blog_001.xml) path = 'blog_{0:03d}.xml'.format(i) with open(path, 'wb') as f: f.write(r.content) # 次のURIを取得 soup = BeautifulSoup(r.content, features='xml') tag = soup.find('link', rel='next') url = tag.get('href') if tag else None if url is None: break
実行すると、カレントフォルダに blog_001.xml ~ のファイル名で保存される。
私の場合、記事数が少ないので、3ファイルだけだった。
(ちなみに画像などは含まれていない。文章だけだ。)
後日、このXMLをもう少し見やすい形式に変換しようと思う。