以前就利用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()
运行之后的部分结果截图如下:
原创不易,如若转载,请注明出处和作者,谢谢!