Python采集爬取cnmo网站手机数据案例实现

前言

本例子利用Python爬虫爬取cnmo网站2020年至2022年11月的手机数据,并对其进行数据处理和可视化分析。

1、效果展示

如下如最终我们将得到一个包含手机型号、颜色、内存、价格及购买链接等的excel表以及dataframe和可视化效果。

2、需要用到的库

import pandas as pd
import numpy as np

import requests
from bs4 import BeautifulSoup
from lxml import etree

import matplotlib.pyplot as plt

第一个库是数据分析处理的库

第二个是数组计算

第三个是爬虫请求库

  • 五个是网页解析

第六个是图形可视化库

3、原理分析

这个大部分原理都很简单。

  1. 观察列表页的链接地址的规律,并运用dataframe存储每一款手机的参数链接以及手机型号等。

2、获取dataframe中的每个手机的参数链接

3、获取每一款手机的所有颜色和内存等所有组合的数据,并存为同一个dataframe,这里运行非常久,毕竟先读取页面获取每个手机的参数链接,在读取每一个参数链接爬取里面的参数,存为dataframe,一来一回很耗内存,所以后面最好把dataframe存为csv等格式,方便处理数据。

4、将获得的dataframe存为csv方便后期处理数据和可视化

5、最后就是读取csv的数据了,用了pandas中的一些方法,读出来的也是dataframe格式,通过pandas方法,进行转换格式,统计计算,最后通过matloplit可视化。

4、代码实现

先获取参数页和标题也就是下图网页中的内容

通过网页定位可以找到手机型号和参数链接的网页代码,方便我们后面用beautifulsoup和etree进行解析定位爬取手机型号和发布时间、参数链接,存为dataframe

具体代码如下:

url='https://product.cnmo.com/all/product_t1_p{}.html'
def lianjie():
    colum_name = ['标题', '时间', '链接']
    data_list = []
    for i in range(1,20):#翻页,20页
        url_lj=url.format(i)
        header = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'}
        res=requests.get(url_lj,header).text
        soup=BeautifulSoup(res,'lxml')
        for b in soup.find('ul',class_="all-con-con-ul cf").find_all('li'):
            temp = []
            title=b.find('a',class_="name").text
            time=b.find('p',class_="red").find('span').text.replace(' ','').replace('\n','')
            url_canshu='https:'+b.find('div',class_="info").select('a')[1].get('href')
            temp.append(title)
            temp.append(time)
            temp.append(url_canshu)
            data_list.append(temp)

    data = pd.DataFrame(data_list, columns=colum_name)
    return data

如上图获得手机型号和发布时间等数据,存为dataframe,但是我们还要接着存其他的参数信息,所以定义一个函数先创建要存储的dataframe的列名称。具体代码如下:

def chuli(data):
    data1=data.dropna(thresh=2)
    data1.drop_duplicates(subset=['标题'])
    data1['nian']=(data1['时间'].str.split('年',expand=True)[0]).astype('int')
    data1['yue'] = ((data1['时间'].str)[-3:-1]).astype('int')
    data1=data1.loc[((data1['nian']>=2020)&(data1['nian']<2023)),:]
    data1=data1.loc[~((data1['nian']==2022)&(data1['yue']==12)),:]
    data1['品牌']=''#列号5
    data1['型号']=''
    data1['尺寸']=''
    data1['价格']=''
    data1['手机内存'] = data1['标题'].str.split('(', expand=True)[1]
    data1['手机内存']=data1['手机内存'].str.split(')', expand=True)[0]
    data1['cpu']=''
    data1['相机像素']=''
    data1['颜色']=''

    return data1

接下来的话,就是把dataframe里面的每一个参数链接提取出来,通过request请求获得该链接里面的手机品牌、内存、颜色、像素、尺寸等参数信息,并且存为同一个dataframe,这里面用到了beautifulsoup和etree去解析定位所要爬的参数,最后将dataframe存储为csv格式,如下图:

实现代码如下:

def canshu(data1):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'}
    for j in range(data1.shape[0]):
        r=requests.get(data1.iloc[j,2],headers=headers).text
        b=etree.HTML(r)
        c=BeautifulSoup(r,'lxml')
        data1.iloc[j,5]=b.xpath('/html/body/div[5]/div[2]/a[3]/text()')
        data1.iloc[j,6]=b.xpath('/html/body/div[5]/div[4]/div[1]/div[1]/div[1]/h2/b/a/text()')
        try:
            data1.iloc[j,7]=c.find('div',class_="cell-con-ul").find_all('ul')[2].find('p',paramname="屏幕尺寸").text.replace('\n','').replace(' ','').replace('\r','')

            data1.iloc[j,8]=b.xpath('/html/body/div[5]/div[4]/div[1]/div[1]/div[2]/span[2]/text()')

            data1.iloc[j,10]=c.find('div',class_="cell-con-ul").find_all('ul')[3].find('p',paramname="CPU型号").text.replace('\n','').replace(' ','').replace('\r','')
            data1.iloc[j,11]=c.find('div',class_="cell-con-ul").find_all('ul')[4].find('p',paramname="后置相机").text.replace('\n','').replace(' ','').replace('\r','')

            data1.iloc[j,12]=c.find('div',class_="cell-con-ul").find_all('ul')[1].find('p',paramname="手机颜色").text.replace('\n','').replace(' ','').replace('\r','')
        except:
            pass
    data1.to_csv('手机各品牌机型数据.csv',index=False)  # 保存数据到CSV文件
    print("第",j,"页")
    return data1

最后一个步骤就是可视化,要可视化先要读取数据,为了节省运行时间,先把存为csv格式的数据提取出来,存为dataframe,然后在去处理各个字段,比如把内存处理,如12+256GB这个数据处理成手机内存和物理内存两种,如果没有物理内存或者手机内存的设置为0,如果出现1TB这种,直接给1024GB;还有时间的处理,将年和月分别拆成按年和按月两个字段,方便去做季度统计;还有将尺寸处理成浮点型数据,方便后期去计算,里面用到的都是pandas的方法。当处理好之后,就可以使用matloplit进行可视化了,如下图处理好的数据和可视化效果。

具体代码如下:

import pandas as pd
import numpy as np
data=pd.read_csv('手机各品牌机型数据.csv',encoding='gbk')
data1=data.dropna(thresh=12)
data1['尺寸']=(data1['尺寸'].str.split('英寸',expand=True)[0]).astype('float')
data1['手机内存']=data1['手机内存'].str.split('G',expand=True)[0]
data1['物理内存']=data1['手机内存'].str.split('+',expand=True)[1]

data1['手机内存']=(data1['手机内存'].str.split('+',expand=True)[0])

data1=data1.dropna(thresh=13)
data1['季度']=None
for i in range(data1.shape[0]):
    if data1.iloc[i,9]=='1TB':
        data1.iloc[i, 9]='1024'
    if int(data1.iloc[i,9])>16:
        data1.iloc[i,13]=data1.iloc[i,9]
    if data1.iloc[i,13] is None:
        data1.iloc[i, 13]='0'
    if data1.iloc[i,13]=='':
        data1.iloc[i, 13] = '0'
    else:
        pass
data1['手机内存']=data1['手机内存'].astype('int')
data1['物理内存']=data1['物理内存'].astype('int')
for j in range(data1.shape[0]):
    if data1.iloc[j, 9]>16:
        data1.iloc[j, 9]=0
    if data1.iloc[j, 9]==1:
        data1.iloc[j, 9]==0
    if 0<data1.iloc[j,4]<4:
        data1.iloc[j,14]='第一季度'
    if 3<data1.iloc[j,4]<7:
        data1.iloc[j,14]='第二季度'
    if 6<data1.iloc[j,4]<10:
        data1.iloc[j,14]='第三季度'
    if 9<data1.iloc[j,4]<13:
        data1.iloc[j,14]='第四季度'
    else:
        pass
# data1 = data1.loc[((data1['手机内存'] >1) & (data1['手机内存'] <=16)), :]
print(data1)
print("所有手机的平均尺寸是:",data1['尺寸'].mean())
data2=data1.groupby(by=['yue'])['尺寸'].mean().reset_index().round(2)#统计月份平均尺寸
data3=data1.groupby(by=['季度'])['尺寸'].mean().reset_index().round(2)#统计季度平均尺寸
data4=data1.groupby(by=['品牌'])['尺寸'].mean().reset_index().round(2)#统计品牌平均尺寸
data1['价格']=data1['价格'].astype('int')
list_jiage=[]
data5=(data1.loc[((data1['价格']>=1000)&(data1['价格']<2000)),:])['尺寸'].mean().round(2)
data6=(data1.loc[((data1['价格']>=2000)&(data1['价格']<3000)),:])['尺寸'].mean().round(2)
data7=(data1.loc[((data1['价格']>=3000)&(data1['价格']<4000)),:])['尺寸'].mean().round(2)
data8=(data1.loc[((data1['价格']>=4000)&(data1['价格']<5000)),:])['尺寸'].mean().round(2)
data9=(data1.loc[((data1['价格']>=5000)&(data1['价格']<6000)),:])['尺寸'].mean().round(2)
data10=(data1.loc[((data1['价格']>=6000)&(data1['价格']<7000)),:])['尺寸'].mean().round(2)
data11=(data1[data1['价格']>=7000])['尺寸'].mean().round(2)
list_jiage.append(data5)
list_jiage.append(data6)
list_jiage.append(data7)
list_jiage.append(data8)
list_jiage.append(data9)
list_jiage.append(data10)
list_jiage.append(data11)
list_jiage1=['价格大于1000小于2000','价格大于2000小于3000','价格大于3000小于4000','价格大于4000小于5000','价格大于6000小于6000','价格大于6000小于7000','价格大于7000']

print(list_jiage)

import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']  # 解决中文显示问题
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
# 生成图形
plt.plot(data3['季度'], data3['尺寸'], 'go:', label='尺寸变化', linewidth=2) # 颜色绿色,点形圆形,线性虚线,设置图例显示内容,线条宽度为2
for a, b in zip(data3['季度'], data3['尺寸']):
    plt.text(a, b, b, ha='center', va='bottom', fontsize=20)
plt.ylabel('尺寸') # 横坐标轴的标题
plt.xlabel('季度') # 纵坐标轴的标题
plt.xticks(np.arange(0, 4, 1)) # 设置横坐标轴的刻度为 0 到 4的数组
plt.ylim([0, 10]) # 设置纵坐标轴范围为 -2 到 2
plt.legend() # 显示图例, 图例中内容由 label 定义
plt.grid() # 显示网格
plt.title('各个季度手机平均尺寸') # 图形的标题

# 显示图形
plt.show()

#定义函数来显示柱子上的数值
def autolabel(rects):
    for rect in rects:
        height = rect.get_height()
        plt.text(rect.get_x()+rect.get_width()/2.-0.08, 1.03*height, '%s' % height, size=6, family="Times new roman")

plt.figure(figsize=(4,3))

cm = plt.bar(data4['品牌'], data4['尺寸'], width=0.5, color=["blue", "green", "yellow", "magenta"])
autolabel(cm)

plt.ylim((0, 10))
plt.xlabel("品牌", size=12)
plt.ylabel("尺寸", size=12)
plt.xticks(rotation=90)
plt.title('各个品牌手机平均尺寸')
plt.show()

plt.plot(list_jiage1, list_jiage, 'go:', label='尺寸变化', linewidth=2) # 颜色绿色,点形圆形,线性虚线,设置图例显示内容,线条宽度为2
for a, b in zip(list_jiage1, list_jiage):
    plt.text(a, b, b, ha='center', va='bottom', fontsize=20)
plt.ylabel('尺寸') # 横坐标轴的标题
plt.xlabel('不同价格区间') # 纵坐标轴的标题
plt.xticks(rotation=90) # 设置横坐标轴的刻度为 0 到 4的数组
plt.ylim([0, 10]) # 设置纵坐标轴范围为 -2 到 2
plt.legend() # 显示图例, 图例中内容由 label 定义
plt.grid() # 显示网格
plt.title('不同价格区间的手机平均尺寸') # 图形的标题
plt.show()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值