1. 程式人生 > >利用requests+分析ajax+mogodb爬取並存儲攜程酒店資料

利用requests+分析ajax+mogodb爬取並存儲攜程酒店資料

以前就利用selenium爬取協程酒店資訊,但是我們知道利用selenium抓取資訊有個缺點就是效率不高,於是這幾天重新開啟網頁,從基本的網頁和原始碼中尋找一些值得利用的資料。

話不多說,我們直接說抓取攜程酒店資料的思路,宣告:本節只做爬蟲交流技術所用,不得用於商業用途,如有侵犯他人權利,聯絡本作者刪除

首先我們開啟攜程所有南京酒店連結http://hotels.ctrip.com/hotel/nanjing12#ctm_ref=ctr_hp_sb_lst

 

簡簡單單,原始碼中包含我們需要的酒店資料,你以為這樣就結束了?攜程的這些資料這麼廉價地就給我們得到了?事實並不是如此,當我們點選第二頁的時候出現問題:雖然酒店的資料改變了,但是我們發現該網頁的網址卻沒有改變,這也就造成了原始碼中酒店的資料不改變,還是第一頁的資料,如下圖所示。我們遇到的第一個問題就是怎麼獲取所有資料的url,

於是我們要換種思路,看看能不能從原始碼中再獲取一些有用的資訊,比如輸入網址讓其頁面改變,且原始碼也改變顯示我們需要的資料。

http://hotels.ctrip.com/hotel/nanjing12#ctm_ref=ctr_hp_sb_lst

 

 

如下圖所示,在翻頁所對應的原始碼中,我們發現在不同的頁面對應不同的連結,比如第二頁,第三頁,第四頁分別為

href="http://hotels.ctrip.com/hotel/nanjing12/p2" ,

href="http://hotels.ctrip.com/hotel/nanjing12/p3",

href="

http://hotels.ctrip.com/hotel/nanjing12/p4"

點開這些連結我們看到酒店別的資料,不同的連結資料不同,而且在原始碼中也得到了所需要爬取的資料結果,於是我們可以得到了在不同的資料的ur連結規律。然後我們還需要找到總共多少頁資料,這個簡單,在節點type="text" value="1"data-pagecount=222 name="" />中可以利用正則表示式直接提取。

 分析到這裡,我們寫出部分程式碼:

import requests,re
urls=[]
origin_url='http://hotels.ctrip.com/hotel/nanjing12' 
def infor_pages():    ####查詢總共多少頁 
    origin_infor=requests.get(origin_url,headers={'User-Agent':'Mozilla/5.0' })
    origin_infor.encoding='utf-8'   ####注意:這個編碼很奇怪,原始碼中是gb2312,但是用該編碼卻
###是中文亂碼,利用utf-8不會亂碼
    res=re.compile('<a rel="nofollow" data-dopost="T" href=".*?" data-type="page" data-value=".*?">(.*?)</a>')
    pages=re.findall(res,origin_infor.text)###提取總頁數
    return pages
def url(pages): #將所有的連結放在列表中
    for i in range (1,int(pages[0])+1):
        url=origin_url+'/p'+str(i)##構造連結
        urls.append(url)        
    return urls

後面就是資料提取的問題了,由於資料繁多,有的資料出現與正常資料不同的情況,如下圖所示,在標準的資料裡面具有評分標準以及程度(極贊),還有地址具有大概地址與詳細地址,但是異常資料卻沒有這些內容,這在利用正則表示式提取的時候有一定的難度,我的思路是先粗提取再利用字串的相關知識進行過濾

我們比對正常資料與異常資料的原始碼

正常資料:

title="客戶點評:4.9分,總分5分。" data-href="http://hotels.ctrip.com/hotel/dianping/5451854.html?isFull=F"><span class="hotel_level">極贊</span><span class="hotel_value">4.9</span><span class="total_judgement_score"><span style="color:#009933;">99%</span>使用者推薦</span><span class="hotel_judgement">源自<span style="color:#FF9900;">13356</span>位住客點評

異常資料:

title="客戶點評:0.0分,總分5分。" data-href="http://hotels.ctrip.com/hotel/dianping/21695629.html?isFull=F"><span class="no_grade">暫無評分</span><span class="total_judgement_score"><span style="color:#009933;">100%</span>使用者推薦</span><span class="hotel_judgement">源自<span style="color:#FF9900;">1</span>位住客點評

對於該情況,我們將紅色字型部分利用(.*?)提取出來,給根據屬於哪一個字元,在給予重新排版資訊區

還有一個異常就是酒店地址資訊,有的是具有大概區街道和詳細地址,有的直接就是詳細地址,比如下面的資料:

正常資料:

<p class="hotel_item_htladdress">【 <a tracekey="nhtllistroomclick" tracevalue="clicktype=zonename;hotelid=5451854;roomid=;isdefaultdisplay=;defaultdisplaypos=;htlpos=1;rompos=;" title="新街口地區(市中心)酒店預訂" onclick="window.bizMap(68); return false;" href="//hotels.ctrip.com/hotel/Nanjing12/zone68">新街口地區(市中心)</a> <a tracekey="nhtllistroomclick" tracevalue="clicktype=zonename;hotelid=5451854;roomid=;isdefaultdisplay=;defaultdisplaypos=;htlpos=1;rompos=;" title="夫子廟地區酒店預訂" onclick="window.bizMap(71); return false;" href="//hotels.ctrip.com/hotel/Nanjing12/zone71">夫子廟地區</a>】秦淮區漢中路101號金鷹新街口店B座,近新街口螺絲轉彎。地鐵一號線新街口站20-22出口,地鐵2號線上海路站1號出口。 <a href="javascript:

異常資料:

<p class="hotel_item_htladdress">雨花臺區應天大街677-2號(雨花臺區,長江裝飾城對面) <a href="javascript:;"

對於這些資料,我們直接利用正則表示式提取<p class="hotel_item_htladdress">(.*?) <a href="javascript:;"這樣對於正常資料,提取出類似於下面資料

【 <a tracekey="nhtllistroomclick" tracevalue="clicktype=zonename;hotelid=5451854;roomid=;isdefaultdisplay=;defaultdisplaypos=;htlpos=1;rompos=;" title="新街口地區(市中心)酒店預訂" onclick="window.bizMap(68); return false;" href="//hotels.ctrip.com/hotel/Nanjing12/zone68">新街口地區(市中心)</a> <a tracekey="nhtllistroomclick" tracevalue="clicktype=zonename;hotelid=5451854;roomid=;isdefaultdisplay=;defaultdisplaypos=;htlpos=1;rompos=;" title="夫子廟地區酒店預訂" onclick="window.bizMap(71); return false;" href="//hotels.ctrip.com/hotel/Nanjing12/zone71">夫子廟地區</a>】秦淮區漢中路101號金鷹新街口店B座,近新街口螺絲轉彎。地鐵一號線新街口站20-22出口,地鐵2號線上海路站1號出口。()

我們利用字元查詢以及split方法得到詳細地址

夫子廟地區</a>】秦淮區漢中路101號金鷹新街口店B座,近新街口螺絲轉彎。地鐵一號線新街口站20-22出口,地鐵2號線上海路站1號出口。

這樣我們的資料處理就結束

該部分程式碼如下:

def items(urls):    ####酒店資訊獲取
    for url in urls :
        response=requests.get(url,headers={'User-Agent':'Mozilla/5.0' })
        response.encoding='utf-8'
        res1=re.compile('<a target="_blank" class="hotel_item_pic  haspic" title="(.*?)" href=".*?" data-hotel=".*?" data-ctm=".*?" tracekey=".*?" tracevalue=".*?" >',re.S)
        title=re.findall(res1,response.text)
        res2=re.compile('''<p class="hotel_item_htladdress">(.*?) <a href="javascript''',re.S)
        address=re.findall(res2,response.text)
        res3=re.compile(''' title="客戶點評:(.*?),總分5分。">(.*?)<span class="(.*?)">.*?</span><span class="total_judgement_score"><span style='color:#009933;'>(.*?)</span>使用者推薦</span><span class="hotel_judgement">源自<span style='color:#FF9900;'>(.*?)</span>位住客點評</span>''',re.S)
        socre=re.findall(res3,response.text)
        res4=re.compile('<span class="J_price_lowList">(.*?)</span>',re.S)
        price=re.findall(res4,response.text)
        yield [title,address,socre,price] 
       # yield address
def process_strings(item) :#####資料處理
    for m in range(len(item[2])):
        if '】'in item[1][m]:
            item[1][m]=item[1][m].split('】')[1] 
        if item[2][m][1] == '' :
            level=item[2][m][1]     
        else :
            lev=re.findall('<span class="hotel_level">(.*?)</span>',item[2][m][1])
            level=lev[0]
       # print(item[2][m])
        if item[2][m][2] == 'no_grade':
            level='暫無'
        quality=level+'評分'+item[2][m][0]+',總分5.0分,'+item[2][m][3]+'推薦,'+'源自'+item[2][m][4]+'顧客評分'
        reps={'name':item[0][m],
               'address' : item[1][m],
               'quality' : quality,
                'price' : item[3][m]  }
        yield reps


最後將爬取到的資料儲存在MongoDB資料庫中,這裡簡單說下MongoDB資料庫

MongoDB資料庫是一個基於分散式檔案儲存的開源資料庫系統,其內容儲存形式類似於JSON格式,相對於MySQL來說,MongoDB相對靈活。具體的MongoDB安裝以及用法參考下面文件

詳細圖解mongodb 3.4.1 win7x64下載、安裝、配置與使用2017/01/16  原創作者 李學凱

https://blog.csdn.net/qq_27093465/article/details/54574948

該部分程式碼如下:

def insert(infor) :
    client=MongoClient()
    db=client['ctrip_company']
    collection=db['hotels information in Nanjing']
    collection.insert_one(infor)  

附:完整程式碼

# -*- coding: utf-8 -*-
"""
Created on Sun Aug 19 15:19:40 2018

@author: NJUer
"""
import requests,re
from pymongo import MongoClient
origin_url='http://hotels.ctrip.com/hotel/nanjing12' 
urls=[]
def infor_pages():     
    origin_infor=requests.get(origin_url,headers={'User-Agent':'Mozilla/5.0' })
    origin_infor.encoding='utf-8'
    res=re.compile('<a rel="nofollow" data-dopost="T" href=".*?" data-type="page" data-value=".*?">(.*?)</a>')
    pages=re.findall(res,origin_infor.text)
    return pages
def url(pages):
    for i in range (1,int(pages[0])+1):
        url=origin_url+'/p'+str(i)
        urls.append(url)
    return urls
def items(urls):
    for url in urls :
        response=requests.get(url,headers={'User-Agent':'Mozilla/5.0' })
        response.encoding='utf-8'
        res1=re.compile('<a target="_blank" class="hotel_item_pic  haspic" title="(.*?)" href=".*?" data-hotel=".*?" data-ctm=".*?" tracekey=".*?" tracevalue=".*?" >',re.S)
        title=re.findall(res1,response.text)
        res2=re.compile('''<p class="hotel_item_htladdress">(.*?) <a href="javascript''',re.S)
        address=re.findall(res2,response.text)
        res3=re.compile(''' title="客戶點評:(.*?),總分5分。">(.*?)<span class="(.*?)">.*?</span><span class="total_judgement_score"><span style='color:#009933;'>(.*?)</span>使用者推薦</span><span class="hotel_judgement">源自<span style='color:#FF9900;'>(.*?)</span>位住客點評</span>''',re.S)
        socre=re.findall(res3,response.text)
        res4=re.compile('<span class="J_price_lowList">(.*?)</span>',re.S)
        price=re.findall(res4,response.text)
        yield [title,address,socre,price] 
       # yield address
def process_strings(item) :#####資料處理
    for m in range(len(item[2])):
        if '】'in item[1][m]:
            item[1][m]=item[1][m].split('】')[1] 
        if item[2][m][1] == '' :
            level=item[2][m][1]     
        else :
            lev=re.findall('<span class="hotel_level">(.*?)</span>',item[2][m][1])
            level=lev[0]
       # print(item[2][m])
        if item[2][m][2] == 'no_grade':
            level='暫無'
         quality=level+'評分'+item[2][m][0]+',總分5.0分,'+item[2][m][3]+'推薦,'+'源自'+item[2][m][4]+'顧客評分'
        reps={'name':item[0][m],
               'address' : item[1][m],
               'quality' : quality,
                'price' : item[3][m]  }
        yield reps
def insert(infor) :
    client=MongoClient()
    db=client['ctrip_company']
    collection=db['hotels information in Nanjing']
    collection.insert_one(infor)       
def main () :
    pages=infor_pages()
    urls=url(pages)
    for item in items(urls) :
        for infor in process_strings(item):
            insert(infor)
if __name__=='__main__' :
    main()

執行之後的部分結果截圖如下:

原創不易,如若轉載,請註明出處和作者,謝謝!