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
再看formdata表單,看起來也很正常
我們也複製下來 。接下來我們使用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請求進行模擬即可