【问题标题】:How to query a restful webservice using Python如何使用 Python 查询一个 RESTful Web 服务
【发布时间】:2020-03-07 22:37:06
【问题描述】:

编写使用Requests lib 的Python 脚本来触发对远程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' 变量,我该如何完成这项工作? 另外,还有比“Requests”库更好的选择吗?

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

【问题讨论】:

  • 为什么不使用请求的公共 API?你在修补内部结构,为什么?

标签: python web-services http python-requests


【解决方案1】:

使用public 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 响应数据。

但是,最终,您的服务器似乎发送了格式错误或不完整的分块响应; chunked transfer encoding 包含每个块的长度信息,并且服务器似乎对块长度撒谎或为给定块发送的数据太少。解码错误仅仅是发送的数据不完整的结果。

【讨论】:

  • 试过这个 - s = requests.session() response = s.get(url, auth=(token01, token02), params={'name': token01}, stream=True, verify= False) 响应中的行。 ' 编解码器无法解码位置 0 中的字节 0xcc:无效的继续字节 在处理 ...期间发生:回溯(最近一次调用):... _readinto_chunked raise IncompleteRead(bytes(b[0:total_bytes])) http。 client.IncompleteRead: IncompleteRead(351 bytes read)
  • @QuestMonger:听起来您的网络服务没有正确发送数据。
  • @QuestMonger:你得到的数据不完整;如果数据在多字节 UTF8 字符之间被截断,那么您就有问题了。
  • @QuestMonger:看起来服务器正在发送格式错误的分块响应,也许服务器坏了?块与 每个块 的内容长度信息一起发送,块读取失败,响应不完整。
  • @QuestMonger:异常出现在http.client 库代码中,它根据服务器发送的标头响应发送分块响应的服务器。是的,您请求读取 351 个字节,但这不是不完整响应异常的原因。
【解决方案2】:

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

response.raw.read(decode_content=True)

【讨论】:

  • ok 试过了 - { s = Session() prepped = Request('GET', url, auth=(token01, token02), params={'name': token01}).prepare() response = s.send(prepped, stream=True, verify=False) logr.info('content: ' + response.raw.read(decode_content=True)) } 得到与上面相同的错误 - {Traceback (最近的调用last): ... UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf3 in position 2: invalid continuation byte during ... exception occurred: Traceback (last recent call last): ... http.client.IncompleteRead : IncompleteRead(351 bytes read)}
猜你喜欢
  • 2016-02-27
  • 1970-01-01
  • 2011-08-10
  • 1970-01-01
  • 1970-01-01
  • 2011-01-02
  • 2016-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多