はてなブログAPIで記事の取得と編集
はてなブログAPIで記事の取得と編集
はてなブログAPI(はてなブログAtomPub)を使って,記事(1つ)の取得と編集のメモ.
前回の続きです.
今回も,PythonでrequestsモジュールとBeautifulSoup4を使う.
ドキュメントはここです.はてなブログAtomPub - Hatena Developer Center
環境
- Windows10
- python 3.5.4
- requests (2.18.4)
- beautifulsoup4 (4.6.0)
- python 3.5.4
はてなブログAtomPub APIがサポートしているメンバURIの操作
- ブログエントリの操作 (メンバURIを使う)
- ブログエントリの取得
- ブログエントリの更新
- ブログエントリの削除
メンバURIを使うことで,ブログエントリ(1記事)を取得・更新・削除ができる.
今回は,ブログエントリ(1記事)を取得・更新を行う.
メンバURI
メンバURIでは,entry idが必要になる.なので,カテゴリURIなどから取得しておく必要がある.
- entry_id: 意味:ブログエントリのID
https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entry/{entry_id}
entry_idの取得は,前回の記事を参照.
メンバURIで記事の取得
ブログエントリの取得
メンバURIをGETすることで、ブログエントリを取得できます。
リクエスト
GET https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entry/{entry_id}
requestsモジュールでResponse確認
import requests hatena_id="<hatena_id>" blog_id="<blog_id>" password="<api_key>" entry_id = "8599973812308945752" member_uri = "https://blog.hatena.ne.jp/{hatena_id}/{blog_id}/atom/entry/{entry_id}".format(hatena_id=hatena_id, blog_id=blog_id,entry_id=entry_id_list[0]) member_uri res_member = requests.get(member_uri, auth=(hatena_id, password)) print(res_member.text)
responseとして,コレクションURIにもあった<entry>
要素が返ってくる.
results:
<?xml version="1.0" encoding="utf-8"?> <entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app"> <id>tag:blog.hatena.ne.jp,2013:blog-cartman0-12921228815722929243-8599973812308945752</id> <link rel="edit" href="https://blog.hatena.ne.jp/cartman0/cartman0.hatenablog.com/atom/entry/8599973812308945752"/> <link rel="alternate" type="text/html" href="http://cartman0.hatenablog.com/entry/2017/10/18/%E3%81%AF%E3%81%A6%E3%81%AA%E3%83%96%E3%83%AD%E3%82%B0API%E3%81%A7%E8%A8%98%E4%BA%8B%E4%B8%80%E8%A6%A7%E3%82%92%E5%8F%96%E5%BE%97"/> <author><name>cartman0</name></author> <title>はてなブログAPIで記事一覧を取得</title> <updated>2017-10-18T00:34:55+09:00</updated> <published>2017-10-18T00:34:55+09:00</published> <app:edited>2017-10-18T00:38:24+09:00</app:edited> <summary type="text">はてなブログAPIで記事一覧を取得…</summary> <content type="text/x-markdown"># はてなブログAPIで記事一覧を取得...</content> <hatena:formatted-content type="text/html" xmlns:hatena="http://www.hatena.ne.jp/info/xmlns#"><h1 id="はてなブログAPIで記事一覧を取得"><a class=&quo... </hatena:formatted-content> <category term="はてなブログ" /> <app:control> <app:draft>no</app:draft> </app:control> </entry>
summary
:最大140文字の文書の要約content
:はてなブログに登録されたシンタックスと解釈されたオリジナルの文面が記載される,つまり,はてな記法などもそのまま表記されている.type="text/html"
: 見たままモードtype="text/x-hatena-syntax"
: はてな記法type="text/x-markdown
: マークダウン記法
hatena:formatted-content
: 常にエンコードされたhtml,はてな記法やmarkdown記法もhtmlで解釈されている.各htmlタグも特殊文字"<", ">"で表記される.
記事のコンテンツ(content要素)の取得
メンバURIのresponseも確認できたので,responseのXML記事のコンテンツのみ取得する.BeautifulSoup4を使ってXMLから<content>
要素のみをパースすればいい.
import requests import bs4 hatena_id="<hatena_id>" blog_id="<blog_id>" password="<api_key>" entry_id = "8599973812308945752" def get_entry_content_str(hatena_id, blog_id, password, entry_id): member_uri = "https://blog.hatena.ne.jp/{hatena_id}/{blog_id}/atom/entry/{entry_id}".format(hatena_id=hatena_id, blog_id=blog_id,entry_id=entry_id) res_member = requests.get(member_uri, auth=(hatena_id, password)) if not res_member.ok: print("Failed: status_code: " + str(res_member.status_code)) return False soup_response_xml = bs4.BeautifulSoup(res_member.content, features="xml") return soup_response_xml.find("content").string get_entry_content_str(hatena_id, blog_id, password, entry_id='8599973812308945752')
今回は,markdown記法で書いた記事なので,markdown書式のコンテンツの文字列が取得できる.
results:
'# はてなブログAPIで記事一覧を取得\n\nはてなブログAPI(はてなブログAtomPub)で記事一覧取得のメモ.\n「サービス文書URIを使って, コレクション操作の一覧の取得」と\n「コレ...
gistにも置いておく.はてなブログAPI entry_idから記事のコンテンツ要素(文字列)を取得 · GitHub
メンバURIで記事の編集
ブログエントリの編集
メンバURIに対してXML文書をPUTすることで、ブログエントリを編集できます。投稿されたブログエントリの日時は投稿を行った日時になります。新規ブログエントリの投稿と同じく、編集時にもブログに登録された記法が適用されます。
新しい記事を含めたXMLをメンバURIに対してPUT
することで記事を編集できる.
リクエスト
PUT https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entry/{entry_id}
リクエストXML文書に必要なパラメータ: - title要素: ブログエントリのタイトル - content要素: 記述されたブログエントリの本文 - updated要素: ブログエントリを投稿する日時を指定.この指定を行わなかった場合、投稿を行った日時が利用される.これが投稿日時となるので,ブログでの表示順に影響する. - category要素: ブログエントリのカテゴリを指定(複数可) - term属性 カテゴリ名を指定 - app:control/app:draft要素: ブログエントリを下書きにするか指定できる.yesと指定した場合,下書きとみなされる.
requestモジュールでのデータ指定
今回のようにXMLを渡す場合は,
requestsモジュールでは data
引数に指定する.
xml_str = "<?xml version="1.0" encoding="utf-8"?>..." requests.put("<URI>, "data=xml_str.encode("utf-8"))
requestsモジュールでは,utf-8でエンコードしないとlatin-1
でエンコードされる.
requestsモジュールでResponseを確認
XMLの必要な要素は上記で書いたように, - title要素 - content要素 - updated要素 - category要素 - term属性 - app:control/app:draft要素
になる.
今回は,PUTするXMLをGETして取得したresponseのXMLを加工して作る.
hatena_id="<hatena_id>" blog_id="<blog_id>" password="<api_key>" entry_id = "8599973812308945752" member_uri = "https://blog.hatena.ne.jp/{hatena_id}/{blog_id}/atom/entry/{entry_id}".format(hatena_id=hatena_id, blog_id=blog_id,entry_id=entry_id) res_member = requests.get(member_uri, auth=(hatena_id, password)) if not res_member.ok: raise Exception("Failed: status_code: " + str(res_member.status_code)) soup_response_xml = bs4.BeautifulSoup(res_member.content, features="xml") # XMLsoupのクローン update_soup_xml = bs4.BeautifulSoup(str(soup_response_xml), features="xml") # id 削除 if update_soup_xml.id: update_soup_xml.id.decompose() # link 削除 for l in update_soup_xml.findAll("link"): l.decompose() # delete published if update_soup_xml.published: update_soup_xml.published.decompose() # delete app:edited edited = update_soup_xml.find("app:edited") if edited: edited.decompose() # delete summary if update_soup_xml.summary: update_soup_xml.summary.decompose() # title new_title = "" if new_title: update_soup_xml.title.string = new_title # author new_author = "" if new_author: update_soup_xml.author.string = new_author # updated new_updated = "" if new_updated: update_soup_xml.updated.string = new_updated # new category new_categories = [] for new_c in new_categories: cate_tag = update_soup_xml.new_tag("category") cate_tag.attrs = {"term": new_c} update_soup_xml.append(cate_tag) # new draft new_draft = "" # yes, no if new_draft: soup_response_xml.find("app:draft").string = new_draft # content書き換え new_content = "# content1" update_soup_xml.content.string = new_content update_soup_xml
このxmlをPUTすればいい.
requests.put(member_uri, data=str(update_soup_xml).encode("utf-8"))
results:
<?xml version="1.0" encoding="utf-8"?> <entry> <id>tag:blog.hatena.ne.jp,2013:blog-{はてなID}-20000000000000-3000000000000000</id> <author><name>{はてなID}</name></author> <title>新しいタイトル</title> <updated>2008-01-01T00:00:00</updated> <published>2013-09-02T11:28:23+09:00</published> <app:edited>2008-01-01T00:00:00</app:edited> <summary type="text"> 新しい本文 </summary> <content type="text/x-markdown"> ** 新しい本文 </content> <hatena:formatted-content type="text/html" xmlns:hatena="http://www.hatena.ne.jp/info/xmlns#"> <div class="section"> <h4>新しい本文</h4> </hatena:formatted-content> <category term="新しいカテゴリ" /> <app:control> <app:draft>no</app:draft> </app:control> ... </entry>
編集に成功した場合,新しいentry
要素のresponseが返る.
gist: はてなブログAPI 記事の編集 · GitHub