利用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="
點開這些連結我們看到酒店別的資料,不同的連結資料不同,而且在原始碼中也得到了所需要爬取的資料結果,於是我們可以得到了在不同的資料的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()
執行之後的部分結果截圖如下:
原創不易,如若轉載,請註明出處和作者,謝謝!