1. 项目目的
俗话说,“巧妇难为无米之炊”。在数据科学道路上,数据获取是数据利用、分析等后续工作中的重要前提。虽然说,如今有许多开源的数据集,但是,锻炼自己从浩如烟海的网络中获取原始数据的能力,对于培养数据科学的基础技能是十分重要的。
2. 需求分析
本文目的是根据好评优先顺序,爬取携程网上的北京五星级酒店列表。
3. 实验环境
- 语言:Python 3.7
- 操作系统:MacOS
- 编程IDE:Pycharm
- 浏览器:Chrome
4. 具体实现
这一部分是本文的重点,将按照基本爬虫思路进行记录。
4.1 分析页面
对于一个学生而言,拿到一道题,首先要做的工作是熟读题目、进行分析,而不是马上下笔答题。同理,爬虫之前,认真分析要爬取的页面,会使得工作事半功倍。
- 打开携程网.
在页面上选择:
- 城市:北京
- 入住日期:2019-02-19
- 退房日期:2019-02-20
- 房间数:1间
- 住客数:2人
- 酒店级别:五星级/豪华
如图1所示:
图1 携程主页及酒店信息选择
- 分析北京五星级酒店页面
点击图1中的“搜索”按钮,在酒店列表页面中选择好评优先排序方式。如图2所示。
图2 北京五星级酒店列表页面
- 寻找页面接口
因为每一个酒店都是可点击跳转的,即可交互,所以,为动态页面。静态页面可以直接通过页面源码,实现获取信息;动态页面需要找寻接口,然后从其接口的源码中获取信息。
在页面空白处,点击右键,选择“检查”,可以看到页面的源码。找寻接口,必须选中第一行的“Network”选项。
点击页面刷新按钮,即可在Filter中输入“Hotel”,选择“XHR”,即可找到页面接口。
图3 酒店列表页面接口 由图3可以看到,左部分列表中的“AjaxHotelList.aspx”就是需要的接口,在右侧的preview中,可以看到,页面上的酒店列表就存放于“hotelPositionJSON”中。这就意味着,请求这个接口,获取其响应中的hotelPositionJSON,即可获得酒店列表信息。
4.2 请求
这是一个B/S架构,如图4所示:
图4 B/S基本请求响应图 图4主要展现的是一个请求–响应关系图。Chrome浏览器向携程服务器请求接口URL,携程服务器将结果返回给浏览器进行呈现。
注:
- 图4主要呈现一个请求–响应关系,而不是严格的HTTPS协议握手过程。
- Cookie是保存在浏览器端中的,而Session保存在服务器端。
4.2.1 请求对象
请求对象,即我们需要的酒店列表接口,用URL进行表示。
five_star_url:http://hotels.ctrip.com/Domestic/Tool/AjaxHotelList.aspx
4.2.2 请求方法
利用Python中的requests包,进行请求。通过Chrome浏览器的开发者选项分析,该请求类型为POST型。POST型请求大多用于提交表单、上传文件等操作。
# 1. Send request.
html = requests.post(url)
4.2.3 请求头和请求体
根据计算机网络的知识,网络中以数据包的形式进行请求,这里不再赘述。
数据包包含请求头和请求内容(即请求体)。
- 请求头
在爬虫过程中,若不设置请求头,频繁请求服务器,可能会造成客户端的IP被服务器端所封闭等后果,所以,需要对请求头进行伪造。
- User-agent是一个特殊字符串,可以使服务器识别客户使用的操作系统及版本 、 浏览器及版本等信息。在做爬虫时加上此信息,可以伪装为浏览器。
headers = {
"Connection": "keep-alive",
"origin": "http://hotels.ctrip.com",
"Host": "hotels.ctrip.com",
"referer": "http://hotels.ctrip.com/hotel/beijing1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36",
"Content-Type":"application/x-www-form-urlencoded; charset=utf-8"
}
请求头headers主要是为了伪造user-agent参数。
- 请求体
请求体,即请求内容。在4.1中,我们讲述了要获取的页面内容,即入住时间、退房时间等,请求内容即为这一部分的代码形式。
# 8 pages
for page in range(1,8):
data = {
"StartTime": "2019-02-19", # The value depends on the date you want to scrap.
"DepTime": "2019-02-20",
"RoomGuestCount": "0,1,2",
"cityId": 1,
"cityPY": " beijing",
"cityCode": "010",
"cityLat": 39.9105329229,
"cityLng": 116.413784021,
"page": page,
"star": 5,
"orderby": 3
}
这里说明一下,因为北京五星级酒店有8页,所以构建一个循环,即可实现对每一页进行请求。
所以,请求的代码修改如下:
html = requests.post(url=five_star_url, headers=headers, data=data)
4.3 响应
在4.1的第3点中,通过Chrome浏览器的开发者选项中的“网络”选项,我们获知,需要的北京五星级酒店列表在响应的hotelPositionJSON中。
获取hotelPositionJSON对象中,即可得到酒店ID、酒店名称、酒店地址、酒店评分、酒店URL。
hotel_list = html.json()["hotelPositionJSON"]
for item in hotel_list:
id.append(item['id'])
name.append(item['name'])
hotel_url.append(item['url'])
address.append(item['address'])
score.append(item['score'])
4.4 保存响应结果
在4.3中,我们爬取的结果是保存在列表中的。想要以csv文件的形式进行存储,需要把借助Python中的csv模块,即可实现。
- 首先用Numpy模块,将列表整合成矩阵的形式;
- 其次调用csv.writer,将矩阵写入csv文件中。
注意:编码形式为“utf8-sig”,否则汉字容易出现乱码情况!
5. 完整代码及结果
5.1 完整代码
使用函数的形式,对爬取北京五星级酒店列表进行封装。
# coding=utf8
import numpy as np
import pandas as pd
from bs4 import BeautifulSoup
import requests
import random
import time
import csv
import json
import re
# Pandas display option
pd.set_option('display.max_columns', 10000)
pd.set_option('display.max_rows', 10000)
pd.set_option('display.max_colwidth', 10000)
pd.set_option('display.width',1000)
# Beijing 5 star hotel list url
five_star_url = "http://hotels.ctrip.com/Domestic/Tool/AjaxHotelList.aspx"
# Save the csv file in the Data Directory
filename = "./Data/Beijing 5 star hotel list.csv"
def Scrap_hotel_lists():
"""
It aims to crawl the 5 star hotel lists in Beijing and save in a csv file.
"""
headers = {
"Connection": "keep-alive",
"origin": "http://hotels.ctrip.com",
"Host": "hotels.ctrip.com",
"referer": "http://hotels.ctrip.com/hotel/beijing1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36",
"Content-Type":"application/x-www-form-urlencoded; charset=utf-8"
}
id = []
name = []
hotel_url = []
address = []
score = []
# 8 pages
for page in range(1,8):
data = {
"StartTime": "2019-02-19", # The value depends on the date you want to scrap.
"DepTime": "2019-02-20",
"RoomGuestCount": "0,1,2",
"cityId": 1,
"cityPY": " beijing",
"cityCode": "010",
"cityLat": 39.9105329229,
"cityLng": 116.413784021,
"page": page,
"star": 5,
"orderby": 3
}
html = requests.post(five_star_url, headers=headers, data=data)
hotel_list = html.json()["hotelPositionJSON"]
# Get the response information
for item in hotel_list:
id.append(item['id'])
name.append(item['name'])
hotel_url.append(item['url'])
address.append(item['address'])
score.append(item['score'])
# Random sleep for some seconds.
# It helps to avoid being blocked by the server.
time.sleep(random.randint(3,5))
# Form the array
hotel_array = np.array((id, name, score, hotel_url, address)).T
list_header = ['id', 'name', 'score', 'url', 'address']
array_header = np.array((list_header))
hotellists = np.vstack((array_header, hotel_array))
with open(filename, 'w', encoding="utf-8-sig", newline="") as f:
csvwriter = csv.writer(f, dialect='excel')
csvwriter.writerows(hotellists)
if __name__ == "__main__":
# Scrap 5 star hotel list in Beijing and save as a csv file
Scrap_hotel_lists()
# Pandas read the csv file and print the DataFrame object
df = pd.read_csv(filename, encoding='utf8')
print("1. Beijing 5 Star Hotel Lists")
print(df)
5.2 运行结果
- 代码运行结果
图5 代码运行截图
- 保存的csv文档结果
图6 保存为csv文件截图
6. 总结
本文基于Python3,实现了对携程网页上,北京五星级酒店列表的爬取。未来工作是要完成对酒店具体详情信息进行爬取。
谢谢大家!