1

编写一个 Python 脚本,该脚本使用Requests lib 来触发对远程 Web 服务的请求。这是我的代码(test.py):

import logging.config
from requests import Request, Session

logging.config.fileConfig('../../resources/logging.conf')
logr = logging.getLogger('pyLog')
url = 'https://158.74.36.11:7443/hqu/hqapi1/user/get.hqu'
token01 = 'hqstatus_python'
token02 = 'ytJFRyV7g'
response_length = 351

def main():
    try:
        logr.info('start SO example')

        s = Session()
        prepped = Request('GET', url, auth=(token01, token02), params={'name': token01}).prepare()
        response = s.send(prepped, stream=True, verify=False)

        logr.info('status: ' + str(response.status_code))
        logr.info('elapsed: ' + str(response.elapsed))
        logr.info('headers: ' + str(response.headers))
        logr.info('content: ' + response.raw.read(response_length).decode())


    except Exception: 
        logr.exception("Exception")
    finally:
        logr.info('stop')


if __name__ == '__main__':
    main()

运行此命令时,我得到以下成功输出:

INFO test - start SO example
INFO test - status: 200
INFO test - elapsed: 0:00:00.532053
INFO test - headers: CaseInsensitiveDict({'server': 'Apache-Coyote/1.1', 'set-cookie': 'JSESSIONID=8F87A69FB2B92F3ADB7F8A73E587A10C; Path=/; Secure; HttpOnly', 'content-type': 'text/xml;charset=UTF-8', 'transfer-encoding': 'chunked', 'date': 'Wed, 18 Sep 2013 06:34:28 GMT'})
INFO test - content: <?xml version="1.0" encoding="utf-8"?>
<UserResponse><Status>Success</Status> .... </UserResponse>
INFO test - stop

如您所见,我需要将这个奇怪的变量“response_length”传递给响应对象(可选参数)才能读取内容。该变量必须设置为等于“内容”长度的数值。这显然意味着我需要事先知道响应内容长度,这是不合理的。

如果我不传递该变量或将其设置为大于内容长度的值,则会收到以下错误:

Traceback (most recent call last):
  File "\Python33\lib\http\client.py", line 590, in _readall_chunked
    chunk_left = self._read_next_chunk_size()
  File "\Python33\lib\http\client.py", line 562, in _read_next_chunk_size
    return int(line, 16)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb4 in position 0: invalid start byte

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 22, in main
    logr.info('content: ' + response.raw.read().decode())
  File "\Python33\lib\site-packages\requests\packages\urllib3\response.py", line 167, in read
    data = self._fp.read()
  File "\Python33\lib\http\client.py", line 509, in read
    return self._readall_chunked()
  File "\Python33\lib\http\client.py", line 594, in _readall_chunked
    raise IncompleteRead(b''.join(value))
http.client.IncompleteRead: IncompleteRead(351 bytes read)

如果没有这个 'response_length' 变量,我该如何完成这项工作?此外,还有比“请求”库更好的选择吗?

PS:此代码为独立脚本,不在Django框架中运行。

4

2 回答 2

4

使用公共 API而不是内部,不用担心内容长度和对库的读取:

import requests

s = requests.Session()
s.verify = False
s.auth = (token01, token02)
resp = s.get(url, params={'name': token01}, stream=True)
content = resp.content

或者,因为stream=True,您可以使用resp.raw文件对象:

for line in resp.iter_lines():
    # process a line

或者

for chunk in resp.iter_content():
    # process a chunk

如果您必须有一个类似文件的对象,则resp.raw可以使用(提供stream=True在请求上设置,如上面所做的那样),但随后只需使用没有长度的.read()调用来读取 EOF。

但是,如果您查询需要流式传输的资源(除了大文件请求、首先测试标头的要求或明确记录为流式传输服务的 Web 服务之外的任何资源),只需省略stream=True并使用resp.contentresp.text用于字节或 unicode 响应数据。

但是,最后,您的服务器似乎正在发送格式错误或不完整的分块响应;分块传输编码包括每个块的长度信息,并且服务器似乎在一个块长度上撒谎或为给定块发送的数据太少。解码错误仅仅是发送不完整数据的结果。

于 2013-09-18T07:01:09.043 回答
1

您请求的服务器使用“分块”传输编码,因此没有内容长度标头。分块传输编码中的原始响应不仅包含实际内容,还包含块,块是十六进制数字,后跟“\r\n”,它总是会导致 xml 或 json 解析器错误。
尝试使用:

response.raw.read(decode_content=True)
于 2013-09-18T07:35:18.733 回答