前言
疫情自从来了之后已经很久没有出去旅游过了,蹭着这段疫情好转,那肯定是要出去走一走的,这一篇其实是全国旅游中的一站,因为每个城市能玩的地方太多了,一篇文章下来肯定是写不了的,所以今天就抓取一下——兰州。如果大家喜欢这个案例的话 后续会陆续更新的。
兰州
摘录一段来自维基百科中关于兰州的简介:
兰州市,简称 兰 ,别称 金城 ,是 中华人民共和国 甘肃省 省会,国务院批复确定的中国西北地区重要的工业基地和综合交通枢纽,丝绸之路经济带的重要节点城市,西部地区重要的中心城市之一,西北地区第三大城市,“兰州—西宁城市群”中的核心城市,位于甘肃省中部。
下面是兰州的行政区域:
爬取信息
爬取的信息主要是兰州的美食和景点信息:
import pandas as pd
import re
import csv
import json
import requests
import random# 显示所有列
pd.set_option('display.max_columns', None)# 显示所有行
pd.set_option('display.max_rows', None)# 设置value的显示长度为100,默认为50
pd.set_option('max_colwidth',100)# 绘图相关
import jieba
import matplotlib.pyplot as plt
from pyecharts.globals import CurrentConfig, OnlineHostType # 事先导入,防止不出图
from pyecharts import options as opts # 配置项
from pyecharts.charts import Bar, Scatter, Pie, Line, HeatMap, Funnel, WordCloud, Grid, Page # 各个图形的类
from pyecharts.commons.utils import JsCode
from pyecharts.globals import ThemeType,SymbolType
景点爬取
下面单页爬取的代码,采用的是正则爬取的方式:
url = "https://travel.qunar.com/p-cs300026-lanzhou-jingdian-1-1"headers = {"user-agent": "个人请求头"}response = requests.get(url=url,headers=headers)
result = response.content.decode()
如果解析的HTML源码中含有双引号,那么re.findall()方法后面的字符串的最外层使用单引号
In [3]:
# 1-景点中文名称cn_title = re.findall('class="cn_tit">(.*?)<span class="en_tit">.*?</span>',result,re.S)
print("长度:",len(cn_title))
cn_title
长度: 10
Out[3]:
['甘肃省博物馆','黄河铁桥','白塔山公园','黄河母亲雕塑','兰州新区长城影视基地','石佛沟国家森林公园','黄河索道','五泉山公园','兰州极地海洋世界','兰山公园']
In [4]:
# 2-景点英文名称en_title = re.findall('<span class="en_tit">(.*?)</span>.*?</span>',result,re.S)
print("长度",len(en_title))
en_title
长度 10
Out[4]:
['Gansu Provincial Museum','Yellow River Steel Bridge','Baitashan Park','Yellow River Mother Sculpture','Lanzhou Xinqu Changcheng Yingshi Base','Lanzhou Shifo Valley National Forest Park','Huanghe Ropeway','Wuquanshan Park','Lanzhou Ocean World','Lanshan Park']
In [5]:
# 3-strategy攻略数量
strategy = re.findall('class="icon_strategy" title="攻略"></span>(.*?)</div>',result, re.S)
print(len(strategy))
strategy
10
Out[5]:
['50', '94', '40', '35', '0', '1', '3', '2', '0', '1']
In [6]:
# 4-comment点评数量comment = re.findall('class="icon_comment" title="点评"></span>(.*?)</div>',result, re.S) print(len(comment)) comment 10
Out[6]:
['1295', '2948', '793', '489', '23', '30', '46', '354', '146', '80']
In [7]:
# 5-景点地点(长沙景点排名、宁乡景点排名等) # 我们只需要"景点排名"之前的信息,代表的是地址location = re.findall('去过(.*?)的驴友', result, re.S) print(len(location)) location 10
Out[7]:
['兰州', '兰州', '兰州', '兰州', '永登', '兰州', '兰州', '兰州', '兰州', '兰州']
In [8]:
# 6-景点排名ranking = re.findall('class="ranking_sum".*?class="sum">(.*?)</span>',result,re.S) print(len(ranking)) ranking 10
Out[8]:
['7', '3', '11', '10', '0%', '56', '1', '17', '5', '49']
In [9]:
# 7-驴友 # 有多少的驴友也去过这个地方lvyou = re.findall('class="comment_sum">.*?class="sum">(.*?)</span>',result,re.S) print(len(lvyou)) lvyou 10
Out[9]:
['35%', '58%', '30%', '30%', '0%', '0%', '0%', '1%', '0%', '1%']
In [10]:
# 8-景点简介 abstract = re.findall('class="desbox">(.*?)</div>',result,re.S) print(len(abstract)) abstract 10
Out[10]:
['通过丰富精彩的展品了解古丝绸之路、唐蕃古道上多民族的文化和历史。','兰州市最为经典的地标建筑,夜晚时铁桥彩灯闪耀,周围夜景非常漂亮。','登上山顶可以俯瞰壮观的兰州城市全景,山间建筑古朴树木众多,让人感受舒心惬意。','黄河母亲雕塑现已经成为兰州的标志性雕塑,也代表着兰州形象。','', # 空值'','','公园景点以五眼名泉和佛教古建筑为主,园内丘壑起伏,林木葱郁,环境清幽。','','公园就位于山顶制高点上,可俯瞰兰州全景。']
最终爬取的结果:
美食爬取
单页信息爬取的代码,同样是基于正则表达式的爬取:总共是200页
In [2]:
url = "https://travel.qunar.com/p-cs300026-lanzhou-meishi?page=1"
headers = {"user-agent": "个人请求头"}
response = requests.get(url=url,headers=headers)
result = response.content.decode()
cn_title
In [3]:
cn_title = re.findall('cn_tit">(.*?)</span>.*?countbox',result,re.S)
In [4]:
print(len(cn_title)) cn_title 10
Out[4]:
['正宁路小吃夜市','安泊尔牛肉面(北滨河路店)','兰州大众巷美食街','马三洋芋片(兰州总店)','清真·马安军辣子牛肉面','安泊尔','杜记甜食','孙子烤肉(皋兰路店)','大漠烤肉(盘旋路店)','清真•白建强牛肉面']
score
In [5]:
score = re.findall('cur_score">(.*?)</span>.*?total_score',result,re.S)
In [6]:
print(len(score)) score 10
Out[6]:
['4.3', '4.5', '--', '4.3', '3.5', '5.0', '4.2', '3.5', '4.2', '0.0']
sublistbox
先提取整个sublistbox,然后对里面的每个子元素单独提取
In [7]:
sublistbox = re.findall('sublistbox">(.*?)</div>', result, re.S)sublistbox[:1]
Out[7]:
['<dl class="sublist_item clrfix"><dt class="sub_tit">人\u3000均</dt><dd class="sub_des">¥ 63</dd></dl><dl class="sublist_item clrfix"><dt class="sub_tit">地\u3000址</dt><dd class="sub_des des_line">白银路街道永昌南路正宁路</dd></dl><dl class="sublist_item clrfix"><dt class="sub_tit">推荐菜</dt><dd class="sub_des des_line">当地口味\t美食街\t老字号\t深夜营业</dd></dl><div class="desbox"><span class="img_doublequote img_l"></span><span class="txt">吃客云集的小吃街,地道吃食令人回味。<span class="img_doublequote img_r"></span></span>']
In [8]:
type(sublistbox)
Out[8]:
list
In [9]:
# 对sublistbox单独提取
均价
In [10]:
person_avg = []for i in range(len(sublistbox)):try:if "均" in sublistbox[i]:person_avg.append(re.findall('¥ (.*?)</dd></dl>',sublistbox[i],re.S)[0])else:person_avg.append(0)continueexcept:person_avg.append(0)
In [11]:
print(len(person_avg)) person_avg 10
Out[11]:
['63', '27', 0, '19', '17', 0, '14', '58', '58', '18']
地址
In [12]:
address = []for i in range(len(sublistbox)):try:if "址" in sublistbox[i]:address.append(re.findall('址.*?des_line">(.*?)</dd></dl>',sublistbox[i],re.S)[0])else:address.append("无")continueexcept:address.append("无")
In [13]:
print(len(address)) address 10
Out[13]:
['白银路街道永昌南路正宁路','北滨河路754号(龙源斜对面)','兰州市城关区大众巷','通渭路79号','七里河北街忠云宾馆斜对面','北滨河路金城关3号','大众巷72号','皋兰路4号(虹云宾馆北侧)','东岗西路451号','雁滩路3423号']
推荐菜
In [14]:
recommand = []for i in range(len(sublistbox)):try:if "推荐菜" in sublistbox[i]:recommand.append(re.findall('推荐菜.*?des_line">(.*?)</dd></dl>',sublistbox[i],re.S)[0])else:recommand.append("无")continueexcept:recommand.append("无")
In [15]:
print(len(recommand)) recommand 10
Out[15]:
['当地口味\t美食街\t老字号\t深夜营业','美食林风味\t当地口味\t肉汤萝卜\t三泡台\t酱牛肉\t蜂蜜油香\t安泊尔牛肉面\t牛筋\t雪梨汤\t牛腱子肉\t甜醅子\t灰豆子','马子禄牛肉面\t香满楼\t俊杰羊肉泡馍馆\t杜维成甜食店','美食林风味\t当地口味\t炸年糕\t里脊肉饼\t洋芋片\t年糕\t辣年糕\t胡萝卜汁\t豆腐皮\t油炸年糕\t炸糖年糕\t胡萝卜素饮料\t豆皮\t牛肚','无','无','当地口味\t下午茶\t老字号\t美食林风味\t高担酿皮\t甜胚子\t牛肉馅饼\t粽子\t八宝醪糟\t炒粉\t茹记杏皮水\t热晶糕\t甜醅子\t牛奶鸡蛋醪糟\t甜醅\t灰豆子','无','深夜营业\t美食林臻选\t羊腰\t凉面\t烤羊肚\t羊汤\t羊肉泡馍\t烤羊排\t烤饼\t烤羊板筋\t烤肉串\t烤羊皮\t杏皮水\t烤茄子','特色小吃\t当地口味\t其他\t兰州拉面\t牛肉面\t牛肉']
评价
In [16]:
comment = []for i in range(len(sublistbox)):try:if "desbox" in sublistbox[i]:comment.append(re.findall('.*?txt">(.*?)<span class="img_doublequote img_r">',sublistbox[i],re.S)[0])else:comment.append("无")continueexcept:comment.append("无")
In [17]:
print(len(comment)) comment 10
Out[17]:
['吃客云集的小吃街,地道吃食令人回味。','专注正宗牛肉面,开放式厨房热气腾腾','大众巷是兰州最古老的美食街,拥有许多老字号当地特色美食,是游客体验兰州美食文化的绝佳去处。','兰州代表性的小吃店,明档操作干净放心','颇具名气的牛肉面馆,当地人的家庭食堂','个人觉得安泊尔是我吃过最好吃的牛肉面了,不过这家店的位置很尴尬,只有这趟路线才能涉及到','老牌人气甜食店,荣获多个美食奖项','店里面的装修环境是非常不错的,有人说价格有点贵,看了一下菜单,相对于夜市上的来说确实有一点贵,...','人气爆棚的口碑餐厅,各式烤串喷香诱人','当地食堂级别的老店,牛肉面汤清味醇面韧。']
最终爬取结果:刚好是2000条数据
下面是对上面爬取到的两份数据进行分析:
美食数据分析
导入数据:
字段基本信息:
In [3]:
# 1-数据缺失值df.isnull().sum()
Out[3]:
中文名 0 得分 0 均价 0 地址 0 推荐菜 0 评价 0 dtype: int64
In [4]:
# 2、字段类型df.dtypes
Out[4]:
中文名 object 得分 object 均价 int64 地址 object 推荐菜 object 评价 object dtype: object
分析哪些店的得分靠前:
In [5]:
# 得分中有未评分的数据:--df["得分"].value_counts()
Out[5]:
-- 1721 3.5 139 3.0 50 4.0 29 4.5 20 5.0 13 4.3 7 4.2 6 0.0 5 4.1 4 2.0 2 3.8 1 4.7 1 2.5 1 1.0 1 Name: 得分, dtype: int64
In [6]:
# 将未评分的数据统一替换成0.0,也就是0分df["得分"] = df["得分"].apply(lambda x: x.replace("--","0.0"))
得分统计
大部分店铺的得分在3.5分,好像并不是很高~
得分前10的店铺
店铺均价占比
高档餐厅
面馆
到了兰州肯定得吃面:据数据统计有284家面馆
火锅店
统计出来有129家火锅店:
px.bar(huoguo[:15],x="中文名",y="得分")
美食统计信息
推荐菜词云
绘制当地推荐菜的词云图:
rec_words = [tuple(z) for z in zip(result["词语"].tolist(), result["次数"].tolist())]# 选择前100个词语
c = (WordCloud(init_opts=opts.InitOpts(theme=ThemeType.CHALK)) .add("", rec_words[:100], word_size_range=[20, 80], shape=SymbolType.DIAMOND).set_global_opts(title_opts=opts.TitleOpts(title="兰州美食词云图"))
)c.render_notebook()
景点数据分析
还是先导入数据
数据探索
In [3]:
df1.shape
Out[3]:
(381, 8)
In [4]:
df1.isnull().sum()
Out[4]:
cn_title 0 en_title 113 strategy 0 comment 0 location 0 ranking 0 lvyou 0 abstract 351 dtype: int64
在景点的英文名en_title和简介abstract中存在缺失值
In [5]:
df1.dtypes
Out[5]:
cn_title object en_title object strategy int64 comment int64 location object ranking int64 lvyou object abstract object dtype: object
景点位置分布
In [6]:
df2 = df1["location"].value_counts().reset_index() df2.columns = ["location","number"] df2
Out[6]:
location | number | |
---|---|---|
0 | 兰州 | 301 |
1 | 永登 | 39 |
2 | 皋兰 | 21 |
3 | 榆中 | 20 |
In [7]:
c = (Pie(init_opts=opts.InitOpts(theme=ThemeType.CHALK)).add("", [list(z) for z in zip(df2["location"].tolist(), df2["number"].tolist())]).set_global_opts(title_opts=opts.TitleOpts(title="兰州景点分布"),legend_opts=opts.LegendOpts(pos_left="80%", orient="vertical")).set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
)c.render_notebook()
攻略数
评论数前10名
c = (Funnel(init_opts=opts.InitOpts(theme=ThemeType.CHALK,width="800px",height="500px")).add("兰州景点评论数漏斗", [list(z) for z in zip(df5["cn_title"].tolist(), df5["comment"].tolist())]).set_series_opts(label_opts=opts.LabelOpts(is_show=True))
)c.render_notebook()
景点词云
前50个词语的展示:
rec_words = [tuple(z) for z in zip(result["词语"].tolist(), result["次数"].tolist())]c = (WordCloud(init_opts=opts.InitOpts(theme=ThemeType.ROMA)).add("", rec_words[:50], word_size_range=[20, 80], shape=SymbolType.DIAMOND).set_global_opts(title_opts=opts.TitleOpts(title="兰州景点词云"))
)c.render_notebook()
整体绘图
# 1-景点位置def piePage() -> Pie:c = (Pie(init_opts=opts.InitOpts(theme=ThemeType.CHALK)).add("", [list(z) for z in zip(df2["location"].tolist(), df2["number"].tolist())]).set_global_opts(title_opts=opts.TitleOpts(title="兰州景点分布"),legend_opts=opts.LegendOpts(pos_left="80%", orient="vertical")).set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}")))return c# 2-攻略数def barPageOne() -> Bar:c = (Bar(init_opts=opts.InitOpts(theme=ThemeType.CHALK)).add_xaxis(df4["cn_title"].tolist()[::-1]).add_yaxis("攻略数", df4["strategy"].tolist()[::-1]).reversal_axis() # 翻转坐标轴.set_series_opts(label_opts=opts.LabelOpts(is_show=True, position="right")) # 是否显示数据以及label的位置(显示在右方).set_global_opts(title_opts=opts.TitleOpts(title="兰州景点攻略数前10")))return c# 3-评论数
def funnlePage() -> Funnel:c = (Funnel(init_opts=opts.InitOpts(theme=ThemeType.MACARONS,width="800px",height="600px")).add("兰州景点评论数漏斗", [list(z) for z in zip(df5["cn_title"].tolist(), df5["comment"].tolist())]).set_series_opts(label_opts=opts.LabelOpts(is_show=True)))return c# 4-驴友占比def barPageTwo() -> Bar:c = (Bar(init_opts=opts.InitOpts(theme=ThemeType.WALDEN)).add_xaxis(df6["cn_title"].tolist()).add_yaxis("", df6["lvyou_number"].tolist())
# .reversal_axis() # 翻转坐标轴.set_series_opts(label_opts=opts.LabelOpts(is_show=True)) # 是否显示数据以及label的位置(显示在右方).set_global_opts(title_opts=opts.TitleOpts(title="兰州景点驴友占比"),xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-30)), # 设置旋转角度))return c# 5-词云图
def worldPage() -> WordCloud:rec_words = [tuple(z) for z in zip(result["词语"].tolist(), result["次数"].tolist())]c = (WordCloud(init_opts=opts.InitOpts(theme=ThemeType.ROMA)).add("", rec_words[:50], word_size_range=[20, 80], shape=SymbolType.DIAMOND).set_global_opts(title_opts=opts.TitleOpts(title="兰州景点词云")))return cpage = (Page(layout=Page.DraggablePageLayout).add(piePage(),barPageOne(),funnlePage(),barPageTwo(),worldPage()
))page.render("lanzhou.html")
需要完整项目代码关注下方 公众号:Python源码