[轉]使用python爬取東方財富網機構調研數據

最近有一個需求,須要爬取東方財富網的機構調研數據.數據所在的網頁地址爲: 機構調研javascript

  網頁以下所示:html

  

  可見數據共有8464頁,此處不能直接使用scrapy爬蟲進行爬取,由於點擊下一頁時,瀏覽器只是發起了javascript網絡訪問,而後將服務器返回的數據插入網頁,沒法經過網址直接獲取對應頁的的頁面數據.java

  經過chrome的開發者工具,咱們能夠看到點擊下一頁按鈕背後發起的網頁訪問:mysql

  在點擊下一頁時,瀏覽器向地址發起了訪問.咱們分析一下這個地址的結構:sql

    http://data.eastmoney.com/DataCenter_V3/jgdy/xx.ashx?pagesize=50&page=2&js=var%20ZUPcjFOK&param=&sortRule=-1&sortType=0&rt=48759234chrome

  上述地址中的&page=  以後指定的是須要獲取第幾個頁面的數據.因此咱們能夠經過修改&page=後面的數字來訪問不一樣頁面對應的數據.數據庫

  如今看一下這個數據的結構:json

  可見這個數據是一個字符串,根據第一個出現的等於號對該字符串進行切分,切分獲得的後半段是一個json字符串,裏面存儲了咱們想要獲取的數據. json數據中的字段pages的值就是頁面的總數.根據這一特性咱們能夠寫出下述函數獲取頁面的總數:瀏覽器

複製代碼
# 獲取頁數
def get_pages_count():
    url = '''http://data.eastmoney.com/DataCenter_V3/jgdy/xx.ashx?pagesize=50&page=%d''' % 1
    url += "&js=var%20ngDoXCbV&param=&sortRule=-1&sortType=0&rt=48753724"
    wp = urllib.urlopen(url)
    data = wp.read().decode("gbk")
    start_pos = data.index('=')
    json_data = data[start_pos + 1:]
    dict = json.loads(json_data)
    pages =dict['pages']
    return pages
複製代碼

  在給定頁數範圍的狀況下能夠獲取數據地址列表,以下所示:服務器

複製代碼
# 獲取連接列表
def get_url_list(start,end):
    url_list=[]
    while(start<=end):
        url = '''http://data.eastmoney.com/DataCenter_V3/jgdy/xx.ashx?pagesize=50&page=%d''' %start
        url += "&js=var%20ngDoXCbV&param=&sortRule=-1&sortType=0&rt=48753724"
        url_list.append(url)
        start+=1
    return url_list
複製代碼

  爲了保存這些數據,我使用sqlalchemy中的orm模型來表示數據模型,數據模型定義以下:

複製代碼
# 此處須要設置charset,不然中文會亂碼
engine =create_engine('mysql+mysqldb://user:passwd@ip:port/db_name?charset=utf8')
Base =declarative_base()

class jigoudiaoyan(Base):
    __tablename__ = "jigoudiaoyan"
    # 自增的主鍵
    id =Column(Integer,primary_key=True)
    # 調研日期
    StartDate = Column(Date,nullable=True)
    # 股票名稱
    SName =Column(VARCHAR(255),nullable=True)
    # 結束日期 通常爲空
    EndDate=Column(Date,nullable=True)
    # 接待方式
    Description =Column(VARCHAR(255),nullable=True)
    # 公司全稱
    CompanyName =Column(VARCHAR(255),nullable=True)
    # 結構名稱
    OrgName=Column(VARCHAR(255),nullable=True)
    # 公司代碼
    CompanyCode=Column(VARCHAR(255),nullable=True)
    # 接待人員
    Licostaff=Column(VARCHAR(800),nullable=True)
    # 通常爲空 意義不清
    OrgSum=Column(VARCHAR(255),nullable=True)
    # 漲跌幅
    ChangePercent=Column(Float,nullable=True)
    # 公告日期
    NoticeDate=Column(Date,nullable=True)
    # 接待地點
    Place=Column(VARCHAR(255),nullable=True)
    # 股票代碼
    SCode=Column(VARCHAR(255),nullable=True)
    # 結構代碼
    OrgCode=Column(VARCHAR(255),nullable=True)
    # 調研人員
    Personnel=Column(VARCHAR(255),nullable=True)
    # 最新價
    Close=Column(Float,nullable=True)
    #機構類型
    OrgtypeName=Column(VARCHAR(255),nullable=True)
    # 機構類型代碼
    Orgtype=Column(VARCHAR(255),nullable=True)
    # 主要內容,通常爲空 意義不清
    Maincontent=Column(VARCHAR(255),nullable=True)
Session =sessionmaker(bind=engine)
session =Session()
# 建立表
Base.metadata.create_all(engine)
# 獲取連接列表
複製代碼

  在上述基礎上,咱們就能夠定義下屬函數用於抓取連接的內容,並將其解析以後存入數據庫,以下所示:

複製代碼
#記錄並保存數據
def save_json_data(user_agent_list):
    pages =get_pages_count()
len_user_agent=len(user_agent_list) url_list =get_url_list(1,pages) count=0 for url in url_list: request = urllib2.Request(url) request.add_header('Referer','http://data.eastmoney.com/jgdy/') # 隨機從user_agent池中取user pos =random.randint(0,len_user_agent-1) request.add_header('User-Agent', user_agent_list[pos]) reader = urllib2.urlopen(request) data=reader.read() # 自動判斷編碼方式並進行解碼 encoding = chardet.detect(data)['encoding'] # 忽略不能解碼的字段 data = data.decode(encoding,'ignore') start_pos = data.index('=') json_data = data[start_pos + 1:] dict = json.loads(json_data) list_data = dict['data'] count+=1 for item in list_data: one = jigoudiaoyan() StartDate =item['StartDate'].encode("utf8") if(StartDate ==""): StartDate = None else: StartDate = datetime.datetime.strptime(StartDate,"%Y-%m-%d").date() SName=item['SName'].encode("utf8") if(SName ==""): SName =None EndDate = item["EndDate"].encode("utf8") if(EndDate==""): EndDate=None else: EndDate=datetime.datetime.strptime(EndDate,"%Y-%m-%d").date() Description=item['Description'].encode("utf8") if(Description ==""): Description= None CompanyName=item['CompanyName'].encode("utf8") if(CompanyName==""): CompanyName=None OrgName=item['OrgName'].encode("utf8") if(OrgName ==""): OrgName=None CompanyCode=item['CompanyCode'].encode("utf8") if(CompanyCode==""): CompanyCode=None Licostaff=item['Licostaff'].encode("utf8") if(Licostaff ==""): Licostaff=None OrgSum = item['OrgSum'].encode("utf8") if(OrgSum ==""): OrgSum=None ChangePercent=item['ChangePercent'].encode("utf8") if(ChangePercent ==""): ChangePercent=None else: ChangePercent=float(ChangePercent) NoticeDate=item['NoticeDate'].encode("utf8") if(NoticeDate==""): NoticeDate=None else: NoticeDate=datetime.datetime.strptime(NoticeDate,"%Y-%m-%d").date() Place=item['Place'].encode("utf8") if(Place==""): Place=None SCode=item["SCode"].encode("utf8") if(SCode==""): SCode=None OrgCode=item['OrgCode'].encode("utf8") if(OrgCode==""): OrgCode=None Personnel=item['Personnel'].encode('utf8') if(Personnel==""): Personnel=None Close=item['Close'].encode("utf8") if(Close==""): Close=None else: Close =float(Close) OrgtypeName =item['OrgtypeName'].encode("utf8") if(OrgtypeName==""): OrgtypeName=None Orgtype=item['Orgtype'].encode("utf8") if(Orgtype==""): Orgtype=None Maincontent=item['Maincontent'].encode("utf8") if(Maincontent==""): Maincontent=None one.StartDate=StartDate one.SName=SName one.EndDate=EndDate one.Description=Description one.CompanyName=CompanyName one.OrgName=OrgName one.CompanyCode=CompanyCode one.Licostaff=Licostaff one.OrgSum=OrgSum one.ChangePercent=ChangePercent one.NoticeDate=NoticeDate one.Place=Place one.SCode=SCode one.OrgCode=OrgCode one.Personnel=Personnel one.Close=Close one.OrgtypeName=OrgtypeName one.Orgtype=Orgtype one.Maincontent=Maincontent session.add(one) session.commit() print 'percent:' ,count*1.0/pages,"complete!,now ",count # delay 1s time.sleep(1)
複製代碼

  爲了加快抓取速度,我設置了user_agent池,每次訪問設置user_agent時隨機從池中取一條做爲此次訪問的user_agent.對應列表user_agent_list ,定義以下:

複製代碼
# user_agent 池
user_agent_list=[]
user_agent_list.append("Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 ")
user_agent_list.append("Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50")
user_agent_list.append("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1")
user_agent_list.append("Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11")
user_agent_list.append("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 ")
user_agent_list.append("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36")
複製代碼

  請注意,爲了自動識別網頁編碼並解碼,我使用了chardet模塊識別網頁的編碼.爲了應對極端狀況下解碼失敗的問題,我在解碼時設置跳過那些不能正確解碼的字符串.相關代碼截取以下:

 encoding = chardet.detect(data)['encoding']
 # 忽略不能解碼的字段
 data = data.decode(encoding,'ignore') 

補充:

  網址中最後一個字段代碼時間戳,用於肯定獲取哪個時刻的最新價(maybe for ban crawler?),在查看網頁源代碼以後,我肯定時間戳的生成代碼以下,給有須要的人(我發現東方財富網的這個字段都是這麼生成的):

# 獲取當前的時間戳
def get_timstamp():
    timestamp =int(int(time.time())/30)
    return str(timestamp)
相關文章
相關標籤/搜索