jsonp 是为了解决跨域问题而诞生出的解决方案。在现代浏览器中,除了src等特殊标签可以允许跨域,其他时候都不允许跨域访问。为了解决这个问题,jsonp诞生了。
其原理主要是 向服务端传递一个一个callback 方法,以及其他请求参数。服务端接受到请求之后,收集对应参数所需要的数据,并加上之前传过来的callback 方法名 ,包装成一个内容为 js文件的响应。客户端再对这个伪js方法进行解析。
示例:
以 http://www.neeq.com.cn/zone/newshare/listofissues.html 为例
其 数据获得接口为 http://www.neeq.com.cn/newShareController/infoResult.do?callback=jQuery211_1592489332270
其中 最后的159开头的即为13位时间戳。
在浏览器中,其显示为post请求。这里我们先copy下整个headers
我们也复制下来 。接下来我们使用requests 包模拟一下这个请求
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import time
headers = {
"Accept": "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
# "Content-Length":"386", #这个需要注释掉,如果长度不对请求会被视作异常
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "Hm_lpvt_b58fe8237d8d72ce286e1dbd2fc8308c=1592488450; BIGipServerNEEQV1.3C_WEB_8000=268501940.16415.0000; BIGipServerJY_NEEQV1.3C_WEB_8000=268501940.16415.0000; Hm_lvt_b58fe8237d8d72ce286e1dbd2fc8308c=1592515239",
"Host": "www.neeq.com.cn",
"Origin": "http://www.neeq.com.cn",
"Pragma": "no-cache",
"Referer": "http://www.neeq.com.cn/zone/newshare/listofissues.html",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36",
"X-Requested-With": "XMLHttpRequest",
}
formdata = {
"statetypes[]": "",
"page": "0",
"companyCode": "",
"isNewThree": "1",
"sortfield": "purchaseDate",
"sorttype": "desc",
"needFields[]": "id",
"needFields[]": "stockCode",
"needFields[]": "stockName",
"needFields[]": "initialIssueAmount",
"needFields[]": "enquiryType",
"needFields[]": "issuePrice",
"needFields[]": "peRatio",
"needFields[]": "purchaseDate",
"needFields[]": "issueResultDate",
"needFields[]": "enterPremiumDate",
}
timestamp = time.time() * 1000
url = "http://www.neeq.com.cn/newShareController/infoResult.do?callback=jQuery211_" + str(timestamp)[:13]
response = requests.get(url, headers=headers, data=formdata)
print(response.text)
#### 下面是响应内容
jQuery211_1592494024179([{"listInfo":{"content":[{"enterPremiumDate":null},{"enterPremiumDate":null}],"firstPage":true,"lastPage":true,"number":0,"numberOfElements":2,"size":20,"sort":null,"totalElements":2,"totalPages":1}}])
但是这样得出来的结果中并没有正确的数据。
我们看到 请求的url中有一个 jQuery211_xxxx的参数,这里就是jsonp方式的调用。
在浏览器中检查这个页面时,可以在http://www.neeq.com.cn/template/4/bluewise/_files/js/components/common/neeqDT.js?V_0.0.1 中得到明确的印证。这就是个jsonp请求。
最终本人经过多次模拟、搜索资料,发现 将请求url换成以下格式,并以get方式请求,即可拿到正常的数据。
url='http://www.neeq.com.cn/newShareController/infoResult.do?callback=jQuery211_{}&statetypes%5B%5D=&page=0&companyCode=&isNewThree=1&sortfield=purchaseDate&sorttype=desc&needFields%5B%5D=id&needFields%5B%5D=stockCode&needFields%5B%5D=stockName&needFields%5B%5D=initialIssueAmount&needFields%5B%5D=enquiryType&needFields%5B%5D=issuePrice&needFields%5B%5D=peRatio&needFields%5B%5D=purchaseDate&needFields%5B%5D=issueResultDate&needFields%5B%5D=enterPremiumDate'.format(str(timestamp)[:13])
这个url是接口前缀加上 时间戳callback再加上formdata拼接而成(formdata可以使用【view source】格式的数据,上文formdata图中可以找到。)
为什么换成get请求就可以了?
经查阅资料,原来jsonp只支持get。浏览器中显示的post 十分误导我们。
这里可以联系到 jsonp最终是将数据以类似src标签的方式加载,而这种加载方式的确是只有get方式。
所以以后遇见jsonp请求,一定不要误以为是post,需要找到参数拼接称get请求进行模拟即可