перейти с urllib2 на pycurl

У меня есть фрагмент кода, показанный ниже, который использует urllib2. Я пытаюсь преобразовать его в pycurl, чтобы воспользоваться поддержкой прокси-сервера pycurl. Преобразованный код pycurl отображается после исходного кода. Я хочу знать, как изменить urllib.urlopen(req).read() на что-то похожее в pycurl.. может быть, используя что-то вроде strinIO?

код urllib2:

URL = 'URL'
UN = 'UN'
PWD = 'PWD'
HEADERS = { 'Accept': 'application/json',
            'Connection': 'Keep-Alive',
            'Accept-Encoding' : 'gzip',
            'Authorization' : 'Basic %s' % base64.encodestring('%s:%s' % (UN, PWD))  }
req = urllib2.Request(URL, headers=HEADERS)
    response = urllib2.urlopen(req, timeout=(KEEP_ALIVE))
    # header -  print response.info()
    decompressor = zlib.decompressobj(16+zlib.MAX_WBITS)
    remainder = ''
    while True:
        tmp = decompressor.decompress(response.read(CHUNKSIZE))

преобразование pycurl с поддержкой прокси:

URL = 'URL'
UN = 'UN'
PWD = 'PWD'
HEADERS = [ 'Accept : application/json',
            'Connection : Keep-Alive',
            'Accept-Encoding : gzip',
            'Authorization : Basic %s' % base64.encodestring('%s:%s' % (UN, PWD))  ]
req = pycurl.Curl()
    req.setopt(pycurl.CONNECTTIMEOUT,KEEP_ALIVE)
    req.setopt(pycurl.HTTPHEADER, HEADERS)
    req.setopt(pycurl.TIMEOUT, 1+KEEP_ALIVE)
    req.setopt(pycurl.PROXY, 'http://my-proxy')
    req.setopt(pycurl.PROXYPORT, 8080)
    req.setopt(pycurl.PROXYUSERPWD, "proxy_access_user : proxy_access_password")
    req.setopt(pycurl.URL , URL)
    response = req.perform()
    decompressor = zlib.decompressobj(16+zlib.MAX_WBITS)
    remainder = ''
    while True:
        tmp = decompressor.decompress(urllib2.urlopen(req).read(CHUNKSIZE))

заранее спасибо.


person tkyass    schedule 07.10.2013    source источник
comment
@abarnert спасибо .. Я отредактирую вопрос.   -  person tkyass    schedule 07.10.2013


Ответы (1)


В отличие от urllib2, который возвращает объект, который вы можете использовать для получения данных, curl требует, чтобы вы передали ему объект, который он может использовать для хранения данных.

Простой способ сделать это, используемый в большинстве примеров, — передать файловый объект как WRITEDATA. Вы можете подумать, что можете просто передать здесь StringIO, например:

# ...
s = StringIO.StringIO()
req.setopt(pycurl.WRITEDATA, s)
req.perform()
data = s.getvalue()

К сожалению, это не сработает, так как файловый объект должен быть реальным файлом (или, по крайней мере, чем-то с файловым дескриптором C-уровня), а StringIO не подходит.


Конечно, вы можете использовать NamedTemporaryFile, но если вы предпочитаете хранить файл в памяти — или, лучше, не хранить его в памяти или на диске, а просто обрабатывать на лету — это не поможет.


Решение состоит в том, чтобы вместо этого использовать параметр WRITEFUNCTION:

s = StringIO.StringIO()
req.setopt(pycurl.WRITEFUNCTION, s.write)
req.perform()
data = s.getvalue()

Как видите, вы можете использовать для этого StringIO, если хотите — на самом деле это именно то, что curl объектная документация из pycurl делает, но на самом деле это не слишком сильно упрощает работу по сравнению с любым другим способом накопления строк (например, помещением их в список и ''.join-обработкой их или даже просто конкатенацией). их на нить).

Обратите внимание, что я ссылаюсь на документацию уровня C libcurl, а не на документацию pycurl, потому что документация pycurl в основном просто говорит: «FOO делает то же самое, что и CURLOPT_FOO» (даже если есть есть различия, например тот факт, что ваш WRITEFUNCTION не получает параметры size, nmemb и userdata).


Что делать, если вы хотите передавать данные на лету? Просто используйте WRITEFUNCTION, который накапливает и обрабатывает данные на лету. Вы не будете сами писать цикл, но curl будет выполнять внутренний цикл и управлять процессом. Например:

z = zlib.decompressobj()
s = []
def handle(chunk):
    s.append(z.decompress(chunk))
    return len(chunk)
req.setopt(pycurl.WRITEFUNCTION, handle)
req.perform()
s.append(z.flush())
data = ''.join(s)

curl будет вызывать вашу функцию один раз для каждого фрагмента данных, которые она извлекает, поэтому весь цикл происходит внутри этого вызова req.perform(). (Он также может вызвать его снова с 0 байтами в конце, поэтому убедитесь, что ваша функция обратного вызова может это обработать. Я думаю, что z.decompress может, но вы можете проверить это.)

Есть способы ограничить размер каждой записи, прервать загрузку в середине, получить заголовок как часть записи, а не отдельно и т. д., но обычно вам не нужно их трогать.

person abarnert    schedule 07.10.2013