はしくれエンジニアもどきのメモ

情報系技術・哲学・デザインなどの勉強メモ・備忘録です。

はてなブログAPIで記事の取得と編集

はてなブログAPIで記事の取得と編集

はてなブログAPIはてなブログAtomPub)を使って,記事(1つ)の取得と編集のメモ.

前回の続きです.

cartman0.hatenablog.com

今回も,PythonでrequestsモジュールとBeautifulSoup4を使う.

ドキュメントはここです.はてなブログAtomPub - Hatena Developer Center

環境

  • Windows10
    • python 3.5.4
      • requests (2.18.4)
      • beautifulsoup4 (4.6.0)

はてなブログ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#">&lt;h1 id=&quot;はてなブログAPIで記事一覧を取得&quot;&gt;&lt;a class=&quo...
</hatena:formatted-content>
<category term="はてなブログ" />
<app:control>
  <app:draft>no</app:draft>
</app:control>
</entry>

記事のコンテンツ(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#">
    &lt;div class=&quot;section&quot;&gt;
    &lt;h4&gt;新しい本文&lt;/h4&gt;
  </hatena:formatted-content>
  <category term="新しいカテゴリ" />
  <app:control>
    <app:draft>no</app:draft>
  </app:control>
...
</entry>

編集に成功した場合,新しいentry要素のresponseが返る.

gist: はてなブログAPI 記事の編集 · GitHub