Published On: 2016-01-21|Last Updated: 2018-06-15|Categories: Python|Tags: , , , |

レスポンスのフック

>>> import requests
>>> def on_response(response:requests.models.Response, **send_args):
...     print(response, send_args)

>>> requests.get('https://httpbin.org/get', hooks={'response': on_response})
<Response [200]> {'timeout': None, 'cert': None, 'stream': False, 'verify': True, 'proxies': OrderedDict()}
<Response [200]>
>>> requests.get('https://httpbin.org/redirect/1', hooks={'response': on_response})
<Response [302]> {'timeout': None, 'cert': None, 'stream': False, 'verify': True, 'proxies': OrderedDict()}
<Response [200]> {'timeout': None, 'stream': False, 'proxies': OrderedDict(), 'cert': None, 'verify': True}
<Response [200]>
>>> requests.get('https://httpbin.org/status/404', hooks={'response': on_response})
<Response [404]> {'timeout': None, 'cert': None, 'stream': False, 'verify': True, 'proxies': OrderedDict()}
<Response [404]>
>>> requests.get('https://httpbin.org/status/503', hooks={'response': on_response})
<Response [503]> {'timeout': None, 'cert': None, 'stream': False, 'verify': True, 'proxies': OrderedDict()}
<Response [503]>

リダイレクトされたときも呼び出されるので便利です。ここで HTML パーサーなどを呼び出して response オブジェクトの属性に設定したりします。大雑把ですが 50x なときなどに time.sleep() して回避するのも楽です。

セッションを使うと簡単にデフォルトパラメーターとして設定できます。

>>> with requests.Session() as session:
...     session.hooks['response'] = on_response
...     session.get('https://httpbin.org/get')

 
<Response [200]> {'timeout': None, 'cert': None, 'stream': False, 'verify': True, 'proxies': OrderedDict()}
<Response [200]>

30x による自動リダイレクトを無効化する

>>> requests.get('https://httpbin.org/redirect/1', hooks={'response': on_response}, allow_redirects=False)
<Response [302]> {'timeout': None, 'cert': None, 'stream': False, 'verify': True, 'proxies': OrderedDict()}
<Response [302]>

v2.9.1 ではデフォルトパラメーターとして設定することはできないようです。

リトライパラメーターをカスタマイズする

正気とは思えない設定の http サーバーを相手するときに特定のステータスコードでリトライさせるようにできます。

>>> with requests.Session() as session:
...     http_adapter = requests.adapters.HTTPAdapter(
...         max_retries=requests.packages.urllib3.util.Retry(total=10, status_forcelist=[500, 404, 302]),
...     )
...     session.mount('https://', http_adapter)
...     session.mount('http://', http_adapter)

mount() の第一パラメーター prefix は内部で長い順にソートされて adapter の検索に使用されます。

PyQuery で iterate

for x in pq(‘selector’) すると x が lxml.html.HtmlElement になってとても面倒なので、items() か each() を使うといいようです。

requests + PyQuery

こんな感じで適当です。

def process_response(response, **send_args):
    import pyquery
    if response.headers.get('Content-Type').startswith('text/html')
        response.doc = pyquery.PyQuery(response.content)
        response.doc.make_links_absolute(response.url)

with requests.Session() as session:
    session.hooks['response'] = process_response

関連