requests.exceptions.ChunkedEncodingError: ('Connection broken: IncompleteRead(0 bytes read)', Incomp
解决了之前的报错:
我的环境:
linux_centos python2.7
报错情况:
Traceback (most recent call last):
File "total_flow_count.py", line 89, in <module>
query_total_flow()
File "total_flow_count.py", line 59, in query_total_flow
response = requests.post(url, json=data, timeout=60, headers=request_header)
File "/usr/lib/python2.7/site-packages/requests/api.py", line 112, in post
return request('post', url, data=data, json=json, **kwargs)
File "/usr/lib/python2.7/site-packages/requests/api.py", line 58, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/lib/python2.7/site-packages/requests/sessions.py", line 508, in request
resp = self.send(prep, **send_kwargs)
File "/usr/lib/python2.7/site-packages/requests/sessions.py", line 658, in send
r.content
File "/usr/lib/python2.7/site-packages/requests/models.py", line 823, in content
self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes()
File "/usr/lib/python2.7/site-packages/requests/models.py", line 748, in generate
raise ChunkedEncodingError(e)
requests.exceptions.ChunkedEncodingError: ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
[root@TENCENT64 /data/test]#
2019-7-31日更新:
经过了一段时间的学习,我对这个问题有了更深的理解,也有了更好的解决方案,
和大家分享一下,哈哈~~
chunkedEncodingError ,有时候在调用公司接口的时候,会出现很多问题,
我看了下文档日期,基本都在2013-2014年,
所以我想,会不会是因为协议不统一导致的呢???
也就是,我们的请求requests默认了是http1.1,但是对方服务器接口发送的却是http1.0的格式,
于是上网搜索,啥也没有搜索到,我决定自己动手,丰衣足食:
先查看错误位置:
于是打开那个文件:
vim /usr/lib/python2.7/site-packages/requests/models.py
跑到第753行去看:
发现捕获了一个协议错误,然后抛出了chunkedEncoding异常,
很明显了。就是这个服务器支持http1.0,我们跑去拿http1.1访问,出了一些兼容性上的问题。
之前我的想法就是,一言不合改源码,但是换了一台机器去部署,你也让别人改源码吗?(手动滑稽)
改完代码的返回的请求头:http1.1传输,强行chunked
没改之前的返回的请求头:http1.0传输,
HTTP1.1支持chunked transfer,所以可以有Transfer-Encoding头部域:Transfer-Encoding: chunked,简单说就是返回内容的时候,分段返回的。chunked的编码是吧整个压缩包分段传输,其实有点像我们把压缩包压缩时分成若干个压缩文件一样,解压的时候,必须把全部文件放到一个目录下解压缩。这个编码也是如此,传过来的是一个一个块,最后需要把这些块都拼接起来才是完整的数据,所以,只要一次把这些块取出来拼到一块就可以了。不设置长度。
chunked编码在压缩块前面都会有一个标识压缩块大小的16进制字符串,我们每次读取压缩块之前,需要先获得这个大小,以便告诉程序接下来该读取多少数据,最后用一个零长度的块作为消息结束的标志。这种方法允许发送方只缓冲消息的一个片段,避免缓冲整个消息带来的过载。
因为上面的服务器不支持chunked传输,从而一开始还没传数据,就报错了。
参考网站:https://blog.csdn.net/lhj_5460/article/details/48156461
HTTP1.0则没有。 HTTP通信时,会使用Content-Length头信息性来通知用户代理(通常意义上是浏览器)服务器发送的文档内容长度,该头信息定义于HTTP1.0协议RFC 1945 10.4章节中。浏览器接收到此头信息后,接受完Content-Length中定义的长度字节后开始解析页面,但如果服务端有部分数据延迟发送吗,则会出现浏览器白屏,造成比较糟糕的用户体验。
如果是chunked传输,那么Content-Length就会自动被失效
所以这里服务器端是http1.0。改包源代码,用chunked传输,自然就会导致Content-Length失效,如果文本接收太长,他也不知道文本没有收完,变成了一个小傻子。
而所谓的chunked编码,因为有版本ProtocolError异常被捕获了,从而其实根本没有用到chunked编码!!!
这报了异常,代码的确是不安全的,所以我们应该考虑的是降低发送请求的版本。
其他的都别动了,就加上三句话就ok:
import httplib
httplib.HTTPConnection._http_vsn = 10
httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'
之前看网上有人用curl,然后设置http协议为1.0什么的,这个也是一样的思路。
然后就可以正常跑起来拉!
遇到了chunk编码的其他问题?
别怕,按照上面的思路去解决就好,
1.报了什么错误,按照路径打开源文件去检查报错和捕获异常情况,requests包写的很优雅,别怕,注释也写了很多滴!
2.如果是DecodeError错误:
- 是不是对方发的报文格式或者协议版本和你的接收格式不同?只能你适应他的话,你就得做相应修改,
- 是不是编解码方式错误?
原文:
解决方法如下:
可以看到,划红线的地方报错了,具体位置在:
File "/usr/lib/python2.7/site-packages/requests/models.py", line 748, in generate
让我觉得很奇葩的是,这个报错居然是可以去掉的。于是我跑到这个models.py里面,把raise给去掉了,然后就成功运行了。
bug原因
这是为什么呢?
网上有些人的情况是:
画了红色圈圈的地方是并不是0的,他们能获得一些数据,偶尔能够连上。
这个应该看下面的网址:https://blog.csdn.net/wangzuxi/article/details/40377467
而我呢?非常凄惨,连都连不上。
于是:
问题一:真的能跑通吗?
在办公网络上的浏览器测试网址:
http://xxx.xxx.xxx.xxx:80/xxxxx/xxxxx/xxxxx.cgi/json?data={"operator":"xxxxx","feature_type":["出包量"],"date":"2019-06-15","begintime":"23:55","endtime":"23:59","type":"set","set_id":[1],"set_name":[]}
在idc(内网机器)的测试机器上:
curl -1d 'data={"operator":"xxxxx","feature_type":["出包量"],"date":"2019-06-15","begintime":"23:55","endtime":"23:59","type":"set","set_id":[1],"set_name":[]}' "http://xxx.xxx.xxx.xxx:80/xxxxx/xxxxx/xxxxx.cgi"
都可以运行,没毛病。。。说明是requests这个过程出了问题。
于是我继续努力的写着,参考下面的网址:
http://blog.sina.com.cn/s/blog_a1e9c7910102x4ux.html
是我同事写的他遇到的情况。
然而并没有用。
当我去掉一切json的参数,直接用py脚本访问那个接口(ip+定位符)时,又是可以访问的。
我知道了,问题出在参数上,
可能是传参出了问题,post阶段出错。
但是你仔细想一下,它什么都没有接收到,应当是连接过程出了问题。
很有可能是返回的值编码有问题。
所以根据我的猜想,看了下chunk编码的知识:
https://blog.csdn.net/zhangboyj/article/details/6236780
一般HTTP通信时,会使用Content-Length头信息性来通知用户代理(通常意义上是浏览器)服务器发送的文档内容长度,该头信息定义于HTTP1.0协议RFC 1945 10.4章节中。浏览器接收到此头信息后,接受完Content-Length中定义的长度字节后开始解析页面,但如果服务端有部分数据延迟发送吗,则会出现浏览器白屏,造成比较糟糕的用户体验。
解决方案是在HTTP1.1协议中,RFC 2616中14.41章节中定义的Transfer-Encoding: chunked的头信息,chunked编码定义在3.6.1中,所有HTTP1.1 应用都支持此使用trunked编码动态的提供body内容的长度的方式。进行Chunked编码传输的HTTP数据要在消息头部设置:Transfer-Encoding: chunked表示Content Body将用chunked编码传输内容。根据定义,浏览器不需要等到内容字节全部下载完成,只要接收到一个chunked块就可解析页面.并且可以下载html中定义的页面内容,包括js,css,image等。
采用chunked编码有两种选择,一种是设定Server的IO buffer长度让Server自动flush buffer中的内容,另一种是手动调用IO中的flush函数。不同的语言IO中都有flush功能……
简而言之,chunked编码的基本方法是将大块数据分解成多块小数据,每块都可以自指定长度,
这里会出错,也许是因为对方使用了http1.0或者是两者不匹配,等等。
这个问题对于数据传输来说,
http1.1是致命的,http1.0根本不会看chunked编码长度,
所以解决方案可以有很多:
1.修改python源码,把报错去掉(去掉后如果你的代码没有问题,就可以传输了)
2.使用http1.0来进行request
3.不用requests,用python curl等等。
4.也许更新python的requests库会有用。
5.加版本声明:
import httplib
httplib.HTTPConnection._http_vsn = 10
httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'
我的请求代码格式:
#-*- coding:utf-8 -*-
import time
import hmac
import hashlib
import requests
import json
import mysql.connector
import requests
import httplib
import urllib
from urllib import unquote
httplib.HTTPConnection._http_vsn = 10
httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'
import json
def query_total_flow():
header = {"Content-Type": "application", 'Connection': 'close', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
post_data = {
"operator": "xxxxxx", # 调用系统的名字
"type": "set",
"set_id": [1],
"set_name": [],
"feature_type": ["入流量"],
"date": "2019-06-15",
"begintime": "23:55",
"endtime": "23:59",
}
url = "http://xxx.xxx.xxx.xxx:80/xxxxx/xxxxx/xxxxx.cgi/json?"
post_data = json.dumps(post_data, ensure_ascii=False, separators=(',',':'))
print post_data
# url = url + post_data
url = url + urllib.urlencode({'data': post_data})
# data = urllib.urlencode({'data': post_data})
# print post_data
# print data
# data = unquote(data)
try:
# print data
print url
response = requests.get(url, timeout=60, headers=header)
print response.headers
print response.url
print response.text.encode('utf-8')
response = urllib.unquote(response)
print response
if __name__ == "__main__":
query_total_flow()
或者用post也可以,上面的代码里url要注意加json?
整体代码如下:
#-*- coding:utf-8 -*-
import time
import hmac
import hashlib
import requests
import json
import mysql.connector
import requests
import httplib
import urllib
from urllib import unquote
import json
def query_total_flow():
header = {"Content-Type": "application/json", 'Connection': 'close', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
post_data = {
"operator": "xxxxxx", # 调用系统的名字
"type": "set",
"set_id": [1],
"set_name": [],
"feature_type": ["入流量"],
"date": "2019-06-15",
"begintime": "23:55",
"endtime": "23:59",
}
url = "http://xxx.xxx.xxx.xxx:80/xxxxx/xxxxx/xxxxx.cgi/json?"
post_data = json.dumps(post_data, ensure_ascii=False, separators=(',',':'))
print post_data
# url = url + post_data
url = url + urllib.urlencode({'data': post_data})
# data = urllib.urlencode({'data': post_data})
# print post_data
# print data
data = unquote(url)
try:
# print data
print data
response = requests.get(url, json=data, timeout=60, headers=header)
print response.headers
print response.url
print response.text.encode('utf-8')
if response['code'] != 0:
result_dict = json.loads(response.text)
data = result_dict["data"]
print(data)
print(data)
set_info = []
return response
raise exp_with_err_instance(err.RESULT_ERROR, 'can not find inst info')
print ("none!")
return []
except Exception as e:
print ("Exception")
raise
if __name__ == "__main__":
query_total_flow()
更多推荐
所有评论(0)