Python crawler viaja por todo el país - Estación de Lanzhou

Número público: You Er Hut
Autor: Peter
Editor: Peter

Hola a todos, mi nombre es Peter~

Este artículo es la séptima ciudad del turismo sobre orugas Python en el país: Lanzhou, principalmente para presentar las atracciones e información gastronómica de Lanzhou.

Lo escribí antes 厦门、长沙、西安、大连、苏州、成都, si está interesado, puede leerlo o puede usarse como una guía de viaje ~

Lanzzhou

Un extracto de una introducción a Lanzhou en Wikipedia:

Lanzhou , conocida como Lan , también conocida como Jincheng , es la capital de la provincia de Gansu de la República Popular China , una importante base industrial y centro de transporte integral en el noroeste de China aprobado por el Consejo de Estado, una importante ciudad nodo de la Ruta de la Seda Cinturón económico, y una de las ciudades centrales importantes en la región occidental. Primero, la tercera ciudad más grande en la región noroeste, la ciudad central en la "aglomeración urbana Lanzhou- Xining ", está ubicada en la parte central de la provincia de Gansu.

Las siguientes son las regiones administrativas de Lanzhou:

Información de rastreo

La información rastreada es principalmente la información de comida y atracciones de Lanzhou:

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

Rastreo de atracciones

El siguiente código de rastreo de una sola página utiliza un método de rastreo regular:

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()

Si la fuente HTML analizada contiene comillas dobles, la cadena más externa después del método re.findall() usa comillas simples

En 3]:

# 1-景点中文名称

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

Fuera[3]:

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

En [4]:

# 2-景点英文名称

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

Fuera[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']

En [5]:

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

Fuera[5]:

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

En [6]:

# 4-comment点评数量

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

Fuera[6]:

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

En [7]:

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

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

Fuera[7]:

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

En [8]:

# 6-景点排名

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

Fuera[8]:

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

En [9]:

# 7-驴友
# 有多少的驴友也去过这个地方

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

Fuera[9]:

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

En [10]:

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

Fuera[10]:

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

El resultado final del rastreo:

Rastreo de comida

El código para el rastreo de información de una sola página también se rastrea en función de expresiones regulares: un total de 200 páginas

En 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

En 3]:

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

En [4]:

print(len(cn_title))
cn_title
10

Fuera[4]:

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

puntaje

En [5]:

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

En [6]:

print(len(score))
score
10

Fuera[6]:

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

cuadro de sublista

Primero extraiga todo el cuadro de lista secundaria y luego extraiga cada subelemento dentro de él por separado

En [7]:

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

sublistbox[:1]

Fuera[7]:

['<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>']

En [8]:

type(sublistbox)

Fuera[8]:

list

En [9]:

# 对sublistbox单独提取
Precio promedio

En [10]:

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)

En [11]:

print(len(person_avg))
person_avg
10

Fuera[11]:

['63', '27', 0, '19', '17', 0, '14', '58', '58', '18']
Dirección

En [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("无")
        continue
    except:
        address.append("无")

En [13]:

print(len(address))
address
10

Fuera[13]:

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

En [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("无")
        continue
    except:
        recommand.append("无")

En [15]:

print(len(recommand))
recommand
10

Fuera[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牛肉']
Evaluación

En [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("无")
        continue
    except:
        comment.append("无")

En [17]:

print(len(comment))
comment
10

Fuera[17]:

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

Resultado final del rastreo: exactamente 2000 piezas de datos

El siguiente es un análisis de los dos datos rastreados anteriormente:

Análisis de datos de alimentos

Datos de importacion:

Información básica del campo:

En 3]:

# 1-数据缺失值

df.isnull().sum()

Fuera[3]:

中文名    0
得分     0
均价     0
地址     0
推荐菜    0
评价     0
dtype: int64

En [4]:

# 2、字段类型

df.dtypes  

Fuera[4]:

中文名    object
得分     object
均价      int64
地址     object
推荐菜    object
评价     object
dtype: object

Analiza qué tiendas tienen las puntuaciones más altas:

En [5]:

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

df["得分"].value_counts()

Fuera[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

En [6]:

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

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

Estadísticas de puntuación

La mayoría de las tiendas tienen una puntuación de 3,5 puntos, que no parece ser muy alta~

Las 10 mejores tiendas

Precio medio de tienda

restaurante de alta gama

Casa de fideos

Cuando llegas a Lanzhou, debes comer fideos: según las estadísticas, hay 284 restaurantes de fideos.

restaurante de olla caliente

Según las estadísticas, hay 129 restaurantes de ollas calientes:

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

Estadísticas de alimentos

Nube de word de platos recomendados

Dibuje un diagrama de nube de palabras de platos locales recomendados:

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()

Análisis de datos de atracción

o importar los datos primero

exploración de datos

En 3]:

df1.shape

Fuera[3]:

(381, 8)

En [4]:

df1.isnull().sum()

Fuera[4]:

cn_title      0
en_title    113
strategy      0
comment       0
location      0
ranking       0
lvyou         0
abstract    351
dtype: int64

Faltan valores en el nombre en inglés de la atracción en_title y en el resumen de la introducción

En [5]:

df1.dtypes

Fuera[5]:

cn_title    object
en_title    object
strategy     int64
comment      int64
location    object
ranking      int64
lvyou       object
abstract    object
dtype: object

Ubicación de las atracciones

En [6]:

df2 = df1["location"].value_counts().reset_index()
df2.columns = ["location","number"]
df2

Fuera[6]:

ubicación número
0 Lanzzhou 301
1 yong deng 39
2 Gaolan veintiún
3 yuzhong 20

En [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()

Número de asaltantes

10 comentarios principales

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()

Nube de word de atracciones

Visualización de las primeras 50 palabras:

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()

dibujo general

# 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")  

おすすめ

転載: juejin.im/post/7116558425533710343
おすすめ