lisz-works

プログラミングと興味を貴方に

Python3→はてなフォトライフへ画像のアップロード

【スポンサーリンク】

Python

Python3から、はてなフォトライフに画像をアップロードする方法についてです。

前回コレに必要だと思い、OAuth認証を試していました。

www.lisz-works.com

しかしぼくの現在の頭では、OAuthでのアップロードができず……

Google先生に聞いたところ、WSSE*1でアップロードしているサンプルを見つけました。

ということで実験です。

フォトライフにアップロードするソース

ソースはコチラ。

元のサンプルから少し手を加えました。

# -*- coding: utf-8 -*-
from base64 import b64encode
from datetime import datetime
from hashlib import sha1
import random
import requests
import sys
from pathlib import Path

# ユーザ名とAPIキーの設定
username = 'UserName'
api_key  = 'ApiKey'

# WSSE
def wsse(username: str, api_key: str) ->str:
    created = datetime.now().isoformat() + "Z"
    b_nonce = sha1(str(random.random()).encode()).digest()
    b_digest = sha1(b_nonce + created.encode() + api_key.encode()).digest()
    print('--wsse--')
    print('Username= ' + username)
    print('Nonce= ' + b64encode(b_nonce).decode())
    print('Created= ' + created)
    print('')
    return f'UsernameToken Username="{username}", PasswordDigest="{b64encode(b_digest).decode()}", Nonce="{b64encode(b_nonce).decode()}", Created="{created}"'

# 送信データの作成
def create_data(file_name: str='./test.png') ->str:
    uploadData = b64encode(Path(file_name).read_bytes())
    return """
    <entry xmlns="http://purl.org/atom/ns#">
    <title>Sample</title>
    <content mode="base64" type="image/png">""" + uploadData.decode() + """</content>
    </entry>
    """

# アップロード
def post_hatena(data):
    headers = {'X-WSSE': wsse(username, api_key)}
    url = 'http://f.hatena.ne.jp/atom/post/'
    r = requests.post(url, data=data, headers=headers)
    try:
        r.raise_for_status()
    except:
        sys.stderr.write(f'Error!\nstatus_code: {r.status_code}\nmessage: {r.text}')
    print('--result--')
    print('url        = ' + r.url)
    print('status code= ' + str(r.status_code))
    print('hreader    = ' + str(r.headers))
    print('encoding   = ' + str(r.encoding))
    print('text       = ' + r.text)
    print('')

# メイン
def main():
    print('************************************\n')
    data = create_data()
    print('--XML--')
    print(data)
    print('')
    post_hatena(data)
    print('************************************\n')

if __name__ == '__main__':
    main()

フォトライフへのアップロードをしてみる

今回はこのようなPNGファイルを用意しました。

test.png

確認用に色々出力させているので、少し長いので閉じておきます。

出力結果を開く

(env) $ python upload.py
--XML--

    <entry xmlns="http://purl.org/atom/ns#">
    <title>Sample</title>
    <content mode="base64" type="image/png">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</content>
    </entry>


--wsse--
Username= liszworks
Nonce= 0koa8tPjdWDvU/TaB6r9fwdFiCc=
Created= 2019-05-30T13:44:24.624138Z

--result--
url        = http://f.hatena.ne.jp/atom/post/
status code= 201
hreader    = {'Date': 'Thu, 30 May 2019 04:44:28 GMT', 'Content-Type': 'application/x.atom+xml', 'Content-Length': '1181', 'Connection': 'keep-alive', 'Server': 'nginx', 'Set-Cookie': 'b=$1$sH5I4Fwq$wvLR3.4pCGcRCtcVBiJjQ0; expires=Wed, 25-May-2039 04:44:25 GMT; domain=hatena.ne.jp; path=/', 'X-Server': '(null)', 'Location': 'http://f.hatena.ne.jp/atom/edit/20190530134425', 'X-Framework': 'Hatena/2.0', 'X-PageMaker': 'Atompost'}
encoding   = None
text       = <?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://purl.org/atom/ns#" xmlns:hatena="http://www.hatena.ne.jp/info/xmlns#">
  <title>Sample</title>
  <link rel="alternate" type="text/html" href="http://f.hatena.ne.jp/liszworks/20190530134425"/>
  <link rel="service.edit" type="application/x.atom+xml" href="http://f.hatena.ne.jp/atom/edit/20190530134425" title="Sample"/>
  <issued>2019-05-30T13:44:25+09:00</issued>
  <author>
    <name>liszworks</name>
  </author>
  <generator url="http://f.hatena.ne.jp/" version="1.0">Hatena::Fotolife</generator>
  <dc:subject xmlns:dc="http://purl.org/dc/elements/1.1/">tmp</dc:subject>
  <id>tag:hatena.ne.jp,2005:fotolife-liszworks-20190530134425</id>
  <hatena:imageurl>https://cdn-ak.f.st-hatena.com/images/fotolife/l/liszworks/20190530/20190530134425.png</hatena:imageurl>
  <hatena:imageurlmedium>https://cdn-ak.f.st-hatena.com/images/fotolife/l/liszworks/20190530/20190530134425_120.jpg</hatena:imageurlmedium>
  <hatena:imageurlsmall>https://cdn-ak.f.st-hatena.com/images/fotolife/l/liszworks/20190530/20190530134425_m.jpg</hatena:imageurlsmall>
  <hatena:syntax>f:id:liszworks:20190530134425p:image</hatena:syntax>
</entry>


************************************


(env) $

ちゃんとアップロードされました!

アップロードされた

ユーザ名について

ログインに使用しているユーザ名です。

「なんだっけ……?」

という方は、はてなにログインすれば色んなところで見れると思います。

例えば、自分の「プロフィール」とか。

プロフィールでユーザ名確認

APIキーについて

はてなブログのダッシュボードから確認できます。

blog.hatena.ne.jp

ダッシュボードを開いて、「設定」→「詳細設定」と進みます。

ダッシュボード→設定→詳細設定

下の方に進んでいくと、「AtomPub」という項目に「APIキー」がありますね。

AtomPub→APIキー

コレです!

アップロード先フォルダを変更する

アップロード先のフォルダを変更したい場合は、送信するXMLに1要素追加してあげます。

コレです。

<generator>フォルダ名</generator>

例えば、「tmp」という名前のフォルダに毎回入れたい場合は、このように修正します。

def create_data(file_name: str='./test.png') ->str:
    uploadData = b64encode(Path(file_name).read_bytes())
    # <generator>は格納先フォルダ名
    return """
    <entry xmlns="http://purl.org/atom/ns#">
    <title>Sample</title>
    <content mode="base64" type="image/png">""" + uploadData.decode() + """</content>
    <generator>tmp</generator>
    </entry>
    """

この状態で実行すると、ちゃんとtmpフォルダにアップロードされました!

tmpフォルダにアップロード

参考

コチラを参考にしました。ありがとうございました!

teratail.com

neue.cc

あとがき

当初、自前でOAuthを使用してのアップロードをしようとしていましたが、結局うまく行かず……

結構頑張ったのですが、知識や実力が足りず、調べているうちに、このページにたどり付きました。

ひとまずやりたいことはできているので、コチラを使用していきたいと思います。

*1:はてなAPIは、大概OAuthかWSSEという2つの方法でAPI操作ができる模様。