urllib.request で十分に通信ができてしまうので、requestsわざわざ使う必要もないかなーと思って(実際Lambdaにあげようとしているのでライブラリは少ないほうが助かる)、oauth_signatureを自作していたのですが、まあ結構辛かったので、メモです。
コード辺
本当に大変だった。概念的には下記みたいな感じ。リクエスト部分はまた今度。
ACCESS_POINT = ( "https://blog.hatena.ne.jp/MireiMixin/namonakimichi.hatenablog.com/atom/entry" ) class MethodType(Flag): GET = auto() POST = auto() PUT = auto() DELETE = auto() WITH_BODY = POST | PUT | DELETE METHOD_TYPE_STRING = { MethodType.GET: "GET", MethodType.POST: "POST", MethodType.PUT: "PUT", MethodType.DELETE: "DELETE", } RequestParams = NewType("RequestParams", Union[str, int]) RequestDict = NewType("RequestDict", Dict[str, RequestParams]) def load_access_keys() -> Dict[str, str]: keys = ("consumer_key", "consumer_secret_key", "token_key", "token_secret_key") return {key: os.getenv(key.upper()) for key in keys} def create_auth_signature(url, method, baseparam, consumer_keys): signature = dict(baseparam) signature = "&".join( "{0}={1}".format(quote(key, "~"), quote(signature[key], "~")) for key in sorted(signature) ) signature = ( "{0}&{1}".format( quote(consumer_keys["consumer_secret_key"], "~"), quote(consumer_keys["token_secret_key"], "~"), ), "{0}&{1}&{2}".format( quote(METHOD_TYPE_STRING[method].upper()), quote(url, "~"), quote(signature, "~"), ), ) key_utf, text_utf = [s.encode("utf-8") for s in signature] signature = hmac.new(key_utf, text_utf, hashlib.sha1) signature = binascii.b2a_base64(signature.digest())[:-1].decode("utf-8") return signature def create_oauth_header(url, method): consumer_keys = load_access_keys() baseparam = { "oauth_token": consumer_keys["token_key"], "oauth_consumer_key": consumer_keys["consumer_key"], "oauth_signature_method": "HMAC-SHA1", "oauth_timestamp": str(int(time.time())), "oauth_nonce": str(random.getrandbits(64)), "oauth_version": "1.0", } oauth_signature = create_auth_signature(url, method, baseparam, consumer_keys) header = dict(baseparam) header.update({"oauth_signature": oauth_signature}) header = ",".join( "{0}={1}".format(quote(k, "~"), quote(header[k], "~")) for k in sorted(header) ) return { "Authorization": "OAuth {0}".format(header), "Content-Type": "application/xml; charset=utf-8", } def auth_request(url, method, body): header = create_oauth_header(url, method) return request( url=url, headers=RequestDict(header), with_encode=True, body=RequestDict(body), )
参考にしたページ
- Pythonの標準ライブラリのみでツイートしてみた - Qiita
- RFC 5849 - The OAuth 1.0 Protocol
- OAuth1.0の署名(Signature)をPythonで生成してみた - pshikoro's blog
- OAuth1.0:リクエストの署名 - Yahoo!デベロッパーネットワーク
- PythonでURLエンコード・デコード(urllib.parse.quote, unquote) | note.nkmk.me
- はてなブログAPIを使った自動投稿プログラムの改良 - 自動観察日記
- Python の Union と Optional ってなに? | 民主主義に乾杯
- requests/requests-oauthlib: OAuthlib support for Python-Requests!
まとめ
リクエスト部分はまた今度で。。。はてなブログAPIを叩くためのAWS LambdaをServerless Frameworkで建てる、みたいな記事を書く予定。
二回もやりたくないかなこれは・・・ 最終的にはRequestsの中身をかなり読んで、それを参考に実装しました。Python3の実装がその辺に転がっていなかったのも結構辛かったです。
とりあえずもう少し上は整理される予定ですが、一旦記事にしとかないとどういうロジックだったか忘れるのでブログにメモだけ残しておきます。