參考:https://github.com/LouisYZK/ShiXi_inWuhan/tree/master/1.23python
Flex技術是網站運用flash方法與客戶端進行數據通訊,數據的格式能夠是txt,json或amf等。
AMF是一種二進制編碼方式,其在flash傳輸效率高,以農業信息網數據爲例,爬取的方式與通常ajax分析相同。經過抓包分析請求頭和響應數據,而後構造請求、接受返回數據git
經過爬取全國農產品批發市場價格信息系統,獲取數據,可是此網站是經過flash展現數據,與如今大部分網站展現數據的方法不一樣,所以使用python下的pyamf來幫助咱們獲取數據。github
經過分析網頁,咱們獲得信息,此網站的data是在hqapp.swf中ajax
2.分析請求包json
目前Chales抓包工具可以支持將AMF編碼解析成明文形式,便於咱們分析。併發
經過chares抓包,咱們發現請求的body包含一個com.itown.kas.pfsc.report.po.HqPara對象和四個參數,結合網頁可知爲flash上的四個選擇菜單,能夠選擇省份和批發市場名稱等。app
class HqPara: def __init__(self): self.marketInfo = None self.breedInfoDl = None self.breedInfo = None self.provice = None # 第一個是查詢參數,第二個是頁數,第三個是控制每頁顯示的數量(默認每頁只顯示15條)但爬取的時候能夠一會兒設置成所有的數量 # 構造請求數據 def getRequestData(page_num,total_num): # 註冊自定義的Body參數類型,這樣數據類型com.itown.kas.pfsc.report.po.HqPara就會在後面被一併發給服務端(不然服務端就可能返回參數不是預期的異常Client.Message.Deserialize.InvalidType) pyamf.register_class(HqPara, alias='com.itown.kas.pfsc.report.po.HqPara') # 構造flex.messaging.messages.RemotingMessage消息 msg = messaging.RemotingMessage(messageId=str(uuid.uuid1()).upper(), clientId=str(uuid.uuid1()).upper(), operation='getHqSearchData', destination='reportStatService', timeToLive=0, timestamp=0) msg.body = [HqPara(),str(page_num), str(total_num)] msg.headers['DSEndpoint'] = None msg.headers['DSId'] = str(uuid.uuid1()).upper() # 按AMF協議編碼數據 req = remoting.Request('null', body=(msg,)) env = remoting.Envelope(amfVersion=pyamf.AMF3) env.bodies = [('/1', req)] data = bytes(remoting.encode(env).read()) return data # 返回一個請求的數據格式
能夠看到返回的response中,包含有全部記錄條數ide
def getResponse(data): url = 'http://jgsb.agri.cn/messagebroker/amf' req = Request(url, data, headers={'Content-Type': 'application/x-amf'}) # 解析返回數據 opener = urllib2.build_opener() return opener.open(req).read() def getContent(response): amf_parse_info = remoting.decode(response) # 數據總條數 total_num = amf_parse_info.bodies[0][1].body.body[3] info = amf_parse_info.bodies[0][1].body.body[0] return total_num, info
def storagejson(info): path='info.json' path1='E:\spyderdata\\' res = [] for record in info: record['reportDate'] = record['reportDate'].strftime('%Y-%m-%d %H:%M:%S') record['auditDate'] = record['auditDate'].strftime('%Y-%m-%d %H:%M:%S') record['snatchDate'] = 'null' res.append(record) fp = open(path,'w',encoding='utf-8') json.dump(res,fp,indent=4,ensure_ascii= False) fp.close() os.rename(path,path1+filename+path) if __name__ == '__main__': filename = time.strftime("%Y-%m-%d", time.localtime()) # 第一次請求時,獲取數據量 reqData = getRequestData(1,2) rep = getResponse(reqData) total_num,info = getContent(rep) # 一次請求完成 reqData = getRequestData(1, total_num) rep = getResponse(reqData) total_num,info = getContent(rep) storagejson(info)
from urllib import request as urllib2 from urllib.request import Request import uuid import pyamf import json import time import os from pyamf import remoting from pyamf.flex import messaging class HqPara: def __init__(self): self.marketInfo = None self.breedInfoDl = None self.breedInfo = None self.provice = None # 第一個是查詢參數,第二個是頁數,第三個是控制每頁顯示的數量(默認每頁只顯示15條)但爬取的時候能夠一會兒設置成所有的數量 # 構造請求數據 def getRequestData(page_num,total_num): # 註冊自定義的Body參數類型,這樣數據類型com.itown.kas.pfsc.report.po.HqPara就會在後面被一併發給服務端(不然服務端就可能返回參數不是預期的異常Client.Message.Deserialize.InvalidType) pyamf.register_class(HqPara, alias='com.itown.kas.pfsc.report.po.HqPara') # 構造flex.messaging.messages.RemotingMessage消息 msg = messaging.RemotingMessage(messageId=str(uuid.uuid1()).upper(), clientId=str(uuid.uuid1()).upper(), operation='getHqSearchData', destination='reportStatService', timeToLive=0, timestamp=0) msg.body = [HqPara(),str(page_num), str(total_num)] msg.headers['DSEndpoint'] = None msg.headers['DSId'] = str(uuid.uuid1()).upper() # 按AMF協議編碼數據 req = remoting.Request('null', body=(msg,)) env = remoting.Envelope(amfVersion=pyamf.AMF3) env.bodies = [('/1', req)] data = bytes(remoting.encode(env).read()) return data # 返回一個請求的數據格式 def getResponse(data): url = 'http://jgsb.agri.cn/messagebroker/amf' req = Request(url, data, headers={'Content-Type': 'application/x-amf'}) # 解析返回數據 opener = urllib2.build_opener() return opener.open(req).read() def getContent(response): amf_parse_info = remoting.decode(response) # 數據總條數 total_num = amf_parse_info.bodies[0][1].body.body[3] info = amf_parse_info.bodies[0][1].body.body[0] return total_num, info def storagejson(info): path='info.json' path1='E:\spyderdata\\' res = [] for record in info: record['reportDate'] = record['reportDate'].strftime('%Y-%m-%d %H:%M:%S') record['auditDate'] = record['auditDate'].strftime('%Y-%m-%d %H:%M:%S') record['snatchDate'] = 'null' res.append(record) fp = open(path,'w',encoding='utf-8') json.dump(res,fp,indent=4,ensure_ascii= False) fp.close() os.rename(path,path1+filename+path) if __name__ == '__main__': filename = time.strftime("%Y-%m-%d", time.localtime()) # 第一次請求時,獲取數據量 reqData = getRequestData(1,2) rep = getResponse(reqData) total_num,info = getContent(rep) # 一次請求完成 reqData = getRequestData(1, total_num) rep = getResponse(reqData) total_num,info = getContent(rep) storagejson(info)
1.pyamf工具
這次爬蟲要用到python的pyamf庫,anaconda中並無這個庫,所以得本身下載,經過查詢得知能夠經過pip安裝flex
pip install PyAMF
可是安裝過程出現錯誤,沒法安裝,因而經過下載源文件,解壓到 python安裝目錄\Lib\site-packages下,更名爲pamf,在pycharm中導入pyamf,便可使用。
網上搜索時,有人說pyamf不支持python3,須要替換爲Py3AMF,可是實測pyamf在python3能夠運行爬蟲。