Python requests发送post请求的编码问题

我用requests中的post请求爬取NSFC网站的时候,遇到了JSON parse error: syntax error问题,经过查阅资料和尝试,发现是一个编码问题。下面是问题分析与解决方法。

在HTTP协议中,post提交的数据必须放在消息主体中,但是协议中并没有规定必须使用什么编码方式,从而导致了提交方式的不同。服务端根据请求头中的Content-Type字段来获知请求中的消息主体是用何种方式进行编码,再对消息主体进行解析。具体的编码方式包括如下:

  • application/x-www-form-urlencoded:以form表单形式提交数据,最常见也是大家最熟悉的
  • application/json:以json串提交数据。

下面使用requests来发送这两种编码的POST请求。

一、提交Form表单

requests提交Form表单,一般存在于网站的登录,用来提交用户名和密码。以http://httpbin.org/post为例,在requests中,以form表单形式发送post请求,只需要将请求的参数构造成一个字典,然后传给requests.post()的data参数即可。代码如下:

1
2
3
4
url = 'http://httpbin.org/post'
d = {'key1': 'value1', 'key2': 'value2'}
r = requests.post(url, data=d)
print(r.text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"args": {},
"data": "",
"files": {},
"form": {
"key1": "value1",
"key2": "value2"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.20.0"
},
"json": null,
"origin": "121.249.15.110, 121.249.15.110",
"url": "https://httpbin.org/post"
}

这里返回的"Content-Type":"application/x-www-form-urlencoded",证明这是提交Form的方式。

二、提交json串

对于提交json串,主要是用于发送ajax请求中,动态加载数据。以NSFC网站为例,加载项目的方式为ajax,项目的内容在响应中。

下面是典型的错误用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
url = "url"

headers = {'Content-Type': "application/json",
'Host': "output.nsfc.gov.cn",
'Origin': "http://output.nsfc.gov.cn",
'Referer': "http://output.nsfc.gov.cn/fundingQuery",
'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36",}

payload = {'adminID': "",
'beginYear': "",
......}

response = requests.post(url = url,data = payload,headers = headers)
print(response.text)
1
'{\n\t"timestamp":1557026952651,\n\t"status":400,\n\t"error":"Bad Request",\n\t"exception":"org.springframework.http.converter.HttpMessageNotReadableException",\n\t"message":"JSON parse error: syntax error, expect {, actual error, pos 0, fastjson-version 1.2.38; nested exception is com.alibaba.fastjson.JSONException: syntax error, expect {, actual error, pos 0, fastjson-version 1.2.38",\n\t"path":"/baseQuery/data/supportQueryResultsData"\n}'

请求头已经跟抓包得到的保持一致了,加上了'Content-Type': "application/json"参数,但是无法正确得到响应。这是因为请求实体的格式错了,服务端无法解码。

正确方法1——把data进行json编码

正确的写法是把data进行json编码,再发送:

1
response = requests.post(url = url,data = json.dumps(payload),headers = headers)
1
'{\n\t"code":200,\n\t"data":{\n\t\t"iTotalRecords":76,\n\t\t"resultsData":[\n\t\t\t[\n\t\t\t\t"59607",\n\t\t\t\t"调节性树突状细胞负向调控烧伤所致过度炎症反应的信号转导机制研究",\n\t\t\t\t"81000847",\n\t\t\t\t"面上项目",\n\t\t\t\t"中国人民解放军总医院",\n\t\t\t\t"王强",\n\t\t\t\t"",\n\t\t\t\t"2010",\n\t\t\t\t"调节性树突状细胞;过度炎症反应;信号转导及转录活化因子-3;负向调控免疫;过继治疗",\n\t\t\t\t"true",\n\t\t\t\t"5;0;0;0;0",\n\t\t\t\t"100518",\n\t\t\t\t"",\n\t\t\t\t"218"\n\t\t\t]\n\t\t]\n\t},\n\t"message":"Success"\n}'

正确方法2——使用json参数发送

除了将data主动编码为json发送之外,requests还提供了一个json参数,自动使用json方式发送,而且在请求头中也不用显示声明’Content-Type’:’application/json’

1
2
3
4
5
6
7
8
9
10
11
12
13
url = "url"

headers = {'Host': "output.nsfc.gov.cn",
'Origin': "http://output.nsfc.gov.cn",
'Referer': "http://output.nsfc.gov.cn/fundingQuery",
'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36",}

payload = {'adminID': "",
'beginYear': "",
......}

response = requests.post(url = url,json = payload,headers = headers)
print(response.text)

同样可以正确得到响应:

1
'{\n\t"code":200,\n\t"data":{\n\t\t"iTotalRecords":76,\n\t\t"resultsData":[\n\t\t\t[\n\t\t\t\t"59607",\n\t\t\t\t"调节性树突状细胞负向调控烧伤所致过度炎症反应的信号转导机制研究",\n\t\t\t\t"81000847",\n\t\t\t\t"面上项目",\n\t\t\t\t"中国人民解放军总医院",\n\t\t\t\t"王强",\n\t\t\t\t"",\n\t\t\t\t"2010",\n\t\t\t\t"调节性树突状细胞;过度炎症反应;信号转导及转录活化因子-3;负向调控免疫;过继治疗",\n\t\t\t\t"true",\n\t\t\t\t"5;0;0;0;0",\n\t\t\t\t"100518",\n\t\t\t\t"",\n\t\t\t\t"218"\n\t\t\t]\n\t\t]\n\t},\n\t"message":"Success"\n}'
赞赏一杯咖啡
0%