购车数据资源公司(我爬了这些数据)
购车数据资源公司(我爬了这些数据)再举个,我想知道486个品牌中,每个品牌评分最高的车型可以看到,现代和起亚在小型轿车和中型MPV是有销量的,悦纳曾经也是月销过万的主流合资轿车,这里它虽然位于小型桥车销量NO.10,但8月仅销售303辆。确实有点惨淡啊,除了伊兰特,8月销量超过1千辆的只有一个库斯途,其它最多的的月销也只有1到3百辆,它们曾经非常受欢迎,不过随着汽车行业的更新换代,逐渐淡出了消费者们的视野。正好前段时间在python技术分享过一篇《不止高效,原来pandas表格可以更美的!》 ,结合里面介绍过的排序分组配色,对我们的数据进行分析。通过这些数据,可以看出厂商的产品布局和销售优势。比如马路上很多别克牌子,我又对它没啥印象,除了知道威朗在紧凑型轿车中排名靠前,还有其它热销车型吗?所以特意看了下别克的数据:好家伙,居然有排名第一的!再比如韩国车企在中国没落了,落寞成什么样子呢,
作者:ssw
来源:python 技术
上班摸鱼看了2个星期车评,还是一头雾水,选合资还是国产?发动机cvt好还是双离合好?艾瑞泽5 GT动力足,但腰线和前脸让人吐槽,真的可靠吗。国产选长安逸动还是吉利帝豪?标志408出来了,还有艾瑞泽8很漂亮。看会视频吧,同质化严重,讲来将去就是车内车外介绍一遍。
热门视频下通常有几百条评论,我一般会翻看一遍,七个八个视频就是几千条评论,信息芜杂,没个定准,所以干脆不看了!我决定从评分入手,爬下懂车帝看到底有多少个汽车品牌。长安、吉利、奇瑞它们有多少车型,高分段的车型多不,每家热销的车子分数排在哪个段位。所以用scrapy爬了3655条数据。
正好前段时间在python技术分享过一篇《不止高效,原来pandas表格可以更美的!》 ,结合里面介绍过的排序分组配色,对我们的数据进行分析。通过这些数据,可以看出厂商的产品布局和销售优势。
比如马路上很多别克牌子,我又对它没啥印象,除了知道威朗在紧凑型轿车中排名靠前,还有其它热销车型吗?所以特意看了下别克的数据:
好家伙,居然有排名第一的!再比如韩国车企在中国没落了,落寞成什么样子呢,
可以看到,现代和起亚在小型轿车和中型MPV是有销量的,悦纳曾经也是月销过万的主流合资轿车,这里它虽然位于小型桥车销量NO.10,但8月仅销售303辆。确实有点惨淡啊,除了伊兰特,8月销量超过1千辆的只有一个库斯途,其它最多的的月销也只有1到3百辆,它们曾经非常受欢迎,不过随着汽车行业的更新换代,逐渐淡出了消费者们的视野。
评分最高的车型再举个,我想知道486个品牌中,每个品牌评分最高的车型
486个品牌的车型评分,已上传,http://ssw.fit/file/ 。由于部分品牌没有车型,如“众泰”没一款车型,所以爬的时候不会把这种写入csv文件中。一共3655条数据,也就是3655个车型。
对car.csv进行处理:
import pandas as pd
df2 = pd.read_csv("D:/桌面/car.csv" encoding='gb18030')
#取出评分大于0的(也就是去掉懂车帝上显示“暂无评分”的)
x = df2.groupby('评分').filter(lambda x:x['评分'].mean() > 0)
#取各组品牌中评分最高的
t = x.sort_values('评分' ascending=False).groupby('品牌' as_index=False).first()
t.sort_values('评分' ascending=False inplace=True)
t.to_csv('评分榜.csv' index = True)
输出如下:
可以看到,所有品牌中,评分最高的车型是劳斯莱斯的“幻影”,4.74分,这个分数很高了。要知道国产长安最高3.88(长安UNI-K),吉利最高3.9(星越L),日产最高也没超过4分。
说明几个地方下面开始整活。先说明几个地方:
- 因为品牌较多,对应不同的url,可以利用multiprocessing.dummy多线程加快速度(这是个好工具,咱们的另一篇文章《3个python小工具,Linux服务器性能直线飞起!!!》 也用到过它)
- 使用的爬虫框架是scrapy,它结构清晰,使用起来方便,比所有内容写到一个文件里好些。
- 有些页面是动态加载的,需要使用selenium模拟页面向下滚动加载,把滚动条拉到最下面。
- 大概运行过程是这样的:
好了,先从观察页面开始。
猜了个猜观察它的url。首先来个十八连猜,猜下它尾部的18个“x”分别代表什么意思?
https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x
我先猜一个,倒数第3个x代表汽车品牌,不信吗,修改数字的话,第二个红框会出现不同的品牌:
所以url的倒数第三位,数字3代表奔驰,2为奥迪,4是宝马。其它你可以试一下,能搜索到结果的数字大概在600以内。我猜汽车品牌数也在600内吧。当然不能这么一个个猜, 还是赶紧干正事,找出每个品牌对应的数字,这样才好向相关品牌的页面发送请求。
找了个找一共有6个属性为layout_label__1qfS8的span标签,我们要找的是第6个,即找到“已知条件”这个span标签。
这个时候下面脚本中的条件condition[5]才成立,BeautifulSoup才找得到:
soup = BeautifulSoup(rep.text 'html.parser')
condition = soup.find_all('span' class_='layout_label__1qfS8')
# 当num大于500时,有可能没这个品牌,condition[5]会报错
try:
condition[5].next_sibling.a.text
except Exception as e:
pass
因为数字大于500时,很可能没这个品牌,页面上不会出现“已知条件”,而是提示“0车系符合条件”
上面的next_sibling属性用来查询兄弟节点,也就是“已选条件”那个span的下一个span;next_sibling.a.text,下一个span的a标签里的文字就是品牌的名字
找出品牌对应的idBeautifulSoup找到数据后,因为品牌对应的url较多,使用multiprocessing.dummy多线程加快速度。另外,数字大于500时,很可能找不到品牌,所以循环1000以内的数字基本能覆盖到所有品牌
pool = ThreadPool(10)
pool.map(get_brand_id [i for i in range(1 1000)])
综上,获取品牌对应id的完整脚本:
# -*- coding: utf-8 -*-
import json re requests ssl
from bs4 import BeautifulSoup
from multiprocessing.dummy import Pool as ThreadPool
num_list = []
brand_dict = {}
def get_brand_id(num):
x = 'https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-%s-x-x' % num
rep = requests.get(x timeout=10)
soup = BeautifulSoup(rep.text 'html.parser')
condition = soup.find_all('span' class_='layout_label__1qfS8')
# 当num大于500时,有可能没这个品牌,condition[5]会报错
try:
s = condition[5].next_sibling.a.text
print('s111' s)
except Exception as e:
pass
for span in condition:
if span.string == '已选条件':
print('ok')
brand_dict[s] = num
num_list.append(num)
pool = ThreadPool(10)
pool.map(get_brand_id [i for i in range(1 1000)])
print(num_list)
print(brand_dict)
输出结果如下,总共486个汽车品牌:
{
前面略...
'雪佛兰': 6
'雪铁龙': 21
'零跑汽车': 207
'雷丁': 282
'雷克萨斯': 22
'雷诺': 46
'雷诺三星': 301
'雷达汽车': 514
'霍顿': 278
'领克': 174
'领志': 309
'领途汽车': 247
'飞凡汽车': 401
'飞碟汽车': 404
'首望': 340
'马自达': 15
'驭胜': 167
'骐铃汽车': 104
'高合': 249
'魏牌': 66
'黄海': 132
'龙程汽车': 415
}
品牌对应的url如下,我们可以挑选自己感兴趣的品牌url发送请求:
[
前面略...
'https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-101-x-x'
'https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-76-x-x'
'https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-1-x-x'
'https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-126-x-x'
]
下面会用scrapy爬虫框架对这些url发起请求,数据写入csv
scrapy设置整个目录结构如下:
- 创建scrapy项目
#创建scrapy项目
scrapy startproject dcd
cd dcd
#生成一个爬虫
scrapy genspider car "https://www.dongchedi.com/"
- 修改settings.py
# 是否遵守协议 设置false
ROBOTSTXT_OBEY = False
#设置请求头
DEFAULT_request_HEADERS = {
'Accept': 'text/html application/xhtml xml application/xml;q=0.9 */*;q=0.8'
'Accept-Language': 'en'
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0'
}
#下载中间件
DOWNLOADER_MIDDLEWARES = {
'dcd.chrom_middlewares.DcdDownloaderMiddleware': 543
}
ITEM_PIPELINES = {
'dcd.pipelines.DcdPipeline': 300
}
- 新建一个chrom_middlewares.py文件
第2步DOWNLOADER_MIDDLEWARES设置的下载中间件,我们自己编写:chrom_middlewares.py
import time
from selenium import webdriver
from scrapy.http.response.html import HtmlResponse
class DcdDownloaderMiddleware(object):
def __init__(self):
# selenium加载浏览器
options = webdriver.ChromeOptions()
options.add_argument('--no-sandbox')
options.add_argument('--disable-gpu')
options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors')
self.driver = webdriver.Chrome(executable_path=r"C:\drf2\drf2\chromedriver.exe" options=options)
self.driver.maximize_window()
#重写process_request方法
def process_request(self request spider):
print('request.url' request.url)
self.driver.get(request.url)
js = 'return document.body.scrollHeight;'
height = 0
#selenium模拟页面向下滚动加载全部页面
if request.url != 'https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x':
while True:
new_height = self.driver.execute_script(js)
if new_height > height:
self.driver.execute_script('window.scrollTo(0 document.body.scrollHeight)')
height = new_height
time.sleep(1)
else:
print("滚动条已经处于页面最下方!")
break
source = self.driver.page_source
# 创建一个response对象 把页面信息都封装在reponse对象中
response = HtmlResponse(url=self.driver.current_url body=source request = request encoding="utf-8")
return response
对process_request说明一点:
如果车型多,需要滚动鼠标分一次或多次才能加载完毕,这个时候需要selenium模拟页面向下滚动加载全部车型,否则取到的车型是不全的。
while True:
new_height = self.driver.execute_script(js)
if new_height > height:
self.driver.execute_script('window.scrollTo(0 document.body.scrollHeight)')
height = new_height
time.sleep(1)
else:
print("滚动条已经处于页面最下方!")
break
- item.py
import scrapy
class DcdItem(scrapy.Item):
#品牌
brand = scrapy.Field()
#车型
name = scrapy.Field()
#评分
score = scrapy.Field()
#特点
title = scrapy.Field()
这几个字段的意思用箭头标明了:
- car.py
import scrapy
from lxml import etree
from dcd.items import DcdItem
import os csv
if os.path.exists('D:/桌面/car.csv'):
print('delete?')
os.remove('D:/桌面/car.csv')
f = open('D:/桌面/car.csv' 'a ' newline='' encoding='gb18030')
f_csv = csv.writer(f)
f_csv.writerow(['品牌' '车型' '评分' '特点'])
class RainSpider(scrapy.Spider):
name = 'car'
allowed_domains = ['https://www.dongchedi.com/']
start_urls = ['https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-11-x-x' 'https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-13-x-x']
def parse(self response):
print('html111')
html =etree.HTML(response.text)
item = DcdItem()
brand = html.xpath('//*[@id="__next"]/div[1]/div[2]/div/div[4]/span[2]/div/div/a/text()')[0]
lis = html.xpath('//*[@id="__next"]/div[1]/div[2]/div/ul/li[position()>= 1]')
print('111 lis' lis)
for li in lis:
name = li.xpath('./div/a[1]/text()')[0]
try:
#有评分
score = li.xpath('./div/a[2]/text()')[0].split('分')[0]
except Exception as e:
#无评分
score = 0
try:
#有标题
title = li.xpath('./div/span/text()')[0]
# print('title111' title)
except Exception as e:
#无标题
title = '无'
print(name score title)
f_csv.writerow([brand name score title])
item['name'] = name
item['score'] = score
item['title'] = title
yield item
下面对car.py的2个地方进行说明
5.1 获取品牌
brand = html.xpath('//*[@id="__next"]/div[1]/div[2]/div/div[4]/span[2]/div/div/a/text()')[0]
xpath路径,在edge浏览器中可以通过右键“检查”找到元素,再右键选择“复制”->"复制 Xpath"
按crtl F键可以粘贴刚才复制的xpath,按回车键,页面上会突出显示对应的元素。
5.2 获取所有的li标签,代表一辆辆汽车信息
然后循坏这些li标签,获取到车型、评分、左上角的蓝色说明文字,写入csv文件
lis = html.xpath('//*[@id="__next"]/div[1]/div[2]/div/ul/li[position()>= 1]')
- 新增一个启动爬虫的文件start.py
from scrapy.cmdline import execute
execute('scrapy crawl car'.split(' '))
文件位置:
在pycharm中右击即可运行爬虫:
排序分组配色参考上文提到的《不止高效,原来pandas表格可以更美的!》
假如你想了解长安吉利奇瑞这3个品牌,那么在car.py中填写对应的url
#url中的73代表吉利,18是奇瑞,35是长安,对这3个品牌发起请求
start_urls = ['https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-73-x-x' 'https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-18-x-x' 'https://www.dongchedi.com/auto/library/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-35-x-x']
这样car.csv中就只有这3个品牌的数据了,方便我们配色和对比
import pandas as pd
from datetime import datetime timedelta
df2 = pd.read_csv("D:/桌面/car.csv" encoding='gb18030')
#取出评分大于0的(也就是去掉懂车帝上显示“暂无评分”的)
x = df2.groupby('评分').filter(lambda x:x['评分'].mean() > 0)
x.sort_values('评分' ascending=True inplace=True)
new = x.groupby(['品牌' '评分' '车型' '特点'] as_index=False)
new3 = new.all()
#给每种品牌加上颜色
#评分大于3.8的,用黄色标注
new3.style.highlight_between(left='吉利汽车' right='吉利汽车' subset=['品牌'] props='background:#ffa5a5')\
.highlight_between(left='奇瑞' right='奇瑞' subset=['品牌'] props='background:#a1eafb')\
.highlight_between(left='长安' right='长安' subset=['品牌'] props='background:#71c9ce')\
.highlight_between(left=3.8 right=5 subset=['评分'] props='background:#f9ed69')
输出结果如下:
从数据可以看出,国内一线品牌产品线丰富,吉利在小型SUV、紧凑型轿车、紧凑型SUV都有热销产品,奇瑞仅瑞虎3x和瑞虎5x位于销量榜前10(怎么没有出口汽车?),看来理工男从产品受欢迎程度上来说离一线品牌还有差距。可以根据自己的喜好,给想看的品牌配上它们的logo色,看看它们的数据是否有惊喜。