【今更】Python初心者がrequestsを使ってHTTPクライアントを実装してみた（ヘッダ操作込）


今更感満載のネタでリソースを使ってしまって申し訳ありませんQiit様。

会社の業務系でWebAPIをいろいろ変更しながら叩く必要があり、じゃあPythonでもやってみるかーと思いました。以前にsocketオブジェクトを使った通信は少しやったので、まー何とかなるだろうと。

で、socketベースでHTTPクライアント作りつつ、いろいろググレカスしてみると、どうもそういうのは違うっぽいと知りました。更に調べてみますと [requests](https://pypi.python.org/pypi/requests/2.7.0)を利用するのが良いのかなーという結論に至りました。

そこで、とりあえずGETするだけの物を作ってみました。

# 特徴

 * Pythonやる際に良く使う要素を用もなくブッ込んでみました。
 * なので普段Pythonやらない人もこれ見ると、基本的な書き方を思い出せると思います（こんなん私しか必要ないという気もしますが、要は備忘で書いたということで）
 * URL指定は　http://　 系が無くてもある程度対応可能にしてみました。
 * ヘッダ操作（入れ込み、受け取り）も分かるように記述してみました。
 * ボディは指定されたファイルに入れるようにしてあります。
 * HTTP処理に関してexception処理を入れてますので、通信失敗込みで連続処理とかやりたい人には参考になるかもしれません。。

# requests について

### requestに関する情報

* まず[本家](https://pypi.python.org/pypi/requests/2.7.0)が大変参考になります。
* ↑にもありますが。[ドキュメント](http://docs.python-requests.org/en/master/)でいろいろ確認も出来ます。

今回ぐらいのケースなら上記のドキュメントに大体記載されてます。

### セットアップ

requestsはpipでインストールが必要です。私の場合はWindows10のCygwin(32bit)で確認をしました。

#### (参考）Cygwinでpipを使う

[このサイト](http://chiranoura.nobody.jp/articles/2014-05-27_01_how_to_install_pip_and_setuptools/)の情報が大変役に立ちました。tanao様ありがとうございます。

上のサイトを見ますと、Cygwinでのpip周りの扱いはいろいろあったようですが、2016/7/18現在、Windows10／Cygwin32bit、Python2.7という環境であれば以下でいけるみたいです。

* Cygwinで追加するモジュールは上記記事のママ
* 2.7であれば、easy_install-2.7が使えますので、上記記事を参考にpipをインストールします。
* ついで定番の ```pip install requests``` で。

# HTTPクライアントクラスのコード

とりあえずクラスの書き方を思い出すためという情けない理由も込みでクラスにしてみました。

```py
import requests

class SampleHTTPclient:

    def __init__(self, url):
        self.url = url
        self.protocol = ""
        self.error_message = ""
        self.request_headers = None
        self.response_headers = None
        self.status_code = None

    def check_protocol(self):
        # ex
        # if self.protocol = "http://" , self.uri = "hoge.org":
        #  self.url = "http://hoge.org"
        if self.url.find("://") == -1 and len(self.protocol) > 3:
            self.url = self.protocol + self.url

    def set_protocol(self, protocol):
        self.protocol = protocol
        self.check_protocol()

    def set_headers(self, headers):
        self.request_headers = headers

    def get(self, fp):
        try:
            if self.request_headers is None:
                response = requests.get(self.url)
            else:
                response = requests.get(self.url, headers=self.request_headers)
            fp.write(response.content)
        except Exception as ex:
            self.error_message = str(ex)
            return False
        else:
            self.status_code = response.status_code
            self.response_headers = response.headers
            return True

    def get_code(self):
        return self.status_code

    def get_headers(self):
        return self.response_headers

    def get_error_message(self):
        return self.error_message

```

実体は２行なのですが・・ゴチャゴチャつけてしまいました。。

使い方は以下のイメージです。

1. まずはURLでインスタンスを生成
2. URLのデータに　http://　とか無い場合なら __set_protocol__で使うプロトコルを指定出来ます（※１）。
3. 追加するヘッダがあれば、__set_headers__で[requestsの仕様](http://docs.python-requests.org/en/master/user/quickstart/#custom-headers)に従ったdictを入れて下さい。下にサンプルコードもありますので、そっちを参考にしてもいいかもです。
4. openを使って書き込みモードでファイルを開けておきます。
5. それを使って__get__を呼び出します。boolで成功(True)、失敗(False)が分かります
6. 通信が成功した場合はbodyが指定されたファイルに入っています（※２）。  
7. 通信に成功した場合、__get_code__でHTTPステータスコードを取得できます。
8. 通信に成功した場合、__get_headers__で[requestの仕様で定められたdictの形のレスポンスヘッダ](http://docs.python-requests.org/en/master/user/quickstart/#response-headers)を取得できます。
9. 失敗した場合は、__get_error_message__ でエラーメッセージを取得できます。

※１ http://　などのプロコトル込みのURL文字列を指定してSampleHTTPclientを生成した後、set_protcolでプロトコルの文字列を指定しても問題はありません。この場合はset_protocolの設定が無視されます。

※２ 例えばHTTP 404のエラーとかは、get関数的には成功の扱いになります。そのためget_codeなどで通信結果を合わせて確認して下さい。また現状ではこの場合でもボディをファイルに書き込む仕様となっています。それが不都合な方は、お手数おかけして申し訳ございませんが改造願います。

# 実際にGETするコード例

上の説明をコード化して、SampleHTTPclientを使った例を以下に示します。

```py
print "sample httpc program."

url = "<<ここに取得するリソースのURLを>>"
headers = {"X-gdaigo_para1": "fujiko", "X-gdaigo-para2":"7890"}
filename = "test.html"
print url, " -> " , filename

httpc = SampleHTTPclient(url);
httpc.set_protocol("http://")
httpc.set_headers(headers)
fp = open(filename, "w")
success = httpc.get(fp)
fp.close()
if success == True:
    headers = httpc.get_headers()
    print "response code=", httpc.get_code()
    for key, value in headers.iteritems():
        print key, ":" , value
    print "success."
else:
    print "httpc:read error\n" , httpc.get_error_message()
    print "error."

print "complete."

```

上記の例はURLを元にリソースを取得して、そのデータを test.html　として取得する例です。（無意味ではありますが）ヘッダを設定し、取得結果の表示も合わせて行っています。GETはこんな感じで大丈夫そうです。

という事でrequestsを使う事で割とあっさり出来ました。

# ライセンス

以下使わせて戴きました。素晴らしいソフトウェアを提供して下さり、ありがとうございます。

* （一応書きます…）上記のコードはパブリックドメインとします。当然ですが使用した際の損害は誰も請け負ってくれません。そこだけ注意で。
* Python自体は[PSF (Python Software Foundation)ライセンス](https://www.python.org/download/releases/2.5/license/)です。
* ↑の情報は[WikipediaのPython](https://ja.wikipedia.org/wiki/Python#.E3.83.A9.E3.82.A4.E3.82.BB.E3.83.B3.E3.82.B9)がソースです。
* requestsは[ここ](https://pypi.python.org/pypi/requests/2.7.0)にある通り Apache2 Licenseです。

以上です。