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

python爬json python爬jsonp_爬虫

 

再看formdata表单,看起来也很正常

python爬json python爬jsonp_python_02

 

我们也复制下来 。接下来我们使用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请求进行模拟即可