Fork me on GitHub

Python爬虫游全国-兰州

Python爬虫游全国:兰州站

本文是Python爬虫旅游全国的第7个城市:兰州,主要是介绍兰州的景点和美食信息。

之前写过厦门、长沙、西安、大连、苏州、成都等,感兴趣的可以阅读,也可以当做一份旅游指南~

兰州

摘录一段来自维基百科中关于兰州的简介:

兰州市,简称,别称金城,是中华人民共和国甘肃省省会,国务院批复确定的中国西北地区重要的工业基地和综合交通枢纽,丝绸之路经济带的重要节点城市,西部地区重要的中心城市之一,西北地区第三大城市,“兰州—西宁城市群”中的核心城市,位于甘肃省中部。

下面是兰州的行政区域:

爬取信息

爬取的信息主要是兰州的美食和景点信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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

景点爬取

下面单页爬取的代码,采用的是正则爬取的方式:

1
2
3
4
5
6
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
2
3
4
5
6
# 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]:

1
2
3
4
5
6
7
8
9
10
['甘肃省博物馆',
'黄河铁桥',
'白塔山公园',
'黄河母亲雕塑',
'兰州新区长城影视基地',
'石佛沟国家森林公园',
'黄河索道',
'五泉山公园',
'兰州极地海洋世界',
'兰山公园']

In [4]:

1
2
3
4
5
6
# 2-景点英文名称

en_title = re.findall('<span class="en_tit">(.*?)</span>.*?</span>',result,re.S)
print("长度",len(en_title))
en_title
长度 10

Out[4]:

1
2
3
4
5
6
7
8
9
10
['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]:

1
2
3
4
5
# 3-strategy攻略数量
strategy = re.findall('class="icon_strategy" title="攻略"></span>(.*?)</div>',result, re.S)
print(len(strategy))
strategy
10

Out[5]:

1
['50', '94', '40', '35', '0', '1', '3', '2', '0', '1']

In [6]:

1
2
3
4
5
6
# 4-comment点评数量

comment = re.findall('class="icon_comment" title="点评"></span>(.*?)</div>',result, re.S)
print(len(comment))
comment
10

Out[6]:

1
['1295', '2948', '793', '489', '23', '30', '46', '354', '146', '80']

In [7]:

1
2
3
4
5
6
7
# 5-景点地点(长沙景点排名、宁乡景点排名等)
# 我们只需要"景点排名"之前的信息,代表的是地址

location = re.findall('去过(.*?)的驴友', result, re.S)
print(len(location))
location
10

Out[7]:

1
['兰州', '兰州', '兰州', '兰州', '永登', '兰州', '兰州', '兰州', '兰州', '兰州']

In [8]:

1
2
3
4
5
6
# 6-景点排名

ranking = re.findall('class="ranking_sum".*?class="sum">(.*?)</span>',result,re.S)
print(len(ranking))
ranking
10

Out[8]:

1
['7', '3', '11', '10', '0%', '56', '1', '17', '5', '49']

In [9]:

1
2
3
4
5
6
7
# 7-驴友
# 有多少的驴友也去过这个地方

lvyou = re.findall('class="comment_sum">.*?class="sum">(.*?)</span>',result,re.S)
print(len(lvyou))
lvyou
10

Out[9]:

1
['35%', '58%', '30%', '30%', '0%', '0%', '0%', '1%', '0%', '1%']

In [10]:

1
2
3
4
5
# 8-景点简介
abstract = re.findall('class="desbox">(.*?)</div>',result,re.S)
print(len(abstract))
abstract
10

Out[10]:

1
2
3
4
5
6
7
8
9
10
['通过丰富精彩的展品了解古丝绸之路、唐蕃古道上多民族的文化和历史。',
'兰州市最为经典的地标建筑,夜晚时铁桥彩灯闪耀,周围夜景非常漂亮。',
'登上山顶可以俯瞰壮观的兰州城市全景,山间建筑古朴树木众多,让人感受舒心惬意。',
'黄河母亲雕塑现已经成为兰州的标志性雕塑,也代表着兰州形象。',
'', # 空值
'',
'',
'公园景点以五眼名泉和佛教古建筑为主,园内丘壑起伏,林木葱郁,环境清幽。',
'',
'公园就位于山顶制高点上,可俯瞰兰州全景。']

最终爬取的结果:

美食爬取

单页信息爬取的代码,同样是基于正则表达式的爬取:总共是200页

In [2]:

1
2
3
4
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]:

1
cn_title = re.findall('cn_tit">(.*?)</span>.*?countbox',result,re.S)

In [4]:

1
2
3
print(len(cn_title))
cn_title
10

Out[4]:

1
2
3
4
5
6
7
8
9
10
['正宁路小吃夜市',
'安泊尔牛肉面(北滨河路店)',
'兰州大众巷美食街',
'马三洋芋片(兰州总店)',
'清真·马安军辣子牛肉面',
'安泊尔',
'杜记甜食',
'孙子烤肉(皋兰路店)',
'大漠烤肉(盘旋路店)',
'清真•白建强牛肉面']

score

In [5]:

1
score = re.findall('cur_score">(.*?)</span>.*?total_score',result,re.S)

In [6]:

1
2
3
print(len(score))
score
10

Out[6]:

1
['4.3', '4.5', '--', '4.3', '3.5', '5.0', '4.2', '3.5', '4.2', '0.0']

sublistbox

先提取整个sublistbox,然后对里面的每个子元素单独提取

In [7]:

1
2
3
sublistbox = re.findall('sublistbox">(.*?)</div>', result, re.S)

sublistbox[:1]

Out[7]:

1
['<dl class="sublist_item clrfix"><dt class="sub_tit">人\u3000均</dt><dd class="sub_des">&yen; 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]:

1
type(sublistbox)

Out[8]:

1
list

In [9]:

1
# 对sublistbox单独提取
均价

In [10]:

1
2
3
4
5
6
7
8
9
10
11
person_avg = []

for i in range(len(sublistbox)):
try:
if "均" in sublistbox[i]:
person_avg.append(re.findall('&yen; (.*?)</dd></dl>',sublistbox[i],re.S)[0])
else:
person_avg.append(0)
continue
except:
person_avg.append(0)

In [11]:

1
2
3
print(len(person_avg))
person_avg
10

Out[11]:

1
['63', '27', 0, '19', '17', 0, '14', '58', '58', '18']
地址

In [12]:

1
2
3
4
5
6
7
8
9
10
11
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("无")
continue
except:
address.append("无")

In [13]:

1
2
3
print(len(address))
address
10

Out[13]:

1
2
3
4
5
6
7
8
9
10
['白银路街道永昌南路正宁路',
'北滨河路754号(龙源斜对面)',
'兰州市城关区大众巷',
'通渭路79号',
'七里河北街忠云宾馆斜对面',
'北滨河路金城关3号',
'大众巷72号',
'皋兰路4号(虹云宾馆北侧)',
'东岗西路451号',
'雁滩路3423号']
推荐菜

In [14]:

1
2
3
4
5
6
7
8
9
10
11
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("无")
continue
except:
recommand.append("无")

In [15]:

1
2
3
print(len(recommand))
recommand
10

Out[15]:

1
2
3
4
5
6
7
8
9
10
['当地口味\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]:

1
2
3
4
5
6
7
8
9
10
11
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("无")
continue
except:
comment.append("无")

In [17]:

1
2
3
print(len(comment))
comment
10

Out[17]:

1
2
3
4
5
6
7
8
9
10
['吃客云集的小吃街,地道吃食令人回味。',
'专注正宗牛肉面,开放式厨房热气腾腾',
'大众巷是兰州最古老的美食街,拥有许多老字号当地特色美食,是游客体验兰州美食文化的绝佳去处。',
'兰州代表性的小吃店,明档操作干净放心',
'颇具名气的牛肉面馆,当地人的家庭食堂',
'个人觉得安泊尔是我吃过最好吃的牛肉面了,不过这家店的位置很尴尬,只有这趟路线才能涉及到',
'老牌人气甜食店,荣获多个美食奖项',
'店里面的装修环境是非常不错的,有人说价格有点贵,看了一下菜单,相对于夜市上的来说确实有一点贵,...',
'人气爆棚的口碑餐厅,各式烤串喷香诱人',
'当地食堂级别的老店,牛肉面汤清味醇面韧。']

最终爬取结果:刚好是2000条数据

下面是对上面爬取到的两份数据进行分析:

美食数据分析

导入数据:

字段基本信息:

In [3]:

1
2
3
# 1-数据缺失值

df.isnull().sum()

Out[3]:

1
2
3
4
5
6
7
中文名    0
得分 0
均价 0
地址 0
推荐菜 0
评价 0
dtype: int64

In [4]:

1
2
3
# 2、字段类型

df.dtypes

Out[4]:

1
2
3
4
5
6
7
中文名    object
得分 object
均价 int64
地址 object
推荐菜 object
评价 object
dtype: object

分析哪些店的得分靠前:

In [5]:

1
2
3
# 得分中有未评分的数据:--

df["得分"].value_counts()

Out[5]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
--     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]:

1
2
3
# 将未评分的数据统一替换成0.0,也就是0分

df["得分"] = df["得分"].apply(lambda x: x.replace("--","0.0"))

得分统计

大部分店铺的得分在3.5分,好像并不是很高~

得分前10的店铺

店铺均价占比

高档餐厅

面馆

到了兰州肯定得吃面:据数据统计有284家面馆

火锅店

统计出来有129家火锅店:

1
px.bar(huoguo[:15],x="中文名",y="得分")

美食统计信息

推荐菜词云

绘制当地推荐菜的词云图:

1
2
3
4
5
6
7
8
9
10
11
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]:

1
df1.shape

Out[3]:

1
(381, 8)

In [4]:

1
df1.isnull().sum()

Out[4]:

1
2
3
4
5
6
7
8
9
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]:

1
df1.dtypes

Out[5]:

1
2
3
4
5
6
7
8
9
cn_title    object
en_title object
strategy int64
comment int64
location object
ranking int64
lvyou object
abstract object
dtype: object

景点位置分布

In [6]:

1
2
3
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]:

1
2
3
4
5
6
7
8
9
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名

1
2
3
4
5
6
7
8
9
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个词语的展示:

1
2
3
4
5
6
7
8
9
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 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 c

page = (
Page(layout=Page.DraggablePageLayout)
.add(
piePage(),
barPageOne(),
funnlePage(),
barPageTwo(),
worldPage()
))

page.render("lanzhou.html")

本文标题:Python爬虫游全国-兰州

发布时间:2022年07月05日 - 21:07

原始链接:http://www.renpeter.cn/2022/07/05/Python%E7%88%AC%E8%99%AB%E6%B8%B8%E5%85%A8%E5%9B%BD-%E5%85%B0%E5%B7%9E.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea