От сессии до сессии

Помимо параметра headers, о котором шла речь в предыдущей заметке, метод requests.get предоставляет много других полезных настроек 1. При работе с одним сайтом эти настройки неудобно задавать при каждом запросе. В библиотеке Requests имеется класс Session. Экземпляр сессии с нужными параметрами создается один раз и затем многократно используется.

Фабрика сессий

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import requests

DEFAULT_HEADERS = {
    'User-Agent'       : 'Mozilla/5.0 (Windows NT 6.1; rv:27.3) Gecko/20130101 Firefox/27.3',
    'Accept'           : 'text/html',
    'Cache-Control'    : 'max-age=0',
}

def create_session(req_headers=None, **kwargs):
    '''
    Return requests.Session object.

    req_headers - custom headers persistent across requests
    kwargs - optional parameters: timeout, max_redirects and proxies.
    '''
    headers = dict(DEFAULT_HEADERS)
    if not req_headers is None:
        headers.update(req_headers)

    s = requests.Session()
    s.headers.update(headers)

    timeout = kwargs.get('timeout')
    if timeout:
        s.timeout = timeout

    max_redirects = kwargs.get('max_redirects')
    if max_redirects:
        s.max_redirects = max_redirects

    proxies = kwargs.get('proxies')
    if proxies:
        s.proxies = proxies

    return s

if __name__ == '__main__':
    import sys

    def print_dict(title, d):
        print title
        for k, v in d.items():
            print '%s: %s' % (k, v)

    url = sys.argv[1] if len(sys.argv) > 1 else 'http://google.com/'
    session = create_session()
    r = session.get(url)
    print '<%s - %s> in %s seconds' % (r.status_code, r.reason, r.elapsed.total_seconds())
    print
    print 'Initial URL: %s' % url
    print 'Final URL: %s' % r.url
    print 'Redirects count: %d' % len(r.history)
    print
    print_dict('Request headers:', r.request.headers)
    print
    print_dict('Response headers:', r.headers)

Функция-"фабрика" create_session возвращает экземпляр сессии. По умолчанию в качестве заголовков используются DEFAULT_HEADERS. Они будут заменены или дополнены другими параметры, если при вызове функции задать иное. В функцию также можно передать следующие параметры:

  • timeout - таймаут в секундах; есть возможность проставить разные значения для разных этапов запроса, но это уже тонкости;
  • max_redirects - максимальное количество перенаправлений. Паук (как, впрочем, и браузер) может оказаться в ситуации посетителя бюрократического учреждения, когда Иван Иванович отсылает его к Петру Семеновичу, тот — к Федору Васильевичу, а последний — снова к Ивану Ивановичу — и так до бесконечности. В какой-то момент надо сказать: "С меня хватит!";
  • proxies - адреса прокси (о них расскажу отдельно)

Результат выглядит примерно так:

equinox:crawlers_info sergey$ ./scripts/session.py 
<200 - OK> in 0.336517 seconds

Initial URL: http://google.com/
Final URL: https://www.google.ru/?gfe_rd=cr&ei=lW_9VcKgOKWdwAPwsa_ADQ&gws_rd=ssl
Redirects count: 2

Request headers:
Accept-Encoding: gzip, deflate
Accept: text/html
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:27.3) Gecko/20130101 Firefox/27.3
Connection: keep-alive
Cookie: PREF=ID=1111111111111111:FF=0:NW=1:TM=1442672534:LM=1442672534:V=1:S=mx7_K_tx68NCeZ3M; NID=71=JcEkjBqTJthStphVUwWVU99NRnrkcNCYClK1pqU5LD_Iv6y7Awxrcnf_bAVdvwAmP0ujUs2ToFdq_oTisHoSjL6YEqo46pZ_LCEKsVJkWQlyQL0OrKS_CjIe-oI2F6A0
Cache-Control: max-age=0

Response headers:
alternate-protocol: 443:quic,p=1
x-xss-protection: 1; mode=block
content-encoding: gzip
transfer-encoding: chunked
set-cookie: NID=71=RzkEX9dtaGCO1SHBhTUBEkGp-WJmFKZOKwfAvCwc5wyxKHteyLBwMTSKisWFLuzCIyeoYtdWK5n6wHKzDxVxxaJth_I72HcV-HSoVR5OgVF0H2_Gwh9TaVJWCNnT1WXPGPiJsw; expires=Sun, 20-Mar-2016 14:22:14 GMT; path=/; domain=.google.ru; HttpOnly
expires: -1
server: gws
cache-control: private, max-age=0
date: Sat, 19 Sep 2015 14:22:14 GMT
p3p: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
alt-svc: quic=":443"; p="1"; ma=604800
content-type: text/html; charset=UTF-8
x-frame-options: SAMEORIGIN

Вывод демонстрирует некоторые полезные свойства класса requests.Response.

  1. r.status_code возвращает код ответа, r.reason — текстовое пояснение к нему (в нашем случае: "OK"), а r.elapsed — время, потребовавшееся на выполнение запроса (объект datetime.timedelta).

  2. r.url представляет адрес, с которого получен контент. В случае редиректов он не совпадает с исходным. Мы запросили http://google.com, но сервер перенаправил нас на http://google.ru. Перенаправлений было 2 (весь маршрут можно узнать по массиву r.history).

  3. Мы также видим, что произошел обмен cookies между клиентом и сервером.

Другие подробности опустим.

Если в строке 49, при вызове функции, поставить более жесткое ограничение на количество перенаправлений, скажем:

session = create_session(max_redirects=1)

то возникнет исключительная ситуация:

requests.exceptions.TooManyRedirects: Exceeded 1 redirects.

social

Яндекс.Метрика