参考: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能够运行爬虫。