Python3から、はてなフォトライフに画像をアップロードする方法についてです。
前回コレに必要だと思い、OAuth認証を試していました。
しかしぼくの現在の頭では、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ファイルを用意しました。
確認用に色々出力させているので、少し長いので閉じておきます。
出力結果を開く
(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キーについて
はてなブログのダッシュボードから確認できます。
ダッシュボードを開いて、「設定」→「詳細設定」と進みます。
下の方に進んでいくと、「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フォルダにアップロードされました!
参考
コチラを参考にしました。ありがとうございました!
あとがき
当初、自前でOAuthを使用してのアップロードをしようとしていましたが、結局うまく行かず……
結構頑張ったのですが、知識や実力が足りず、調べているうちに、このページにたどり付きました。
ひとまずやりたいことはできているので、コチラを使用していきたいと思います。
*1:はてなAPIは、大概OAuthかWSSEという2つの方法でAPI操作ができる模様。