携程酒店数据爬取2020.5

携程酒店数据爬取2020.5

1. 开题

目前网上有好多爬取携程网站的教程,大多数通过xpath,beautifulsoup,正则来解析网页的源代码。然后我这个菜b贪方便,直接copy源码的xpath,paste在xpath helper改改规则。xpath helper识别出结果后,我就已经看到了成功的希望,xpath真香。在这里插入图片描述
然而,意想不到的是,在进行测试时,却匹配不出任何结果,我手中的鸡腿(xpath)就不香了,曾一度怀疑,是我水平太菜了,没写正确,然后一直回去学xpath知识,一边学啊,一边测试,甚至中途换了beautifulsoup,正则,selenium(建议不要用selenium,打开网站时候会自动跳转到携程账号登录页面,估计是携程反爬的设置)。直到后来才发现解析的源代码根本就没包含我想要的信息(酒店名称,酒店地址这些东西,携程有可能设置了反爬),这也说明了,我水平真的菜,才会一直没发现源代码的错误(我暂且认为源代码中没有显示酒店名称,地址这类信息)

>>> hotel_name = html.xpath('//p[@class="hotel_item_htladdress"]/text()')    
>>> print(hotel_name)
[]
>>> hotel_name = html.xpath('//li[@class="hotel_item_name"]/h2')
>>> print(hotel_name)
[]

2.人肉搜索器的变化

因为写不出来代码,而业务上需要信息,故我这个菜b只能一边Ctrl C + Ctrl v,手指都快抽筋了,才把部分数据给复制下来,最后只能祭出GitHub大法了
在这里插入图片描述
在看完了几个大佬写的代码,看不懂还是看不懂。
但是抄,我是专业的,这孩子从小就是抄袭砖家,Ctrl A + Crtl c + crl v 三键下去,一气呵成。
感谢让抄的老哥(author: songwei,附上他的网址:https://github.com/songweiwei/pachong/blob/master/main.py#L29)
在抄完之后要干吗?改啊!
要怎么改啊?针对这一点,我是会的。
同学们,小葵花妈妈开课了,孩子会抄,不会改,怎么办呢?多半是上课没好好学,打一顿就好了。咳咳,偏题了。
首先呢,我们先看看携程网站的网址,它的网址是固定了,也就是不管你翻到第几页,它的网址一成不变。稍微学过点爬虫的同学都知道,我们爬取网站都是通过网址的页数进行翻页,那没能通过页数进行解析,怎么办呢?
在这里插入图片描述
此时,百度就是你的老师,你要熟练地打开网站,敲上你最喜欢的网址,准备好纸巾…呸呸呸,又飙车了。
本菜b饱览群书(抄袭大佬的观点),在网页反馈的信息中后面添加p1,p2便可实现翻页的功能:
第一页:https://hotels.ctrip.com/hotel/guangzhou32/p1
第二页:https://hotels.ctrip.com/hotel/guangzhou32/p2
在这里插入图片描述
看到这里,细心的同学就会想到,楼主这个菜b不是刚刚才说完,网页的源代码不是没有想要的信息(酒店名称,地址)吗?即便会翻页,那网页的源代码没信息,还翻个屁啊?
对,没错,翻个屁,就是没什么卵用。
于是,我们要从其他地方入手。
楼主这个菜b又饱览群书,通过百度老师,猛然发现@icmp_no_request大神的帖子(https://blog.csdn.net/pandalaiscu/article/details/87644235),于是乎,我在抄的路上又迈开一大步,学会通过接口获取数据。
在AjaxHotelList.aspx文件发现了存储信息的位置,其格式是json,其中hotelPositionJSON里面存储了25条数据(一个网页只显示25条数据)
在这里插入图片描述
hotelPositionJSON中的url是打开酒店详细信息的“钥匙”,能查看酒店的具体信息(如介绍,设施等情况)
在这里插入图片描述
但在打开酒店具体信息的网址后,我们会发现一些问题,如下图中的原始网站只显示“联系方式”,这需要手工点击“联系方式”字段才能显示电话号码,若是通过xpath解析,也只是出现个“联系方式”的字段,因此这一块需要解析原网站的网页元素,针对这一块内容,我这边是用正则来写的,没错,“正则”真香。
在这里插入图片描述
其次对于酒店品牌和集团的获取,我们这里用回了xpath进行解析(因为网页的源代码已经有我们想要的信息)。由于携程的品牌名称显示在网站的首头,而部分网站没有品牌名称(如第一种和第二种所示),只有第三钟包含我们想要的品牌名称字段(酒店品牌:希尔顿)。此外部分酒店网页也是没有显示酒店集团的元素,若是通过xpath定位,有可能导致匹配信息错误。
针对这些问题,我们通过判断返回的xpath值若是为空,则不存在该元素来规避数据错误值
在这里插入图片描述
在这里插入图片描述

3.开始征程之路

作好了以上的解析后,我们可以开始写(改大神的)代码了
3.1 构建存储器

#构建数组存储数据
#构建数组存储数据
hotel_name = [] #酒店名称
hotel_address = []  #酒店地址
hotel_iphone = []   #酒店电话
hotel_brand = [] #酒店品牌
hotel_business = [] #酒店所属集团

3.2 构建请求头
其中user-agent用了fake_useragent包

url = "https://hotels.ctrip.com/Domestic/Tool/AjaxHotelList.aspx"
headers = {
    'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Origin': 'https://hotels.ctrip.com',
    'Referer': 'https://hotels.ctrip.com/hotel/guangzhou32',
    'accept': '*/*',
    'user-agent': str(UserAgent().random)
}

3.3 构造请求字段

    #page是页码(翻页),广州cityID是32(北京cityID是1)
    formData = {
        'cityId': 32,	#城市id号
        'page': 1	#页码
    }

3.4 发起网站请求

    # 发起网络请求
    r = requests.post(url, data=formData, headers=headers)
    r.raise_for_status()
    r.encoding = r.apparent_encoding    #防止出现乱码现象

3.5 解析网站元素

       # 解析 json 文件,提取酒店数据
    js=json.loads(r.text)
    json_data = json.loads(r.text)['hotelPositionJSON']

    for item in json_data:
        hotelName = item['name']
        hotelAdress = item['address']
        hotelUrl = item['url']

        hotel_name.append(hotelName)
        hotel_address.append(hotelAdress)

        #在首页打开酒店url,获取电话号码,其中电话隐藏了,因此用正则直接提取
        new_hotelUrl = "https://hotels.ctrip.com" + item['url']
        req = requests.get(new_hotelUrl,headers=headers)
        page = req.text
        pattern = re.compile('<span id="J_realContact" data-real="(.*?)&nbsp;&nbsp',re.S)
        item_iphone = pattern.findall(page)
        hotel_iphone.append(item_iphone)    #正则写入数据的形式是列表,这里再次把列表数据写入列表
        
        #xpath解析酒店品牌+集团
        html = etree.HTML(page)
        hotelBrand = html.xpath('//*[@id="base_bd"]/div[2]/a[3]/text()')
        
        if fnmatch(str(hotelBrand),"*区*"):
            hotel_brand.append("无")
        elif len(hotelBrand) ==0:
            hotel_brand.append("无")
        else:
            hotel_brand.append(hotelBrand)
        
        hotelBusiness = html.xpath('//*[@id="hotel_info_comment"]/div/span/text()')
        
        if len(hotelBusiness) == 0:
            hotel_business.append("无")
        else:
            hotel_business.append(hotelBusiness)

3.6 存储数据

#储存
wb = workbook.Workbook()
ws = wb.active
ws.append(["酒店名称","地址",'电话号码','酒店品牌','酒店集团'])

for i in range(0,len(hotel_name)):
    ws.append([hotel_name[i],hotel_address[i],hotel_iphone[i][0],hotel_brand[i][0],hotel_business[i][0]])
#存储位置:C:\Users\Administrator
wb.save("携程酒店数据.xlsx")

4.结果展示与不足反思

本次代码由于业务和时间关系,只解析了酒店名称,地址,电话号码,酒店品牌和集团这五个字段,因此各位小伙伴若是要解析其他信息,您可以通过我这篇文章的代码思路继续进行解析。
最后我反思一下本次代码的不足:

  1. 没有设置ip代理池,若是数据量大,必然会封ip。后期用代理ip进行测试,发现一个代理ip短时间内抓100条数据就失效了
  2. 获取25条数据时,需要打开25个网页,也就说打开一个网页需要一秒时间,若是要爬取大量的数据,其时间耗费长。后期仍需努力学习啊。

在这里插入图片描述

5.完整代码

import requests
from lxml import etree
from fake_useragent import UserAgent
import os, re, json, traceback,random,time
from openpyxl import workbook  # 写入Excel表所用
from fnmatch import fnmatch

#启用时间
start = time.time()

#构建数组存储数据
hotel_name = [] #酒店名称
hotel_address = []  #酒店地址
hotel_iphone = []   #酒店电话
hotel_brand = [] #酒店品牌
hotel_business = [] #酒店所属集团

#构建请求头
url = "https://hotels.ctrip.com/Domestic/Tool/AjaxHotelList.aspx"
headers = {
    'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Origin': 'https://hotels.ctrip.com',
    'Referer': 'https://hotels.ctrip.com/hotel/guangzhou32',
    'accept': '*/*',
    'user-agent': str(UserAgent().random)
}

#主代码
for i in range(1,10):
    print("爬取第%d页" %i )
    proxy = ["103.233.152.140:8080","222.249.238.138:8080","101.4.136.34:8080","58.253.66.47:8080"]
    proxies = {"http": str(random.choice(proxy))}

    #page是页码(翻页),广州cityID是32(北京cityID是1)
    formData = {
        'cityId': 32,
        'page': i
    }

    # 发起网络请求
    r = requests.post(url, data=formData, headers=headers,proxies=proxies)
    r.raise_for_status()
    r.encoding = r.apparent_encoding    #防止出现乱码现象

    # 解析 json 文件,提取酒店数据
    js=json.loads(r.text)
    json_data = json.loads(r.text)['hotelPositionJSON']

    for item in json_data:
        hotelName = item['name']
        hotelAdress = item['address']
        hotelUrl = item['url']

        hotel_name.append(hotelName)
        hotel_address.append(hotelAdress)

        #在首页打开酒店url,获取电话号码,其中电话隐藏了,因此用正则直接提取
        new_hotelUrl = "https://hotels.ctrip.com" + item['url']
        req = requests.get(new_hotelUrl,headers=headers)
        page = req.text
        pattern = re.compile('<span id="J_realContact" data-real="(.*?)&nbsp;&nbsp',re.S)
        item_iphone = pattern.findall(page)
        hotel_iphone.append(item_iphone)    #正则写入数据的形式是列表,这里再次把列表数据写入列表
        
        #xpath解析酒店品牌+集团
        html = etree.HTML(page)
        hotelBrand = html.xpath('//*[@id="base_bd"]/div[2]/a[3]/text()')
        
        if fnmatch(str(hotelBrand),"*区*"):
            hotel_brand.append("无")
        elif len(hotelBrand) ==0:
            hotel_brand.append("无")
        else:
            hotel_brand.append(hotelBrand)
        
        hotelBusiness = html.xpath('//*[@id="hotel_info_comment"]/div/span/text()')
        
        if len(hotelBusiness) == 0:
            hotel_business.append("无")
        else:
            hotel_business.append(hotelBusiness)
    

#储存
wb = workbook.Workbook()
ws = wb.active
ws.append(["酒店名称","地址",'电话号码','酒店品牌','酒店集团'])

for i in range(0,len(hotel_name)):
    ws.append([hotel_name[i],hotel_address[i],hotel_iphone[i][0],hotel_brand[i][0],hotel_business[i][0]])
#存储位置:C:\Users\Administrator
wb.save("携程酒店数据.xlsx")

#结束
end = time.time()
print("耗时(分钟):",(end - start)/60)
print("爬取页数:",len(hotel_name)/25)
  • 10
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值