[Django/Python]requestsモジュールでよく使うところ。

# はじめに
業務でAPI連携を設計から任された時に使ったrequestsモジュールについて自分の議事録としてまとめようと思います。個人の感想ですが、直感的でわかりやすく多機能なモジュールなのですごくありがたかったです。ただ一点、リトライの処理がurllib3の方に比べて使いにくかったなというのが残念に感じました。

公式のクイックスタートがすごいわかりやすかったので、そちらを一通りやってみるのもおすすめです。
## バージョン
```bash:version
% pip list | grep requests #インストールしてない人は pip install requestsを実行
requests           2.28.1
% python -V
Python 3.9.6
```
## requestsモジュールができること
できることはたくさんあるのですが、自分がよく使われるだろうと思った機能を6つ紹介させて頂きます。

### 1. リクエストの生成
たった２行であるサイトの情報を取得することができます。
今回は`get`を使っていますが、`post`,`put`,`delete`,`head`,`options`などもちゃんとあります。
```python:
import requests
r = requests.get("http://httpbin.org/get")
print(r.text)

#”http://httpbin.org/get"を利用しているのでレスポンスはリクエストと同じ内容であることに注意。
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.28.1", 
    "X-Amzn-Trace-Id": "Root=1-63739e5e-3012837e2ed8d5c73d021077"
  }, 
  "origin": "133.32.130.44", 
  "url": "http://httpbin.org/get"
}

```

ちなみに　"http://httpbin.org/”　
このサイトは自分がどんなリクエストを送ったかわかりやすくしてくるのですごい便利です。

https://qiita.com/sameyasu/items/adacceb8a1bee893599b
### 2. クエリパラメータの作成
クエリパラメータをURLに追加したい時ありますよね。
for分で`?,&,=`などを一個一個URLに追加することもできますが、requestsを使えば一発で解決です。今回は例としてdogとcatをクエリパラメータに追加してみます。
やり方は簡単で、辞書にキーと値を設定してそれを`params`を引数にして渡してあげるだけです。
```python:
params = {
    "animal1":"dog",
    "animal2":"cat"
    }
r = requests.get("http://httpbin.org/get",params=params)

print(r.text)

#レスポンス
{
  "args": {
    "animal1": "dog", 
    "animal2": "cat"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.28.1", 
    "X-Amzn-Trace-Id": "Root=1-6373a04c-2e523f144af4b13d087d28e4"
  }, 
  "origin": "133.32.130.44", 
  "url": "http://httpbin.org/get?animal1=dog&animal2=cat"
}
```
`http://httpbin.org/get?animal1=dog&animal2=cat`
ちゃんとURLにクエリパラメータが付け加えられていますね。
今回の注意点としては、引数にparamsと明示することです。明示しないと`data=params`として受けたられクエリパラメータではなくdata、つまりbodyの方に含まれることになります。
### 3. ヘッダーの内容を指定
これもクエリパラメータの時と同じで、HTTPヘッダーを追加したい場合は、`headers`パラメーターにdictを渡すだけです。
今まではなかった"Content-Type": "text/plain"が追加されていますね。こちらから送ったheadersの"Content-Type"が追加されていることが確認できていればOKです。
```python:
headers = {"content-type":"text/plain"}
r = requests.get("http://httpbin.org/get",headers=headers)

print(r.text)

#レスポンス
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Type": "text/plain", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.28.1", 
    "X-Amzn-Trace-Id": "Root=1-6373a362-3dee925f1c112b120353f8a1"
  }, 
  "origin": "133.32.130.44", 
  "url": "http://httpbin.org/get"
}
```
### 4.1 リクエスト本文(data)をPOST
これも辞書で書いて、引数に`data=`をして渡してあげれば完了です。
辞書のdataは自動的にエンコードされるので、ありがたいですよね。
画像などのbinaryを送信したい場合には引数に`files`を使うのでよかったら覚えておいてください。
```python:
data = {'key1': 'value1', 'key2': 'value2'}
r = requests.post("http://httpbin.org/post", data=data)

print(r.text)
#レスポンス
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "key1": "value1", 
    "key2": "value2"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "23", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.28.1", 
    "X-Amzn-Trace-Id": "Root=1-6373a69c-5e870e1a252e6f1d52449701"
  }, 
  "json": null, 
  "origin": "133.32.130.44", 
  "url": "http://httpbin.org/post"
}
```
formに辞書で指定したキーと値が入っていることが確認できます。

### 4.2 jsonを使ってPOSTする場合
API通信をする際にはjsonを使われることが多いので、jsonでリクエストをする場合についても残しておきます。

```python
import requests
import json

headers = {"Content-Type":"application/json"}
data = {'key1': 'value1', 'key2': 'value2'}
r = requests.post("http://httpbin.org/post", data=json.dumps(data), headers=headers)
print(r.text)
#レスポンス
{
  "args": {}, 
  "data": "{\"key1\": \"value1\", \"key2\": \"value2\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "36", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.28.1", 
    "X-Amzn-Trace-Id": "Root=1-6373a898-3ebe70d46336d6cc65ecf279"
  }, 
  "json": {
    "key1": "value1", 
    "key2": "value2"
  }, 
  "origin": "133.32.130.44", 
  "url": "http://httpbin.org/post"
}
```
先ほどはformにdataが入っていましたが、jsonの時は"data"に入っていますね。
jsonでレスポンスが帰ってくる場合、`r.json()`とすれば辞書型に変換してくれるので、こちらも便利です。ただ、jsonのデコードに失敗したらNoneとなって返ってくようになってます。
### 5. コードが200番以外の時、エラーを起こす。
パラメータの不足やネットワークの問題でうまく通信ができない可能性があるので、現場ではエラーハンドリングを必ず行うと思います。その際使うのが、`raise_for_status()`です。これは、200以外のレスポンスが返ってきたときに例外を発生させます。その例外発生を契機に、リトライを行ったり待機を行ったり分岐をさせていくのに使われることが多いのかなと思います。
```python
r = requests.get('http://httpbin.org/status/404')
r.raise_for_status()

#例外
Traceback (most recent call last):
  File "PATH", line 5, in <module>
    r.raise_for_status()
  File "PATH", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: NOT FOUND for url: http://httpbin.org/status/404
```
正常系ならOKですが、(準)異常系だった場合は`except`を使ってうまくエラーハンドリングを行う必要があります。
```python
try:
    r = requests.get('http://httpbin.org/status/404')
    r.raise_for_status()
    return r
except Exception as e:
    print(e) #例外処理を記述していく
```
### 6. セッションの利用
API連携の場合、アクセストークンで認証されることが多いですが、ログインを必要とするサイトなどはセッションを使うことが一般的ですよね。
Sessionモードを有効にするのも簡単に実装できます。まずSessionオブジェクトからインスタンスを作成して、そのインスタンスを使って今までのように`get`,`post`などでリクエストを送ります。ログイン時にはログイン情報を送るので初めのリクエストは`post`メソッドになることが多いと思いますが、今回はhttpbin.orgを使っているので`get`としています。紛らわしくてすいません。
```python
s = requests.Session() #Sessionインスタンス作成
r = s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
print(r)
r = s.get("http://httpbin.org/cookies")
print(r)

#レスポンス
{
  "cookies": {
    "sessioncookie": "123456789"
  }
}

{
  "cookies": {
    "sessioncookie": "123456789"
  }
}
```
# おわりに
requestsはスクレイピングにもAPI連携にも使えるので、覚えておいて損はないと思います。
もし修正などがあれば教えていただけるとすごくありがたいです。
読んでくださりありがとうございました。

# 参考記事
https://requests-docs-ja.readthedocs.io/en/latest/user/quickstart/

https://qiita.com/mSpring/items/257adb27d9170da3b372