中國空氣質量在線監測分析平臺是一個收錄全國各大城市天氣數據的網站,包括溫度、溼度、PM 2.五、AQI 等數據,連接爲:https://www.aqistudy.cn/html/city_detail.html 在分析頁面的時候咱們發現頁面中的數據是動態加載的,當咱們點擊搜索按鈕的時候,就會獲取到alax請求到的數據javascript
打開咱們動態獲取到的數據以後,發現當前ajax請求爲post類型的請求,攜帶一個請求參數d,而且請求參數爲加密後的參數而且響應數據也是通過加密後的密文數據php
請求的數據 html
響應的數據java
咱們已經知道,剛纔咱們捕獲到的ajax請求是經過點擊了設定"範圍"後的查詢按鈕後觸發的,也就是說該查詢按鈕上必定綁定了某個點擊事件且觸發了對應ajax請求發送的事件。那麼接下來咱們就能夠經過火狐瀏覽器去檢測該查詢按鈕上到底綁定了哪些事件且是否發起了ajax請求,火狐瀏覽器能夠分析頁面某些元素的綁定事件以及定位到具體的代碼在哪一行 node
進到點擊事件對應的頁面源碼中,發現真的對搜索按鈕添加了一個點擊事件。 ajax
接下來,咱們須要分析getData函數的內部實現,當前源文件中,搜索該方法進行定位,定位到了以後經過分析,發現其內部是調用了下面的這兩個方法進行數據的請求api
接着分析這兩個方法內部的實現,這兩個方法就是在getDate實現的下方。再進一步分析發現這兩個方法都調用的getServerDate(),這個方法,並傳遞了method,param等參數,而後還有一個回調函數跟明顯是對返回數據進行處理的,這說明ajax請求就是由這和getServerDate()方法發起瀏覽器
定位getServerData方法,查看內部的具體實現。在谷歌瀏覽器中開啓抓包工具,而後對該網站首頁發起請求,捕獲全部的數據包,而後在全部的數據包中實現全局搜索,搜索該方法是存在在哪一個文件中的服務器
JavaScript混淆:咱們會驚訝的發現getServerData後面跟着的東西咱們看不懂,而且也不符合js函數的寫法,其實這裏是通過javascript混淆加密了,混淆加密以後,代碼將變爲不可讀的形式,可是功能是徹底一致的,這是一種常見的Javascript加密手段。咱們想要查看到該方法的實現則必須進行反混淆。函數
最簡單的方法即是搜索在線反混淆網站。http://www.bm8.com.cn/jsConfusion/
在反混淆後,咱們很清晰的看到了ajax請求發送的實現,而後還看到了ajax對應post請求的動態加密請求參數的加密方法getParam(),而且將method和object做爲了函數的參數。metod和object是從getServerDate函數的參數中獲取的。那麼getServerData函數中的method和object表示的是什麼,咱們須要返回去查看getServerDate函數的調用:
發現method是固定形式字符串,object就是param的一個字典,裏面存儲了三組鍵值對city表示查詢城市名稱,startTime和endTime爲查詢起止事件,typebioassay爲HOUR;
至此getParam()函數中兩個參數的表示含有咱們已經清楚了。getParam函數的返回值就是ajax對應post請求的動態加密請求參數了,咱們須要定位到其函數內部的實現,看看如何對請求參數加密的。在getServerDate中咱們發現ajax請求對應的操做代碼,其中還有一個很是重要的一部,就是ajax請求成功後的回調函數實現內部,接受到了響應數據data,data咱們知道是一組密文數據,而後調用了decodeData對data進行解密操做
在反混淆網站的代碼中咱們搜索到getPrame和decodeDate這兩個函數的實現:
服務器響應回來的密文數據是被decodeData進行解密的。觀察解密函數發現是經過base64+AES+DES進行的解密!
接下來,咱們須要藉助於PyExecJS庫來實現模擬JavaScript代碼執行獲取動態加密的請求參數,而後再將加密的響應數據帶入decodeData進行解密便可!
開始執行js:
function getPostParamCode(method, city, type, startTime, endTime){ var param = {}; param.city = city; param.type = type; param.startTime = startTime; param.endTime = endTime; return getParam(method, param); }
在py源文件中能夠基於pyExecJS模擬執行步驟2中定義好的自定義函數,獲取動態加密參數:
import execjs node = execjs.get() # Params method = 'GETCITYWEATHER' city = '北京' type = 'HOUR' start_time = '2018-01-25 00:00:00' end_time = '2018-01-25 23:00:00' # Compile javascript file = 'jsCode.js' ctx = node.compile(open(file,encoding='utf-8').read()) # Get params js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time) params = ctx.eval(js) print(params)
接下來咱們用requests庫來模擬POST請求
import execjs import requests node = execjs.get() # Params method = 'GETCITYWEATHER' city = '北京' type = 'HOUR' start_time = '2018-01-25 00:00:00' end_time = '2018-01-25 23:00:00' # Compile javascript file = 'jsCode.js' ctx = node.compile(open(file).read()) # Get params js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time) params = ctx.eval(js) #發起post請求 url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php' response_text = requests.post(url, data={'d': params}).text print(response_text)
接下來咱們再調用一下 JavaScript 中的 decodeData() 方法便可實現解密:
import execjs import requests node = execjs.get() # Params method = 'GETCITYWEATHER' city = '北京' type = 'HOUR' start_time = '2018-01-25 00:00:00' end_time = '2018-01-25 23:00:00' # Compile javascript file = 'jsCode.js' ctx = node.compile(open(file).read()) # Get params js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time) params = ctx.eval(js) #發起post請求 url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php' response_text = requests.post(url, data={'d': params}).text #對加密的響應數據進行解密 js = 'decodeData("{0}")'.format(response_text) decrypted_data = ctx.eval(js) print(decrypted_data)