1. 选取主题

meituan是我常用的网页,按照个人喜好确定了爬取meituan大盘鸡的数据并进行统计分析

python 登录美团 python爬美团数据_Python

2. 分析网页

翻页可以看到,上方的URL并没有发生改变,所以考虑是通过json翻页。
右键检查,network->刷新->XHR,找到uuid的文件,获取URL,进行分析,一步步删除多余部分,最后对比第一页与第二页的URL,发现仅offset发生变化,观察网页可知,美团页面一页有32个所需元素,第一页的offset为0,第二页的offset=32,也就是每翻页一次,offset要增加32
GetHTMLText函数用于爬取网页源代码,头部从上图uuid文件中找到Request部 分,复制Accept、User-Agent、Cookie部分。翻页在主函数实现
在爬取网页时需要先登录,搜索大盘鸡,然后得到网页的cookie

代码部分

import requests
import re
from bs4 import BeautifulSoup
import json
from urllib.request import urlopen,quote
import pandas as pd
import numpy as np
from  pylab import *

def getHTMLText(url):
    headers={"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36",
            "Cookie":"__mta=20871943.1625705574088.1625810520459.1625813097008.16; _lxsdk_cuid=17a839a3528c8-070f1da58fbd21-6373264-144000-17a839a3529c8; ci=1; rvct=1; mtcdn=K; uuid=5226c694e07949bdb828.1625725143.1.0.0; mt_c_token=xR1aknv_B8Yiio_ZWzUmVSFo2koAAAAACw4AAIkyH5SSt867G_XkxBIcCNINlemXinZVHTQDkWGAFQukS3HrBbqc_BktISzd7MhPvw; lsu=; iuuid=C72B4FEF3403F5A0924DEFB4DF9C9FFEC17E5FC35B2B4D6CF55632ABF4817904; isid=xR1aknv_B8Yiio_ZWzUmVSFo2koAAAAACw4AAIkyH5SSt867G_XkxBIcCNINlemXinZVHTQDkWGAFQukS3HrBbqc_BktISzd7MhPvw; logintype=normal; cityname=%E5%8C%97%E4%BA%AC; _lxsdk=C72B4FEF3403F5A0924DEFB4DF9C9FFEC17E5FC35B2B4D6CF55632ABF4817904; webp=1; i_extend=H__a100002__b1; latlng=39.830516,116.290895,1625725257685; __utma=74597006.798321478.1625725258.1625725258.1625725258.1; __utmz=74597006.1625725258.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; lt=fYaN6SmjIjuKuNSbDGgqCDbdjQgAAAAACw4AABKyZyfS3ZbSub8odXTK9CKxaHAOvtIkpz464sfHyJcIfPPYbpw2Vh_Rnd3Mr5B24A; u=583093909; n=%E9%A5%95%E9%A4%AEzzzzz; token2=fYaN6SmjIjuKuNSbDGgqCDbdjQgAAAAACw4AABKyZyfS3ZbSub8odXTK9CKxaHAOvtIkpz464sfHyJcIfPPYbpw2Vh_Rnd3Mr5B24A; unc=%E9%A5%95%E9%A4%AEzzzzz; __mta=20871943.1625705574088.1625810319319.1625810340857.13; firstTime=1625813095796; _lxsdk_s=17a89d8b4fc-08e-6c7-641%7C%7C48"}
    try:
        r = requests.get(url,headers=headers,timeout=100)
        r.raise_for_status()
        r.encoding = r.apparent_encoding#对文本中使用的编码替换整体的编码
        return r.text
    except:
        return "失败"

#将数据写入csv文件中        
def file_data(title,address,avgprice,avgscore,comment):
    data={
        '店铺名称':title,
        '店铺地址':address,
        '平均消费价格':avgprice,
        '店铺评分':avgscore,
        '评价人数':comment
    }
    with open('C:/Users/dell/Desktop/美团大盘鸡店铺数据4.csv','a',encoding='utf-8-sig') as fb:
        fb.write(json.dumps(data,ensure_ascii=False)+'\n')


        
def main():
    goods = '大盘鸡'#检索词
    depth = 49#设置爬取的深度,一共50页
    start_url = 'https://bj.meituan.com/s/' + goods
    
   #以下采用for循环对每个页面URL进行访问
    i=0
    for i in range(depth):
        try:
            url =start_url + '&offset=' + str(32*i)
            #url = start_url +  str(32*i)
            html = getHTMLText(url)
            titles = re.findall('","title":"(.*?)","address":"',html)
            addresses = re.findall(',"address":"(.*?)",',html)
            avgprices = re.findall(',"avgprice":(.*?),', html)
            avgscores = re.findall(',"avgscore":(.*?),',html)
            comments = re.findall(',"comments":(.*?),',html)
            
            print(titles)
            print(addresses)
            print(avgprices)
            print(avgscores)
            print(comments)
            #输出当前返回数据的长度
            print(len(titles), len(addresses), len(avgprices), len(avgscores), len(comments))
            #将每个店铺的信息通过循环写入文件
            for j in range(len(titles)):
                title=titles[j]
                address=addresses[j]
                avgprice=avgprices[j]
                avgscore=avgscores[j]
                comment=comments[j]
                #file_data(title,address,avgprice,avgscore,comment)
            
        except:
            continue
    #printGoodsList(infoList)

    
main()

其中的headers和cookie、accept后面的参数要替换成自己的,如果要存为txt文件,则把.csv改为.txt即可;一共爬取50页,因为第一页的offset为0,所以i最开始为0,之后每一次offset是32*i,实现网页的翻页功能;main()函数部分,因为json方法无法针对店铺特有的title键值进行获取所以使用正则表达式获取当前相应内容中的数据;主函数通过循环将每次得到的店铺信息传入子函数;子函数也就是写入csv文件的子函数,将主函数传过来的数据写入csv文件

数据展示:

python 登录美团 python爬美团数据_python 登录美团_02


在获得的原始数据中,由于美团网页的机制会存在大量的重复数据,同时也为了数据更直观更方便使用,我们还需要对数据进行清洗。这一次我主要对数据进行了删除重复记录和数据类型转换这两项,将评分和人数以及平均消费价格这三列数据从object类型转换为float以用于后续的运算。

#删除重复记录
import pandas as pd
df=pd.read_csv('C:/Users/dell/Desktop/Python/END/美团大盘鸡店铺数据.csv',encoding='GB18030')
df.head()
df.drop_duplicates(['店铺名称','店铺地址'],inplace=True)
print("ok")

#数据类型转换
df['平均消费价格(元)']=df['平均消费价格(元)'].astype('float')
df['评分']=df['评分'].astype('float')
df['评价人数(人)']=df['评价人数(人)'].astype('float')
print(df.dtypes)

之后我们可以将处理好的数据存入数据库中,方便使用,这里我存的是mysql数据库

load data infile 'D:\MySQL\mysql-8.0.23-winx64\mysql-8.0.23-winx64\data\meituan.csv'
into table mt_shop character set utf8
fields terminated by ',' optionally enclosed by '"' escaped by '"'
lines terminated by '\r\n';

在导入数据库之前,需要先自行创建新的数据库和表格,我这里的表格名称是mt_shop,用的是cmd窗口导入数据库

导入数据库,要注意字符集的问题,我试了gb2312,gb18030,utf8,最后utf8能够正确展示数据;还有一个是\和/以及\的问题,路径最好不要有中文名,免得出现数据存入不成功的问题

python 登录美团 python爬美团数据_python 登录美团_03


3.Python数据分析及展示

词云:

用到jieba库和worldcloud库

import jieba
import wordcloud
fo=open("C:/Users/dell/Desktop/Python/END/词云文本.txt",encoding='UTF-8-sig')
txt=fo.read()
fo.close()
ls=jieba.lcut(txt)
txt=" ".join(ls)
from imageio import imread
mk=imread("C:/Users/dell/Desktop/Python/END/chicken.png")
w=wordcloud.WordCloud(width=1000,\
                     font_path="msyh.ttc",height=700,mask=mk,background_color="white")
w.generate(" ".join(jieba.lcut(txt)))
w.to_file("C:/Users/dell/Desktop/Python/END/pywcloud.png")

python 登录美团 python爬美团数据_html_04


条形图:

用数据聚合和分组运算统计评分前十、平均消费前十、评价人数前十的店铺条形图,先用groupby选出店铺名称和评分这两列数据,然后排序,用numpy库画图

#数据聚合:取出店铺名称和评分这两列数据
a=df[['评分']].groupby(df['店铺名称']).sum()
a.index

#按照评分从高到低排序
a=a.sort_values(axis=0,ascending=False,by='评分')
a

#生成评分最高前十名的条形图
import numpy as np
plt.rcParams['font.sans-serif']=['SimHei'] 
sns.barplot(x=a.head(10).index,y=a['评分'].head(10),color='skyblue')
i=0
for x,y in enumerate(a.评分):
    if i<10:
        plt.text(x,y+0.1,"%s"%round(y,1),ha='center')
        i+=1
plt.title("评分前十店铺")
plt.ylim(0,10)
plt.xticks(rotation=300)
plt.tight_layout()  # 当有多个子图时,可以使用该语句保证各子图标题不会重叠
plt.savefig('C:/Users/dell/Desktop/Python/END/评分前十.jpg', dpi=700)  # dpi 表示以高分辨率保存一个图片文件,pdf为文件格式,输出位图文件

python 登录美团 python爬美团数据_Python_05


python 登录美团 python爬美团数据_python 登录美团_06


python 登录美团 python爬美团数据_数据_07


条形图制作过程中,循环条件使用enumerate枚举法对元素进行遍历

在做评价人数前十店铺时,遇到如下问题:
TypeError: can only concatenate tuple (not "float") to tuple
出错行数:13
最开始评价人数那一列命名为:评价人数(人),含有中文括号,在使用循环遍历元素时,无法识别或者说找到对应的那一列数据导致报错
解决方法:更改列名,删除中文括号或将中文括号替换为英文括号

折线图:
没有对应库的先在cmd窗口安装对应的库,这里我在csv文件中获取了各个区店铺的数量用于画折线图

import matplotlib.pyplot as plt
from pylab import *                                 #支持中文
mpl.rcParams['font.sans-serif'] = ['SimHei']

names = ['昌平区','朝阳区','东城区','房山区','丰台区','海淀区','密云区','平谷区','石景山区','顺义区','通州区','西城区']
x = range(len(names))
y = [6,49,13,9,9,100,4,1,2,4,9,3]
y1=[0,10,20,30,40,50,60,70,80,90,100,110]
plt.ylim(0, 110)  # 限定纵轴的范围
plt.plot(x, y, marker='o', mec='r', mfc='w',label=u'各城区大盘鸡店铺数量分布')
plt.legend()  # 让图例生效
plt.xticks(x, names, rotation=45)
plt.margins(0)
plt.subplots_adjust(bottom=0.15)
plt.xlabel(u"time(s)邻居") #X轴标签
plt.ylabel("RMSE") #Y轴标签
plt.title("各城区大盘鸡店铺数量分布图") #标题
plt.savefig('C:/Users/dell/Desktop/Python/END/各城区大盘鸡店铺密度折线图.jpg', dpi=700)
plt.show()

python 登录美团 python爬美团数据_html_08


通过折线图可以看出店铺主要集中在海淀区、朝阳区这两个区,分布很集中

饼状图:
选取店铺数量最多的四个城区用matplotlib库绘制饼状图,海淀区占比最大

import matplotlib.pyplot as plt

labels = ['东城区','朝阳区','海淀区','房山区']

x = [13,49,100,9]
colors='lightgreen','gold','lightskyblue','lightcoral'
#显示百分比
#饼图分离
explode = (0,0.1,0,0)

#设置阴影效果
#startangle,为起始角度,0表示从0开始逆时针旋转,为第一块。
plt.pie(x,labels=labels,autopct='%3.2f%%',explode=explode,shadow=True,startangle=90,colors=colors)

#设置x,y的刻度一样,使其饼图为正圆
plt.axis('equal')

plt.show()
plt.savefig('C:/Users/dell/Desktop/Python/END/各城区大盘鸡店铺密度.jpg', dpi=700)

python 登录美团 python爬美团数据_数据_09


地图-热力图:

采用0.5版本的pyecharts库进行画图,安装:以管理员身份运行cmd窗口,输入安装命令:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyecharts==0.5.10

由于使用到了地图,所以依然通过cmd命令来安装对应的地图,因为本次使用到的是北京市的地图,所以安装中国市级地图:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple echarts-china-cities-pypkg

在绘制地图过程中,由于echarts库的1.0前的版本和1.0后的版本存在较大的差异,在引用库上的格式存在着不同,最开始安装了1.9版本的库导致没能成功绘制,后删除1.9版本的echarts库,安装0.5版本的echarts的库和地图则可成功绘制。

#0.5版本
from pyecharts import Map
map2 = Map("北京地图", '北京', width=1200, height=600)
#十二个城区
city = ['昌平区', '朝阳区', '东城区','房山区','丰台区','海淀区','密云区','平谷区','石景山区','顺义区','通州区','西城区']
values2 = [6,49,13,9,9,100,4,1,2,4,9,3]
map2.add('北京', city, values2, visual_range=[1, 100], maptype='北京', is_visualmap=True, visual_text_color='#000')
map2.render(path="北京地图.html")
print("ok")

python 登录美团 python爬美团数据_html_10


还有一个用IDEA做的图,也就是后缀为.py的文件

import requests
import re
from bs4 import BeautifulSoup
import json
from urllib.request import urlopen,quote
import pandas as pd
import numpy as np
from  pylab import *

def getHTMLText(url):
    headers={"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36",
            "Cookie":"__mta=20871943.1625705574088.1625810520459.1625813097008.16; _lxsdk_cuid=17a839a3528c8-070f1da58fbd21-6373264-144000-17a839a3529c8; ci=1; rvct=1; mtcdn=K; uuid=5226c694e07949bdb828.1625725143.1.0.0; mt_c_token=xR1aknv_B8Yiio_ZWzUmVSFo2koAAAAACw4AAIkyH5SSt867G_XkxBIcCNINlemXinZVHTQDkWGAFQukS3HrBbqc_BktISzd7MhPvw; lsu=; iuuid=C72B4FEF3403F5A0924DEFB4DF9C9FFEC17E5FC35B2B4D6CF55632ABF4817904; isid=xR1aknv_B8Yiio_ZWzUmVSFo2koAAAAACw4AAIkyH5SSt867G_XkxBIcCNINlemXinZVHTQDkWGAFQukS3HrBbqc_BktISzd7MhPvw; logintype=normal; cityname=%E5%8C%97%E4%BA%AC; _lxsdk=C72B4FEF3403F5A0924DEFB4DF9C9FFEC17E5FC35B2B4D6CF55632ABF4817904; webp=1; i_extend=H__a100002__b1; latlng=39.830516,116.290895,1625725257685; __utma=74597006.798321478.1625725258.1625725258.1625725258.1; __utmz=74597006.1625725258.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; lt=fYaN6SmjIjuKuNSbDGgqCDbdjQgAAAAACw4AABKyZyfS3ZbSub8odXTK9CKxaHAOvtIkpz464sfHyJcIfPPYbpw2Vh_Rnd3Mr5B24A; u=583093909; n=%E9%A5%95%E9%A4%AEzzzzz; token2=fYaN6SmjIjuKuNSbDGgqCDbdjQgAAAAACw4AABKyZyfS3ZbSub8odXTK9CKxaHAOvtIkpz464sfHyJcIfPPYbpw2Vh_Rnd3Mr5B24A; unc=%E9%A5%95%E9%A4%AEzzzzz; __mta=20871943.1625705574088.1625810319319.1625810340857.13; firstTime=1625813095796; _lxsdk_s=17a89d8b4fc-08e-6c7-641%7C%7C48"}
    try:
        r = requests.get(url,headers=headers,timeout=100)
        r.raise_for_status()
        r.encoding = r.apparent_encoding#对文本中使用的编码替换整体的编码
        return r.text
    except:
        return "失败"

#将数据写入csv文件中        
def file_data(title,address,avgprice,avgscore,comment):
    data={
        '店铺名称':title,
        '店铺地址':address,
        '平均消费价格':avgprice,
        '店铺评分':avgscore,
        '评价人数':comment
    }
    with open('C:/Users/dell/Desktop/美团大盘鸡店铺数据4.csv','a',encoding='utf-8-sig') as fb:
        fb.write(json.dumps(data,ensure_ascii=False)+'\n')

#经纬度转换
def addrlnglat(addr):
    try:
        url='http://api.map.baidu.com/geocoding/v3/'
        addr=quote(addr)#quote编码
        output='json'
        ak='zW9us6KSVv4HKwcvcAnTt3DYOzIKtu0L'#API密钥
        uri=url+'?address='+address+'&output='+output+'&ak='+ak
        req=urlopen(uri)
        res=req.read().decode()
        temp=json.loads(res)
        lng=temp['result']['location']['lng']
        lat=temp['result']['location']['lat']
        return lng,lat
    except:
        return 0,0
#将经纬度存为csv文件
def save(lng,lat):
    data={
        "经度":lng,
        "纬度":lat
    }
    fpath='C:/Users/dell/Desktop/Python/END/经纬度.csv'
    with open(fpath,'a',encoding='utf-8') as f:
        f.write(json.dumps(data,ensure_ascii=False)+'\n')
        
def main():
    goods = '大盘鸡'#检索词
    depth = 49#设置爬取的深度,一共50页
    start_url = 'https://bj.meituan.com/s/' + goods
    
   #以下采用for循环对每个页面URL进行访问
    i=0
    for i in range(depth):
        try:
            url =start_url + '&offset=' + str(32*i)
            #url = start_url +  str(32*i)
            html = getHTMLText(url)
            titles = re.findall('","title":"(.*?)","address":"',html)
            addresses = re.findall(',"address":"(.*?)",',html)
            avgprices = re.findall(',"avgprice":(.*?),', html)
            avgscores = re.findall(',"avgscore":(.*?),',html)
            comments = re.findall(',"comments":(.*?),',html)
            
            print(titles)
            print(addresses)
            print(avgprices)
            print(avgscores)
            print(comments)
            #输出当前返回数据的长度
            print(len(titles), len(addresses), len(avgprices), len(avgscores), len(comments))
            #将每个店铺的信息通过循环写入文件
            for j in range(len(titles)):
                title=titles[j]
                address=addresses[j]
                avgprice=avgprices[j]
                avgscore=avgscores[j]
                comment=comments[j]
                #file_data(title,address,avgprice,avgscore,comment)
            
        except:
            continue
    #printGoodsList(infoList)

    
main()

在最开始的代码中我同时将得到的地址通过百度地图API转化为具体的经纬度并将经纬度存成csv文件

python 登录美团 python爬美团数据_Python_11


.py文件中画图代码

python 登录美团 python爬美团数据_html_12


从百度API转换成地图上的点:

在百度地图API页面找到地图DEMO

选择所需的示例;

Pycharm创建一个HTML文件,将csv文件中的点替换图中的坐标点并添加自己的AK,创建点标记,并在地图上添加点标记,

python 登录美团 python爬美团数据_数据_13


python 登录美团 python爬美团数据_Python_14


python 登录美团 python爬美团数据_python 登录美团_15


python 登录美团 python爬美团数据_python 登录美团_16