程序员买车推荐(我爬了这些数据)

作者:ssw

来源:python 技术

上班摸鱼看了2个星期车评,还是一头雾水,选合资还是国产?发动机cvt好还是双离合好?艾瑞泽5 GT动力足,但腰线和前脸让人吐槽,真的可靠吗。国产选长安逸动还是吉利帝豪?标志408出来了,还有艾瑞泽8很漂亮。看会视频吧,同质化严重,讲来将去就是车内车外介绍一遍。

热门视频下通常有几百条评论,我一般会翻看一遍,七个八个视频就是几千条评论,信息芜杂,没个定准,所以干脆不看了!我决定从评分入手,爬下懂车帝看到底有多少个汽车品牌。长安、吉利、奇瑞它们有多少车型,高分段的车型多不,每家热销的车子分数排在哪个段位。所以用scrapy爬了3655条数据。

正好前段时间在python技术分享过一篇《不止高效,原来pandas表格可以更美的!》 ,结合里面介绍过的排序分组配色,对我们的数据进行分析。通过这些数据,可以看出厂商的产品布局和销售优势。

比如马路上很多别克牌子,我又对它没啥印象,除了知道威朗在紧凑型轿车中排名靠前,还有其它热销车型吗?所以特意看了下别克的数据:

程序员买车推荐(我爬了这些数据)(1)

好家伙,居然有排名第一的!再比如韩国车企在中国没落了,落寞成什么样子呢,

程序员买车推荐(我爬了这些数据)(2)

可以看到,现代和起亚在小型轿车和中型MPV是有销量的,悦纳曾经也是月销过万的主流合资轿车,这里它虽然位于小型桥车销量NO.10,但8月仅销售303辆。确实有点惨淡啊,除了伊兰特,8月销量超过1千辆的只有一个库斯途,其它最多的的月销也只有1到3百辆,它们曾经非常受欢迎,不过随着汽车行业的更新换代,逐渐淡出了消费者们的视野。

评分最高的车型

再举个,我想知道486个品牌中,每个品牌评分最高的车型

程序员买车推荐(我爬了这些数据)(3)

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)

可以看到,所有品牌中,评分最高的车型是劳斯莱斯的“幻影”,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代表汽车品牌,不信吗,修改数字的话,第二个红框会出现不同的品牌:

程序员买车推荐(我爬了这些数据)(5)

所以url的倒数第三位,数字3代表奔驰,2为奥迪,4是宝马。其它你可以试一下,能搜索到结果的数字大概在600以内。我猜汽车品牌数也在600内吧。当然不能这么一个个猜, 还是赶紧干正事,找出每个品牌对应的数字,这样才好向相关品牌的页面发送请求。

找了个找

一共有6个属性为layout_label__1qfS8的span标签,我们要找的是第6个,即找到“已知条件”这个span标签。

程序员买车推荐(我爬了这些数据)(6)

这个时候下面脚本中的条件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车系符合条件”

程序员买车推荐(我爬了这些数据)(7)

上面的next_sibling属性用来查询兄弟节点,也就是“已选条件”那个span的下一个span;next_sibling.a.text,下一个span的a标签里的文字就是品牌的名字

程序员买车推荐(我爬了这些数据)(8)

找出品牌对应的id

BeautifulSoup找到数据后,因为品牌对应的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设置

整个目录结构如下:

程序员买车推荐(我爬了这些数据)(9)

  1. 创建scrapy项目

#创建scrapy项目 scrapy startproject dcd cd dcd #生成一个爬虫 scrapy genspider car "https://www.dongchedi.com/"

  1. 修改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, }

  1. 新建一个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

程序员买车推荐(我爬了这些数据)(10)

  1. item.py

import scrapy class DcdItem(scrapy.Item): #品牌 brand = scrapy.Field() #车型 name = scrapy.Field() #评分 score = scrapy.Field() #特点 title = scrapy.Field()

这几个字段的意思用箭头标明了:

程序员买车推荐(我爬了这些数据)(11)

  1. 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,按回车键,页面上会突出显示对应的元素。

程序员买车推荐(我爬了这些数据)(12)

5.2 获取所有的li标签,代表一辆辆汽车信息

然后循坏这些li标签,获取到车型、评分、左上角的蓝色说明文字,写入csv文件

lis = html.xpath('//*[@id="__next"]/div[1]/div[2]/div/ul/li[position()>= 1]')

程序员买车推荐(我爬了这些数据)(13)

  1. 新增一个启动爬虫的文件start.py

from scrapy.cmdline import execute execute('scrapy crawl car'.split(' '))

文件位置:

程序员买车推荐(我爬了这些数据)(14)

在pycharm中右击即可运行爬虫:

程序员买车推荐(我爬了这些数据)(15)

排序分组配色

参考上文提到的《不止高效,原来pandas表格可以更美的!》

假如你想了解长安吉利奇瑞这3个品牌,那么在car.py中填写对应的url

程序员买车推荐(我爬了这些数据)(16)

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

输出结果如下:

程序员买车推荐(我爬了这些数据)(17)

从数据可以看出,国内一线品牌产品线丰富,吉利在小型SUV、紧凑型轿车、紧凑型SUV都有热销产品,奇瑞仅瑞虎3x和瑞虎5x位于销量榜前10(怎么没有出口汽车?),看来理工男从产品受欢迎程度上来说离一线品牌还有差距。可以根据自己的喜好,给想看的品牌配上它们的logo色,看看它们的数据是否有惊喜。

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页