首页 👨‍💻计算机,🐍Python

携程爬虫代码

[btnblue href="https://gitee.com/Lavines/xiecheng" target="blank"]源码下载地址[/btnblue]
本篇文章过长建议开启目录查看

from selenium import webdriver
import time
from pc import data_bool
import random
bool_=data_bool()
driver=webdriver.Chrome()
driver.get('https://vacations.ctrip.com/list/whole/d-guangzhou-152.html?cityname=%E6%AD%A6%E6%B1%89&s=4&salecity=477&startcity=477')

1.分析网页结构

2.使用selenium-webdriver 获取全部数据

3.通过分析 网页结构发现 大部分数据都是可以 使用css选择器来获取的

4.最后形成一个二维列表存储成csv

#通过分析 网页结构发现 大部分数据都是可以 使用css选择器来获取的
def result():
    parent=driver.find_element_by_class_name('main_col')
    #获取 景点介绍多信息_________________________
    #text解析
    try:
        str_introduce=parent.find_elements_by_css_selector('p.list_product_title')
    except:
        str_introduce=[]
        print('头部获取了空数值')
    # 跟团 或者 地点出发地_______________________
    #发现规律 切片[0:3] 和 切片[3:8]
    #text[0:3] 和 text[3:8] 解析
    try:
        str_product=parent.find_elements_by_css_selector('p.list_product_tip')
    except:
        str_product=[]
        print('跟团或地点获取了空数值')
    #景点标签_______________________
    # text解析
    try:
        list_box=parent.find_elements_by_css_selector('div.list_label_box')
        list_box_itme=[]
        for i in list_box:
            list_box_itme.append(i.find_elements_by_css_selector('span'))
    except:
        list_box_itme=[]
        print('列表获取了空数值')
    #供应商_______________________
    #解析text
    try:
        list_retail=parent.find_elements_by_css_selector('p.list_product_retail')
    except:
        list_retail=[]
        print('供应商获取了空数值')
    #评分_______________________
    #解析text

    try:
        str_grade=parent.find_elements_by_css_selector('p.list_change_grade')
    except:
        str_grade=[]
        print('评分获取了空数值')
    #出行人数_______________________
    #解析text
    try:
        str_number=parent.find_elements_by_css_selector('div.list_change_one')
    except:
        str_number=[]
        print('出行人数获取了空数值')
    #点评数量 _______________________
    #解析text
    try:
        str_remark=parent.find_elements_by_css_selector('div.list_change_two')
    except:
        str_remark=[]
        print('点评数量获取了空数值')
    #价格_______________________
    #解析text
    try:
        str_price=parent.find_elements_by_css_selector('span.list_sr_price')
    except:
        str_price=[]
        print('价格获取了空数值')
    #   添加主要数据_______________________
    try:
        rank=[]
        for i in range(len(str_grade)): #以供应商的列表长度作为循环范围合理
            box=[]
            introduce=str_introduce[i].text#景点介绍多信息

            product=str_product[i].text[0:3]#跟团游还是自驾游---在完成项目时发现这个字段没啥用

            product2=str_product[i].text[3:8]#哪里出发---在完成项目时发现这个字段没啥用

            retail=list_retail[i].text #供应商

            corporation=list_retail[i].get_attribute('title') #公司名称---在完成项目时发现这个字段没啥用

            grade=str_grade[i].text#评分

            number=str_number[i].text#出行人数

#             print(str_number[i],i) #输出索引 放遍观察索引问题

            remark=str_remark[i].text#点评人数

            price=str_price[i].text #价格

            item=list_box_itme[i] #每一个 list_box_item
            for j in item: #标签数据添加
                box.append(j.text)
              #添加到 rank列表  
            rank.append([introduce,product,product2,retail,corporation,grade,number,remark,price,box])
        bool_.MemoryCsv(rank,'携程旅游数据测试','a')
        parent.find_element_by_css_selector('a.down').click() #寻找 点击下一页元素 进行点击
    except:
        parent.find_element_by_css_selector('a.down').click()# 如果数据缺失 寻找点击下一页元素 进行点击
        print('数据丢失了\n')
def range_(begin,end):
    for i in range(begin,end):
        result()
        time.sleep(random.randint(20,25))
        print(f'第{i}页')
range_(0,31)
携程旅游数据测试 存储成功
第0页
携程旅游数据测试 存储成功
第1页
携程旅游数据测试 存储成功
第2页
携程旅游数据测试 存储成功
第3页
数据丢失了
第4页
携程旅游数据测试 存储成功
第5页
携程旅游数据测试 存储成功

分析并且进行数据清洗

.去除重复项---完成

. 删除含有NaN的一行---完成

. 重置索引---完成

. 信息字段---- 提取-地区,提取---游玩时间---游玩类型(三个字段)---完成

.旅游方式---删除(数据混乱,提取信息字段的游玩类型)----完成

. 出发地----删除 (数据混乱,利用价值低)----完成

.供应商--- 提取供应商---完成

. 公司----处理空格值------完成

. 评分---提取数值,数据类型为float---完成

. 出游人数---提取数值,类型为int ---完成

. 点评---提取数值,类型为int

.价格---提取数值,类型为float---完成

. 标签----数据展示文件中处理----待定

.处理出游时间---完成

. 重置索引----完成

. 存储处理完毕的数据----完成

from pandas import DataFrame,Series
import pandas as pd
import numpy as np
from pc import data_bool
from fuzzywuzzy import process
from fuzzywuzzy import fuzz
f:\python\lib\site-packages\fuzzywuzzy\fuzz.py:11: UserWarning: Using slow pure-python SequenceMatcher. Install python-Levenshtein to remove this warning
  warnings.warn('Using slow pure-python SequenceMatcher. Install python-Levenshtein to remove this warning')
df=pd.read_csv('携程旅游数据.csv',header=None ,names=['信息','旅游方式','出发地','供应商','公司','评分','出游人数','点评','价格','标签'])
df.info()
df.head(1)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7917 entries, 0 to 7916
Data columns (total 10 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   信息      7917 non-null   object
 1   旅游方式    7917 non-null   object
 2   出发地     7917 non-null   object
 3   供应商     7917 non-null   object
 4   公司      7917 non-null   object
 5   评分      7917 non-null   object
 6   出游人数    7781 non-null   object
 7   点评      7917 non-null   object
 8   价格      7917 non-null   object
 9   标签      7917 non-null   object
dtypes: object(10)
memory usage: 618.6+ KB
信息 旅游方式 出发地 供应商 公司 评分 出游人数 点评 价格 标签
0 无锡+拈花湾2日1晚跟团游·【金秋特惠季 ·2成人免1儿童】千人好评·口碑地接『夜宿网红拈花... 跟团游 上海出发 供应商:誉江南 杭州远景国际旅行社有限公司 5.0分 累计6560人出游 1781条点评 ¥349起 ['安心游', '无购物', '成团保障', '寺院禅修', '外籍可订', '赏花', '...
df=df.drop(['旅游方式','出发地'],axis=1)
df.drop_duplicates(inplace=True)#在原有对象上去除重复项
df=df.dropna(axis=0,how='any') #删除全部含有NaN的行
df=df.reset_index(drop=True) #重置索引
df.info() 
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7179 entries, 0 to 7178
Data columns (total 8 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   信息      7179 non-null   object
 1   供应商     7179 non-null   object
 2   公司      7179 non-null   object
 3   评分      7179 non-null   object
 4   出游人数    7179 non-null   object
 5   点评      7179 non-null   object
 6   价格      7179 non-null   object
 7   标签      7179 non-null   object
dtypes: object(8)
memory usage: 448.8+ KB

1,信息字段处理

df['条件'] = df['信息'].apply(lambda x:x.split('·')).apply(lambda x:x[0]) ##提取新的一列方便观察 提取
df['地点'] = df['条件'].str.extract(('(.*?)\d+.*?')).astype('str') ## 根据条件一列 筛选出地点 并且把NaN值转为str
df['地点'] = df['地点'].apply(lambda x:x.split('+'))#通过分隔符+ 把多个地点转为列表类型
df['游玩时间'] = df['条件'].str.extract(('.*?(\d+日\d+晚).*?'))#通过str.extract 正则匹配 日期
df['出游方式'] = df['条件'].apply(lambda x:x[-3:])#出游方式 条件一列后三位字符串
# df

2,供应商字段处理

df['供应商']=df['供应商'].apply(lambda x:x.split(':')[1].split(',')[0])

3,评分字段处

# df['评分'].value_counts() ##第一步分组查找脏数据 未发现
df['评分']=df['评分'].apply(lambda x:x.split('分')[0]).astype('float')

4,出游人数字段处理

def replace_d(x): ##处理 带万人数乘10000
    result=[]
    for i in  x:
        b=i.split('万')
        if b[-1]=='':
            result.append(float(b[0])*10000)
        else:
            result.append(b[0])
    return result
df['出游人数']=df['出游人数'].apply(lambda x: '累计0人出游' if '分' in  x else x)
# for i in df['出游人数'].value_counts().index: ##查找脏数据 找到评分在本列
#     print(i)
df['出游人数']=df['出游人数'].str.extract(('(\d+.\d+万|\d+)')).astype('str')
df['出游人数']=replace_d(df['出游人数'])
df['出游人数']=df['出游人数'].astype('int') 

5,点评数量字段处理

df['点评']=df['点评'].apply(lambda x:x.split('条点评')[0]).apply(int)

6,价格字段处理

df['价格']=df['价格'].str.replace('实时计价','0') #调用 取消实时计价 替换乘平均值

df['价格']=df['价格'].str.extract(('¥(\d+)')).astype('float')
df['价格']=df['价格'].fillna(df['价格'].mean()) #替换NaN 为平均值

7,标签字段处理

df['标签']=df['标签'].apply(eval)

8,游玩时间处理

#处理空值  填充前一个值
df['游玩时间']=df['游玩时间'].fillna(method='ffill')

9,进一步 细致处理数据

df=df.drop(['信息','条件'],axis=1)
df=df.dropna(axis=0,how='any') #删除全部含有NaN的行
df.reset_index(drop=True) #重置索引
供应商 公司 评分 出游人数 点评 价格 标签 地点 游玩时间 出游方式
0 誉江南 杭州远景国际旅行社有限公司 5.0 6560 1781 349.0 [安心游, 无购物, 成团保障, 寺院禅修, 外籍可订, 赏花, 夜宿拈花湾] [无锡, 拈花湾] 2日1晚 跟团游
1 润江南 江苏五方国际旅行社有限公司 5.0 4812 1531 899.0 [安心游, 无购物, 成团保障, 外籍可订, 古镇古村] [上海, 苏州, 乌镇, 杭州] 4日3晚 跟团游
2 携程自营 上海携程国际旅行社有限公司 5.0 6407 1388 2859.0 [安心游, 无购物, 无自费, 成团保障, 外籍可订, 湖泊, 宗祠, 夜宿乌镇, 当地美食] [上海, 苏州, 杭州, 乌镇] 5日4晚 跟团游
3 舟山佛海旅行社 舟山佛海旅行社有限公司 5.0 1220 379 538.0 [安心游, 成团保障, 外籍可订, 寺院祈福] [浙江普陀山] 2日1晚 跟团游
4 携程自营 上海携程国际旅行社有限公司 5.0 1105 330 977.0 [安心游, 无购物, 动车高铁游, 外籍可订, 夜宿乌镇, 当地美食] [上海, 苏州, 杭州, 乌镇] 5日4晚 跟团游
... ... ... ... ... ... ... ... ... ... ...
7174 酷游旅行 广州酷游旅行社有限公司 1.3 2 1 1092.0 [温泉酒店] [江门] 2日1晚 自由行
7175 趣旅 广州市趣旅国际旅行社有限责任公司 1.0 29 1 488.0 [安心游, 无购物, 温泉体验] [广东从化区] 2日1晚 自助游
7176 广东中信国旅 广东中信国际旅行社有限公司 1.0 24 1 126.0 [安心游] [惠州] 3日2晚 自助游
7177 潮汕味道 深圳市方向旅行社有限责任公司 1.0 18 1 5382.0 [安心游, 无购物, 成团保障, 动车高铁游, 古镇古村, 宗祠, 博物馆, 全程五星酒店] [广州, 佛山, 顺德区] 5日4晚 私家团
7178 趣旅 广州市趣旅国际旅行社有限责任公司 1.0 10 1 349.0 [安心游, 海滨/沙滩] [广东惠州] 2日1晚 自助游

<p>7179 rows × 10 columns</p>

df.head(1)
供应商 公司 评分 出游人数 点评 价格 标签 地点 游玩时间 出游方式
0 誉江南 杭州远景国际旅行社有限公司 5.0 6560 1781 349.0 [安心游, 无购物, 成团保障, 寺院禅修, 外籍可订, 赏花, 夜宿拈花湾] [无锡, 拈花湾] 2日1晚 跟团游
df.to_csv('清洗(完)携程旅游数据.csv')

数据可视化

from pandas import DataFrame,Series
import numpy as np
import pandas as pd
from pyecharts import options as opts
from pyecharts.globals import SymbolType
from pyecharts.charts import *
from pyecharts.faker import Faker
from pyecharts.globals import ChartType, SymbolType
df=pd.read_csv('清洗(完)携程旅游数据.csv')
df=df.drop(['Unnamed: 0'],axis=1)
df

image-20201024144817791

merchant=df['供应商'].value_counts(ascending=False).head(10) #供应商前十 纵向柱状图
number=df.groupby('供应商')['出游人数'].sum().sort_values(ascending=False).head(10).sort_values(ascending=True) #供应商出游前十 #横向柱状图
way=df['出游方式'].value_counts().head(4) #出游方式 #饼图
cut_bins=[0,300,800,2500,5000,8000,16000,999999]
cut_labels=['0~300元','300~800元','800~2500元','2500~5000元','5000~8000元','8000~1.6万元','1.6万以上']
price_cut=pd.cut(df['价格'],bins=cut_bins,labels=cut_labels)
price_num=price_cut.value_counts()
data_pair=[list(z) for z in zip(price_num.index.tolist(),price_num.values.tolist())]
# 供应商旅游价格区间   圆环图
# 那种出游时间最受欢迎
date=df['游玩时间'].value_counts()
corr=df[['供应商','点评','评分']].groupby('供应商')['点评','评分'].mean().sort_values(by=['点评','评分'],ascending=False)
corr['点评']=corr['点评'].apply(int) #转为in
corr['评分']=corr['评分'].apply(lambda x:x).round(1) #保留一位小数
corr=corr['评分'].head(12) #提取评分  趋势图
df['地点']=df['地点'].apply(eval)

site=df['地点'].apply(Series).apply(pd.value_counts).sum(axis=1).sort_values(ascending=False).apply(int) #地图 地图
a=[z for z in zip(site.index.tolist(), site.values.tolist())]

供应商前十

# 条形图


bar1=Bar(init_opts=opts.InitOpts(width='900px',height='500px'))
bar1.add_xaxis(merchant.index.tolist())
bar1.add_yaxis('',merchant.values.tolist())
bar1.set_global_opts(title_opts=opts.TitleOpts(title='携程前十供应商排名'),
                     xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-15))
                    )
bar1.render_notebook()

image-20201024145346373

供应商前十 累计出行人数

#横向柱状图
x_data=number.index.tolist()
y_data=number.values.tolist()
bar1=Bar()
bar1.add_xaxis(x_data)
bar1.add_yaxis('',y_data)
bar1.set_global_opts(title_opts=opts.TitleOpts(title='携程供应商出游人数Top10排名'),
                    )
bar1.set_series_opts(label_opts=opts.LabelOpts(position='right'))
bar1.reversal_axis()
bar1.render_notebook()

image-20201024145412960

游玩时间 可以拉动的柱状图

#可以拉动的柱状图
c = (
    Bar()
    .add_xaxis(date.index.tolist())
    .add_yaxis("", date.values.tolist(), color=Faker.rand_color())
    .set_global_opts(
        title_opts=opts.TitleOpts(title="游玩时间分析"),
        datazoom_opts=[opts.DataZoomOpts(), opts.DataZoomOpts(type_="inside")],
    )
    .render_notebook()
)
c

image-20201024145437256

出游方式分布 饼图

#饼图
data_pair = [list(z) for z in zip(way.index.tolist(),way.values.tolist())]
pie1 = Pie()
pie1.add('',data_pair)
pie1.set_global_opts(title_opts=opts.TitleOpts(title='出游方式整体表现'),
                    legend_opts = opts.LegendOpts(orient='verical',pos_top='15%',pos_left='2%'))
pie1.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{d}%"))
pie1.set_colors(['#EF9050','#3B7BA9','#6FB27C','#FFAF34','#D8BFD8','#00BFFF','#7FFFAA'])
pie1.render_notebook()

image-20201024145505124

圆环图价格区间

# 绘制环图
pie1 = Pie()
pie1.add('',data_pair,radius=['35%','60%'])
pie1.set_global_opts(title_opts=opts.TitleOpts(title='不同价格区间人数占比'),
                    legend_opts = opts.LegendOpts(orient='verical',pos_top='15%',pos_left='2%'))
pie1.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{d}%"))
pie1.set_colors(['#EF9050','#3B7BA9','#6FB27C','#FFAF34','#D8BFD8','#00BFFF','#7FFFAA'])
pie1.render_notebook()

image-20201024145549899

## 折线图  点评数量最多的十个供应商的平均评分
x1 = corr.index.tolist()
y1 = corr.values.tolist()
c=(
    #init_opts=opts.InitOpts(width="1500px"),设置可是画图的长度,1500像素
    Line()
    .add_xaxis(xaxis_data=x1)
    .add_yaxis(
        series_name='供应商平均评分折线图',
        y_axis=y1,
    )
    .set_global_opts(
        #
        tooltip_opts=opts.TooltipOpts(is_show=False),
        xaxis_opts=opts.AxisOpts(
            axislabel_opts={"rotate":-31}
        ),
        yaxis_opts=opts.AxisOpts(
            #分割线配置,显示 y 轴每个刻度的分割线
            splitline_opts=opts.SplitLineOpts(is_show=True),
            max_=5,
            min_=4.5
            )
    )
    .render_notebook()
)
c

image-20201024145616828

绘制地图 --旅游地点

c = (
    Map()
    .add("", a, "china")
    .set_global_opts(title_opts=opts.TitleOpts(title="旅游地点地图分布"),
        visualmap_opts=opts.VisualMapOpts(max_=300),)
    .render_notebook()
)
c

image-20201024145652342

绘制词云

import copy
df['标签']=df['标签'].apply(eval)
word=df['标签'].apply(Series).apply(pd.value_counts).sum(axis=1).sort_values(ascending=False)
word=word.astype('str')
words=[ list(i) for i in zip(word.index,word.values)] # 分析标签数量  做一个词云
dp_copy=copy.deepcopy(words)
#词云变量丢失 
#深度拷贝即可, 
#因为 二维列表拷贝的是一维列表的内存地址
#二维列表里面保存的是一堆 一维列表的地址,并不是二维列表
#词云图
c = (
    WordCloud()
    .add("",a,word_size_range=[20, 50], shape=SymbolType.DIAMOND)
    .set_global_opts(title_opts=opts.TitleOpts(title="标签词云图"))
     .render_notebook()
)
c

image-20201024145735404

[greenbar]由此上图词云显示 无购物,无自费 ,安心游,成团保障, 成为大部分供应商必备的条件,这样的供应商才最受游客们的欢迎 ,

供应商能不能提供一条龙服务 让游客玩得放心,住得安心,能够有效提高旅游消费很是重要,以及成团保障 能不能保障 游客们可以按时出发,非常重要

近些年来个别供应商 强制游客消费 未经游客同意私自安排购物,也是游客最为在意的一点[/greenbar]




文章评论

目录