Python基于selenium和seaborn的尼康F口镜头粗分析
思路(尽量省略代码,代码和注释放在最后)
用到的库
from selenium import webdriver
from selenium.webdriver.common.by import By
import pyautogui as auto
import os
import json
import pandas as pd
import matplotlib.pyplot as plt
import dataframe_image as dfi
import cv2
import numpy as np
import seaborn as sns
爬虫部分
- 首先我们爬取前,一定要知道自己爬取的目的,不能盲目的进行爬取,这样可能会导致大量无用的数据,或者造成数据冗余,这样会导致数据处理环节出现一定问题,所以我们一定要先对自己的需求进行分析,经过一段时间分析,我们的需求如下:
(1). 爬取F口镜头的所有名字
(2). 爬取F口镜头的焦段信息,光圈信息
(3). 爬取F口镜头的镜片组结构信息
(4). 爬取镜头的画幅信息(APS-C,全画幅)
(5). 爬取官网售价(如果有)
(6). 爬取镜头尺寸,重量(如果有)
选择上述信息是因为上述信息是消费者主要关注的信息,也是关联性较大的一些信息,在后续还有一定的分析价值。 - 我们新建一个爬虫类Spider,打开我们需要爬取的网页,这里我选择了尼康官网,这里的信息比较全,其实也可以选择其他的平台如中关村在线,或者是站长之家,我们仔细观察这个网站
尼康-F卡口镜头
可以看到这里一共有如下特征
(1). 一共需要爬取的产品数为87,这说明如果顺利,我们可以直接写一个含有87次的大循环来直接读取所有的信息
(2). 右上角有一个页面,最后有一个查看所有,可以看到他把所有的镜头都列在了一个可见页面,所以我们可以简化翻页步骤,直接放在同一个界面上找规律
(3). 这里每一颗镜头的名称已经给出,但是我们可以在界面内一起爬取,就省的单独写 - 我们按f12查看相应元素,可以找到其中的规律,然后写成一个循环,每次点击其中的一个镜头,然后读取完毕再回到这个界面进行下一次点击(具体细节其实比描述起来复杂,因为这一个界面其实是整合了7个界面的结果,xpath规律不太一样,需要多次找规律率,具体详见末尾代码部分)
- 我们随便进入一个镜头的介绍界面
可以发现我们需要爬取的部分位于列表的第三位,当然许多其他的界面可能不一样,要么位于第三位要么位于第二位,我们这里进行讨论,首先读取第三位的textContent里面的信息,判断是不是’技术规格’,如果是,那我们直接进行后续步骤,如果不是,那么我们就爬取第二个。但是这里也出现了一个例外那就是
这一面当中只有两个,所以如果一开始判断第三个会导致报错,但是如果我们先判断第二个,会导致程序速度显著变慢,如果我们每次判断列表长度,那么也会导致程序变慢,我们发现这里似乎只有这一个特例,所以我们选择捕获这个异常,在except当中单独处理
try:
JSGG = find_x(f'/html/body/div[1]/div[2]/ul/li[3]/a/p') # 进入技术规格界面
except:
JSGG = find_x(f'/html/body/div[1]/div[2]/ul/li[2]/a/p') # 有可能没有第三个
由于我们设置了implilcitywait(5),所以这里我们需要等待的时间就是它发现错误并等待响应的时间,也就是5s,这比87次每次需要将近一秒的寻找和判断时间还是少一点的
5. 我们将获取到的信息保存到字典中用json保存到本地,方便分析的时候进行调用。
with open('./lens_json.json', 'w+') as f:
json.dump(dic_lens, f, ensure_ascii=False)
分析部分
数据清洗
- 我们新建一个分析类Analyse,这里我们首先新建一个函数data_to_excel来对数据进行读取和清洗以及转excel储存操作,后续数据可以直接从excel中读取已经清洗完成的dataframe
- 我们提取的数据其实较多,所以我们这里要进行处理,我们提出需要的字段,这里首先确定我们需要的列名,放在index_当中,这里直接给出我处理的函数
def data_to_excel(self):
data = self.data
index_ = [
'名称',
'画幅',
'最小焦段',
'最大焦段',
'最大光圈',
'结构',
'对焦宁静波动马达',
'滤镜尺寸',
'减震',
'重量',
]
dic = [index_]
for i in data.keys():
ls = []
names = i.split()
# ls = [data[i][1][x] for x in [y for y in range(len(data[i][0])) for z in index_ if z in data[i][0][y]]]
ls.append(i)
if '增距' in i:
ls.extend([None]*9)
continue
ls.append("APS-C画幅") if "DX" in names else ls.append("全画幅")
# print(names)
ls.append([j for j in names if 'mm' in j][0].split("-")[0])
if len([j for j in names if 'mm' in j][0].split("-")) > 1:
ls.append([j for j in names if 'mm' in j][0].split("-")[1])
else:
ls.append([j for j in names if 'mm' in j][0].split("-")[0])
ls.append("".join([j for j in names if 'f/' in j]))
JieGou = [j for j in data[i][1] if '组' in j and '片' in j]
if JieGou == []:
ls.append('官网未提供')
else:
ls.append(JieGou[0])
MaDa = [j for j in data[i][1] if '马达' in j]
if MaDa == []:
ls.append('其他')
else:
ls.append(MaDa[0])
size = [data[i][1][j] for j in range(len(data[i][1])) if '镜尺' in data[i][0][j]]
if size == []:
ls.append('官网未提供')
else:
ls.append(size[0])
ls.append("VR防抖") if "VR" in names else ls.append('无')
Weight = [j for j in data[i][1] if 'g' in j]
if Weight == []:
ls.append('官网未提供')
else:
ls.append(Weight[0])
ls[2] = ls[2] + 'mm' if 'mm' not in ls[2] else ls[2]
dic.append(ls)
dic = pd.DataFrame(dic)
dic.columns = dic.values.tolist()[0]
dic.drop([0], inplace=True)
self.dic = dic
if 'Nikon_F_Mount.xlsx' not in os.listdir():
writer = pd.ExcelWriter('Nikon_F_Mount.xlsx')
dic.to_excel(writer)
writer.save()
- 为了更简单明了的观察数据,这里我选择把excel再转化为图片输出,详见代码部分,图片如下
统计分析
- 这里简单分析了镜头焦段区间,镜头光圈区间,以及他们两者的覆盖情况,具体分析内容是为了查看尼康F口镜头各个光圈下焦段的覆盖率
- 我们这里可以直接从镜头名当中简单的提取出焦段起始终止值和光圈,甚至是防抖类型和镜片镀膜结构,我们只需要前面的焦距和光圈即可
- 我们用matplotlib对取出来的数据进行绘制图表
- 这里的函数如下
def focal_lenth__f_stop_count(self):
if 'Nikon_F_Mount焦段光圈覆盖情况_sy.png' in os.listdir():
return
dic = self.dic
self.names = names = [i for i in dic['名称'].values]
start = list(map(eval, [i[:-2] for i in dic['最小焦段'].values]))
end = list(map(eval, [i[:-2] for i in dic['最大焦段'].values]))
f_stop = ["".join([j for j in i if j.isdigit() or j == '-' or j == '.']) for i in dic['最大光圈'].values]
f_stop_min, f_stop_max = [], []
for i in f_stop:
ls = list(map(float, i.split('-')))
if len(ls) == 1:
f_stop_max.append(ls[0])
f_stop_min.append(ls[0])
else:
f_stop_max.append(ls[1])
f_stop_min.append(ls[0])
for i in range(len(names)):
if start[i] != end[i]:
plt.plot([start[i], end[i]], [f_stop_min[i], f_stop_max[i]])
else:
print(start[i], end[i])
plt.plot([start[i], end[i]], [f_stop_min[i], f_stop_max[i]], marker = 'o')
plt.xlabel("focal_lenth")
plt.ylabel("f_stop")
plt.xlim((0, 810))
plt.ylim((1.2, 7))
plt.xticks([14, 24, 35, 50, 85, 120, 150, 200, 300, 400, 500, 600, 800])
plt.yticks([1.2, 1.4, 1.8, 2, 2.8, 3.5, 4, 4.5, 5.6, 6.3, 7])
plt.title("Nikon_F_Mount焦段光圈覆盖情况")
plt.legend(names)
plt.savefig('Nikon_F_Mount焦段光圈覆盖情况.png', dpi=350)
plt.show()
self.sign("Nikon_F_Mount焦段光圈覆盖情况.png")
图表如下
回归分析
- 这里简单的分析下可以进行回归分析的参数,我们用到了seaborn中的函数heatmap和pairplot
- 这里直接给出结果,代码详见后面
可以发现我们这里的最小焦段和最大焦段具有相关性较高的线性关系,但是这两个参数的线性关系分析意义不大,蕴含的信息太少
其他的几个参数之间相关性也较低,这可能是因为我对布尔类型值的赋值并不是很好,比如我对APS-C赋值为1,全画幅赋值为1.5,防抖有无设置为了0和1,这样似乎在分析上回有一定的问题,还有一个很重要的原因是我drop掉了许多有用信息,比如晶片组数量的信息,这个应该与画幅和焦段以及最大光圈都有一定的关系,还有滤镜尺寸也是。
代码
点击Nikon_F_Mount.py下载(要是您喜欢,麻烦star一下,您的支持对我有很大帮助,谢谢!)
总结
- 数据分析首先需要明确需要爬取的目标,我们再对所需要的获取信息的网站分析xpath
- 进行爬取的同时要考虑到效率问题
- 数据处理的时候不能丢掉太多数据,否则可能导致结果无效。
以上,共勉