Help us understand the problem. What is going on with this article?

Pythonの標準ライブラリのみでツイートしてみた

More than 1 year has passed since last update.

まとめ

Requestsを使わずurllib等のpython2.7の標準ライブラリのみでOAuth1.0の署名付きリクエストを送信する関数を書きました。この関数でTwitterのRestAPIを使いツイートしました。

なぜ実装した?

RequestsやTweepyなどは依存関係が複雑でGoogle App Engineに搭載できなかったから

にゃーんと呟く

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib2
import time, random
import hmac, hashlib, base64
from urllib import quote, urlencode

#リクエストを送信する関数
#   method: "POST" or "GET"
#   url: e.g. "https://www.w3.org/History/19921103-hypertext/hypertext/WWW/TheProject.html"
#   param: e.g. {"status":"にゃーん"}
#   header: e.g. {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64)"}
#
#   return: {"code":200,"body":"something"}
def request(method, url, param, header):
    method = method.upper()
    param = urlencode(param)
    if method == "POST":
        pass
    if method == "GET":
        url += "?" + param
        param = None
    try:
        req = urllib2.Request(url, param, header)
        handler = urllib2.urlopen(req)
        return {"body": handler.read(), "code": handler.getcode()}
    except urllib2.HTTPError as e:
        print(e.fp.read())

#OAuth1.0の署名付きリクエストを送信する関数
#   consumer_key: Consumer Key (API Key)
#   consumer_secret: Consumer Secret (API Secret)
#   access_token: Access Token
#   access_token_secret: Access Token Secret
#   method: e.g. "POST"
#   url: e.g. https://api.twitter.com/1.1/statuses/update.json
#   param: {"status":にゃーん}
#
#   return {"code":200,"body":"something"}
def oauth10(consumer_key, consumer_secret, access_token, access_token_secret, method, url, param):
    for k,v in param.items():
        if isinstance(v,unicode):
            v=v.encode("utf-8")
        param[k]=str(v)
    baseparam = {
        "oauth_token": access_token,
        "oauth_consumer_key": consumer_key,
        "oauth_signature_method": "HMAC-SHA1",
        "oauth_timestamp": str(int(time.time())),
        "oauth_nonce": str(random.getrandbits(64)),
        "oauth_version": "1.0"
    }
    signature = dict(baseparam)
    signature.update(param)
    signature = '&'.join('{0}={1}'.format(quote(key, ''), quote(signature[key], '~')) for key in sorted(signature))
    signature = ("{0}&{1}".format(consumer_secret, access_token_secret), '{0}&{1}&{2}'.format(method.upper(), quote(url, ''), quote(signature, '')))
    signature = base64.b64encode(hmac.new(signature[0], signature[1], hashlib.sha1).digest())
    header = dict(baseparam)
    header.update({"oauth_signature": signature})
    header = ",".join("{0}={1}".format(quote(k, ''), quote(header[k], '~')) for k in sorted(header))
    header = {"Authorization": 'OAuth {0}'.format(header)}
    return request(method, url, param, header)

consumer_key="ひみつ"
consumer_sec="ひみつ"
access_key="ひみつ"
access_sec="ひみつ"

r=oauth10(consumer_key,consumer_sec,access_key,access_sec,"POST","https://api.twitter.com/1.1/statuses/update.json",{"status":"にゃーん"})

print(r["code"])#200
print(r["body"])#{"created_at":"Sat May 19 06:42:23 +0000 2018","id":9977291606~~

大まかな手順

0.用意するもの

認証に必要な情報 説明
consumer_key Consumer Key (API Key)
consumer_secret Consumer Secret (API Secret)
access_token Access Token
access_token_secret Access Token Secret
method RESTAPIに指定されている送信方法。今回はPOST
url APIのURL "https://api.twitter.com/1.1/statuses/update.json"
param APIのパラメータ。今回はつぶやく内容を指定。{"status":"にゃーん"}

1.基本的なパラメータを集める

認証に必要な情報 説明
oauth_token アプリケーションの利用者を示す文字列
oauth_consumer_key アプリケーションを示す文字列
oauth_signature_method 署名に用いるハッシュ関数を示す文字列
oauth_timestamp UNIX時刻
oauth_nonce リクエストごとに固有の文字列。なんでもいい。
oauth_version OAuth1.0を使うので"1.0"
    baseparam = {
        "oauth_token": access_token,
        "oauth_consumer_key": consumer_key,
        "oauth_signature_method": "HMAC-SHA1",
        "oauth_timestamp": str(int(time.time())),
        "oauth_nonce": str(random.getrandbits(64)),
        "oauth_version": "1.0"
    }

2.APIのパラメーターを加える

signature = dict(baseparam)
signature.update(param)

3.合わさったパラメータを文字列に変える

全てのキーと値はパーセントエンコードする。
キーをABC順に並べる
a=nurupo&b=ga みたいに連結する

signature = '&'.join('{0}={1}'.format(quote(key, ''), quote(signature[key], '~')) for key in sorted(signature))

4.ハッシュ関数とbase64で変換した署名を作成する

method&url&さっきの連結した文字列
というメッセージを
consumer_secret&access_token_secretで署名する

signature = ("{0}&{1}".format(consumer_secret, access_token_secret), '{0}&{1}&{2}'.format(method.upper(), quote(url, ''), quote(signature, '')))
signature = base64.b64encode(hmac.new(signature[0], signature[1], hashlib.sha1).digest())

5. 1.のパラメータと署名を合わせてヘッダに追加

    header = dict(baseparam)
    header.update({"oauth_signature": signature})
    header = ",".join("{0}={1}".format(quote(k, ''), quote(header[k], '~')) for k in sorted(header))
    header = {"Authorization": 'OAuth {0}'.format(header)}

6. 送信

#リクエストを送信する関数
#   method: "POST" or "GET"
#   url: e.g. "https://www.w3.org/History/19921103-hypertext/hypertext/WWW/TheProject.html"
#   param: e.g. {"status":"にゃーん"}
#   header: e.g. {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64)"}
#
#   return: {"code":200,"body":"something"}
def request(method, url, param, header):
    method = method.upper()
    param = urlencode(param)
    if method == "POST":
        pass
    if method == "GET":
        url += "?" + param
        param = None
    try:
        req = urllib2.Request(url, param, header)
        handler = urllib2.urlopen(req)
        return {"body": handler.read(), "code": handler.getcode()}
    except urllib2.HTTPError as e:
        print(e.fp.read())
request(method, url, param, header)

APIのサーバー側ではmethod,URL,Authorization,paramから同様にハッシュを計算し一致を確かめるのだろう

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away