python 爬取 jsonp 請求的響應數據

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

jsonp請求

 

再看formdata表單,看起來也很正常jsonp請求的form表單

 

我們也複製下來 。接下來我們使用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請求進行模擬即可

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章