python爬虫汽车之家全车型及基本参数入数据库(截止50524个数据)(详解)


文章目录

  • 免责声明

  • 一、总体思路

  • 二、需要使用的库

  • 三、具体实施

    • (1)页面1:

    • (2)页面2:

    • 在售页面

    • 停售页面

    • 1.第一种方向

    • 2.第二种方向

  • 四.基本参数写入数据库

  • 五.总结

免责声明

本人新手小白,看到网上很多类似的文章,本着实践,交流学习目的,如侵,立删。
如文章被转载利用,出现一切后果与本人(笔者)无关。

一、总体思路

目的:汽车之家官网所有的车型以及他的基本参数这些,我们知道每个车的ID不一样,那我们找到所有的ID,在找到他们的基本参数那就不是问题了。
分析网站:
闲话少说:第一种方向:是按照品牌一级一级往下找,比较繁琐;
第二种方向:按照车型对比界面,找到JSON提取数据,这个比较容易点
(那我们用第二种简单的方案不就行了,我当时也是这样觉得,但这样真的取得全吗?是所以的数据吗?带着这些疑问去实践不就好了)

二、需要使用的库

可能用到的库:

from selenium import webdriver
from pandas.core.frame import DataFrame
import json
import random
import pymysql
import re, time
import socket
import io
import sys
import os
import pandas as pd
import requests
from lxml import etree
from pyquery import PyQuery as pq
from bs4 import BeautifulSoup
from sqlalchemy import create_engine

三、具体实施

1.第一种方向

(1)页面1:


按F12打开开发者工具,监听一下动态页面刷到https://www.autohome.com.cn/grade/carhtml/B.html,那我们就可以联想到这些按A-Z排序遍历一下就可以把所有的品牌和对应车系ID拿下来了

分析一下需要的数据在那些标签,很整齐,建议使用BS4解析,代码如下(示例):

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
}#模拟游览器
headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
        random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
        random.randrange(1, 200, 20))#IP地址构造
url = 'https://www.autohome.com.cn/grade/carhtml/%d.html'
for i in range(ord("A"),ord("Z")+1):
    U=chr(i)
    new_url = "https://www.autohome.com.cn/grade/carhtml/%s.html" %U#字符串拼接A-Z字母
    #new_url = format(url%i)

    respone = requests.get(url=new_url,headers=headers)#发送请求
    respone.encoding = 'gbk'
    page_text = respone.text
    soup = BeautifulSoup(page_text, 'lxml')
    dls = soup.findAll("dl")#bs4直接定位所有的dl标签再遍历
    for dl in dls:
        brandId = dl.get("id")#品牌ID
        brandName = dl.find("dt").text#品牌名称
        # print(brandId,brandName)
        logo = dl.find("img")#loge
        cxzs = dl.find_all(class_="rank-list-ul")#直接定位这个车系的车系标签
        for cxz in cxzs:
            zm = cxz.findPrevious().text#车系名称

            cxs = cxz.findAll("li")
            for cx in cxs:
                try:
                    cxId = cx.get("id").replace("s", "")#车型ID
                    cxName = cx.find("a").text#车型名称

这样就拿到了这页面上的所有车型的ID,我们点击这些车系,会弹出宁一个窗口,但有2种情况,一个是停售界面,一个是在售界面,笔者没有分清楚折个界面(感觉很乱),只有一个个带进去了:

在售页面


在售界面的第一个分栏:也就是2种界面,一个在售一个停售:


我们获取停售的href标签网址

如果实在太多,就可以查询我们需要的数据,然后这样的界面就很规范,取出数据也很轻松:

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
}
headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
        random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
        random.randrange(1, 200, 20))
brandId = "3170"#车型ID
ts_url = "https://www.che168.com/autoseries/list.aspx?seriesid=%s"%(cxId)

content = requests.get(url=ts_url, headers=headers).text
soup = BeautifulSoup(content, "lxml")
cars = soup.findAll("li")

for c in cars:
   try:#异常处理,不是每一个车型页面都有的
       if c.find("input").get("all") == "0":
           car_id = c.find("input").get("specid")#汽车ID
           car_name = c.text.replace("\n","")#汽车名称
           # print(brandId,brandName,cxId,cxName,car_id,car_name)
               
   except:
       pass

第二个分栏:

定位li标签,我需要的是他的href标签,形成网址,后续的就会动态数据传输


这样的数据很显而易见了 ,但也别忘前面的数据,代码如下:

cxId = "3170"
url = "https://www.autohome.com.cn/%s"%(cxId)
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
}
headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
    random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
    random.randrange(1, 200, 20))
response = requests.get(url=url,headers=headers).text
soup = BeautifulSoup(response,'lxml')
list1 = soup.findAll(class_="spec-wrap active")
p1 = []#建空列表,为后面append函数传参数
for l in list1:
    if l.find_all("dd") != []:#肯定会有的车系没有,所以我们进行了判断
        dds = l.find_all("dd")
        for dd in dds:
            #�
            carName1 = dd.find("a").text#汽车名称
            carName1.replace("�","")
            if dd.find("p").get("data-gcjid") == None:#进行判断
                car_id1s = dd.find("a").get("href")#汽车ID
                car_id1 = re.findall(r'-?\d+\.?\d*e?-?\d*?', car_id1s)[0]
                yearName1 = re.findall(r'-?\d+\.?\d*e?-?\d*?', carName1)[0] + "款"#年款

                p1.append(car_id1)
                p1.append(carName1)
                p1.append(yearName1)
            else:
                car_id1 = dd.find("p").get("data-gcjid")
                yearName1 = dd.get("data-sift1")
                p1.append(car_id1)
                p1.append(carName1)
                p1.append(yearName1)
            # print(car_id,carName,yearName)
    else:
        continue
#上面是第一个在售页面原本就有的数据,下面我们就要获取其中参数构造请求获得数据,也就是停售款下拉表的数据
tree = etree.HTML(response)#这里用了XPATH定位解析
if tree.xpath('//div[@class="athm-title__sub"]//li[@class="more-dropdown"]/ul/li') == []:#进行了判断,不是每一个车型网页都有这个标签,
    list2 = tree.xpath('//div[@class="athm-title__sub"]//li[1]/ul/li')

    for li in list2:
        syearid = li.xpath('./a/@data-yearid')[0]#获得了很重要的参数syearid
        # print(cxId,syearid)
        url1 = "https://www.autohome.com.cn/ashx/car/Spec_ListByYearId.ashx?seriesid=%s&syearid=%s" % (cxId, syearid)#进行了字符串的拼接
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
        }
        headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
            random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
            random.randrange(1, 200, 20))
        # car_id,carName,brandId, brandName, cxId, cxName,yearName
        try:#异常处理
            content = requests.get(url=url1, headers=headers).json()#json数据格式,类似于字典,理清层级关系
            for group1 in content:
                for group2 in group1["speclist"]:
                    car_id2 = group2["specid"]#汽车ID
                    carName2 = group2["specname"]#汽车名称
                    yearName2 = re.findall(r'-?\d+\.?\d*e?-?\d*?', carName2)[0] + "款"#年款
                    # print(car_id, carName, yearName)
                    p1.append(car_id2)
                    p1.append(carName2)
                    p1.append(yearName2)
        except:
            pass
else:#跟上面的一样,正反2个方面
    list2 = tree.xpath('//div[@class="athm-title__sub"]//li[2]/ul/li')
    # print(list2)

    for li in list2:
        syearid = li.xpath('./a/@data-yearid')[0]
        # print(cxId,syearid)
        url1 = "https://www.autohome.com.cn/ashx/car/Spec_ListByYearId.ashx?seriesid=%s&syearid=%s" % (cxId, syearid)
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
        }
        headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
            random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
            random.randrange(1, 200, 20))
        # car_id,carName,brandId, brandName, cxId, cxName,yearName
        try:
            content = requests.get(url=url1, headers=headers).json()
            for group1 in content:
                for group2 in group1["speclist"]:
                    car_id2 = group2["specid"]
                    carName2 = group2["specname"]
                    yearName2 = re.findall(r'-?\d+\.?\d*e?-?\d*?', carName2)[0] + "款"
                    # print(car_id, carName, yearName)
                    p1.append(car_id2)
                    p1.append(carName2)
                    p1.append(yearName2)
        except:
            pass


results = [p1[i:i+3] for i in range(0,len(p1),3)]#3个参数为一组

for z in results:
    td1 = [str(i) for i in z]
    td2 = ','.join(td1)  # 列表和字符串之间的转换
    print(td2)

大功告成,参数都整齐,最后这几部分代码拼接写入CSV文件什么的就OK了,

停售页面

这个停售页面跟上面的不一样哦,有空可以试一下:(打开奥迪A4)

这个页面管理一下没有动态数据加载的过程,那就很舒服啦,笔者使用的是BS4加正则表达式,代码如下:

cxId = "19"#车系ID
url1 = "https://www.autohome.com.cn/%s"%(cxId)
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
}
headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
    random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
    random.randrange(1, 200, 20))

content = requests.get(url=url1,headers=headers).text
soup = BeautifulSoup(content, "lxml")
cars = soup.findAll(class_="name")#标签定位,这里有点麻烦,以后改进
for c in cars:
    #print(c)
    if c.find("a") != None:#判断排除一些不需要的,正则提取字符串的数字也就是汽车ID
        car_ids = c.find('a').get("href")
        car_id = re.findall(r'-?\d+\.?\d*e?-?\d*?', car_ids)[0]#汽车ID
        car_Name = c.find("a").get("title")#汽车名称
        yearName = re.findall(r'-?\d+\.?\d*e?-?\d*?', car_Name)[0] + "款"#年款

        print(car_id,car_Name,yearName)

最后部分代码拼接一下,可能需要去重,那就是后来的处理了

(2)页面2:

为什么会有页面呢,因为后面数据对比发现,页面1没有的数据,页面2有,也就是说他这个不全:

按F12,监听一下动态加载的页面,我就会发现,品牌和车型都在这个列表上:
第一步获取品牌,品牌ID和品牌的这个界面网址:

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
}
headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
        random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
        random.randrange(1, 200, 20))
url = 'https://car.autohome.com.cn/AsLeftMenu/As_LeftListNew.ashx?typeId=1%20&brandId=0%20&fctId=0%20&seriesId=0'
product_response = requests.get(url=url,headers=headers).text
soup = BeautifulSoup(product_response,'lxml')
product_lis = soup.findAll("li")#标签定位
for pr in product_lis:
    brandId = pr.get("id").replace("b","")#品牌ID
    brandName = pr.find("a").text#品牌名称
    brand_url = "https://car.autohome.com.cn" + pr.find("a").get("href")#品牌网址
    print(brandId,brandName,brand_url)

第二部:获取车系ID和车系网址:

也就是获取A标签中的href和文本内容,代码如下:

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
}
headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
        random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
        random.randrange(1, 200, 20))
brand_url = 'https://car.autohome.com.cn/price/brand-238.html'
response1 = requests.get(url=brand_url,headers=headers).text
soup = BeautifulSoup(response1,'lxml')

dls = soup.findAll(class_="list-dl")#标签定位
for dl in dls:
    cxNames = dl.find_all(class_="list-dl-text")
    for dd in cxNames:
        cxName_list = dd.find_all("a")
        for a in cxName_list:
            cxName = a.text#车型名称
            cx_href = a.get("href")
            cx_id = re.findall('\d+', cx_href)[0]#车型ID
            cx_url = "https://car.autohome.com.cn" + cx_href#车型网址
            print(cxName,cx_id,cx_url)

最后一步,获取汽车ID和名称:

显示渠道这2个标签的网址,然后我们在对网址发送请求,解析提取我们想要的数据,代码如下:

cx_url = "https://car.autohome.com.cn/price/series-3170.html#pvareaid=2042205"#奥迪A3的网址

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
}
headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
        random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
        random.randrange(1, 200, 20))
response2 = requests.get(url=cx_url,headers=headers).text

tree = etree.HTML(response2)

lis = tree.xpath('//div[@class="tab-nav border-t-no"]/ul/li')#这里我们用了Xath解析
p1 = []#创建空列表,传参
for li in lis:
    if li.xpath('./a/@href') != []:#依旧是判断,有可能这界面没有网址
        href_url = "https://car.autohome.com.cn" + li.xpath('./a/@href')[0]
        # print(href_url)
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
        }
        headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
            random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
            random.randrange(1, 200, 20))
        response3 = requests.get(url=href_url, headers=headers).text#再次发送请求
        soup = BeautifulSoup(response3,'lxml')
        if soup.findAll(class_="interval01-list") != []:#进行判段,可能会没有返回的是空列表
            uls = soup.findAll(class_="interval01-list")
            for ul in uls:
                t1 = ul.find_all("li")
                for t2 in t1:
                    car_id = t2.get("data-value")#汽车ID
                    p1.append(car_id)
                    car_name = t2.find(class_="interval01-list-cars-infor").find("a").text#汽车名称
                    p1.append(car_name)
        if soup.findAll(class_="page") != []:#考虑到有没有分页,依然是判断
            page_list = soup.findAll(class_="page")
            for page in page_list:
                pages = page.find_all("a")
                for pa in pages:
                    if pa.get("class") == None:
                        href_url2 = "https://car.autohome.com.cn" + pa.get("href")#构造分页的网址
                        # print(href_url2)
                        headers = {
                            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
                        }
                        headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
                            random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
                            random.randrange(1, 200, 20))
                        response4 = requests.get(url=href_url2, headers=headers).text
                        soup4 = BeautifulSoup(response4, 'lxml')
                        if soup4.findAll(class_="interval01-list") != []:判断可能会没有数据,防止报错
                            uls4 = soup4.findAll(class_="interval01-list")
                            for ul4 in uls4:
                                t14 = ul4.find_all("li")
                                for t24 in t14:
                                    car_id4 = t24.get("data-value")#汽车ID
                                    p1.append(car_id4)
                                    car_name4 = t24.find(class_="interval01-list-cars-infor").find("a").text#汽车名称
                                    p1.append(car_name4)


result1 = [p1[i:i + 2] for i in range(0, len(p1), 2)]#2个参数每一组
for z in result1:
    td1 = [str(i) for i in z]
    td2 = ','.join(td1)#列表与字符串之间的传唤
    print(td2)

最后再把几个部分的代码拼接一下,写入文件,跟上面的情况去重看看,会不会多了写数据。

2.第二种方向


参数对比的页面,我们点击选择车型,按F12进行监听,等到了json数据,那结果就很明显了,类似于字典一样的处理,代码如下:

import requests
import re
import json
import pymysql
import random
from lxml import etree
from bs4 import BeautifulSoup
import pandas as pd
from pandas.core.frame import DataFrame
cxId = "3972"#车型ID
cx_url = "https://car.autohome.com.cn/duibi/ashx/specComparehandler.ashx?type=1&seriesid=%s&format=json"%(cxId)

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
}
headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
        random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
        random.randrange(1, 200, 20))
response2 = requests.get(url=cx_url,headers=headers).json()
p2 = []
p3 = []
for group1 in response2["List"]:
    # print(group1["List"])
    for group2 in group1["List"]:
        car_id = group2["I"]
        p2.append(car_id)
        car_name = group2["N"]
        p2.append(car_name)
        # print(car_id,car_name)
        p1 = [car_id,car_name]
        p3.append(p1)
print(p3)
result2 = [p2[i:i + 2] for i in range(0, len(p2), 2)]
new = DataFrame(p3,columns=['cxId','cxName'])#转成DataFrame格式
print(new)
new.to_excel("new.xls")#写入文件
print("完成")

讲到这里,获取数据基本结束了

四.基本参数写入数据库

你这里好了全部的汽车ID,然后去车型对比页遍历这些ID网站,获得json数据,获取即可,最后写入数据库:

table = 'cars_parameters'#表名
dicts = {
        'car_id': "",
        'parameter_name': "",
        'parameter_value': "",

                        }#创建空字典
keys = ','.join(dicts.keys())
values = ','.join(["%s"] * len(dicts))
sql1 = 'INSERT INTO {table}({keys}) VALUES ({values})'.format(table=table, keys=keys,
                                                              values=values)#构造动态sql语句
db = pymysql.connect(
                            host='localhost',
                            user='root',
                            password='123',
                            port=3306,
                            db='cars_home',
                            charset='utf8'
                        )#连接数据库
cursor = db.cursor()#获取游标
sql = 'CREATE TABLE IF NOT EXISTS cars_parameters(car_id VARCHAR(255) NOT NULL,parameter_name VARCHAR(255),parameter_value VARCHAR (255))'
cursor.execute(sql)#创建数据表cars_parameters,并执行


headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
}
headers["X-Forwarded-For"] = "%s.%s.%s.%s" % (
        random.randrange(1, 200, 20), random.randrange(1, 200, 20), random.randrange(1, 200, 20),
        random.randrange(1, 200, 20))
df = pd.read_excel("3.xlsx",usecols=[0],names=None)#读取你处理好的汽车ID
df_li = df.values.tolist()
results = []
for s_li in df_li:
    results.append(s_li[0])
for q in results:
    car_id = q
    print(car_id)
    
    try:
        url2 = "https://carif.api.autohome.com.cn/Car/Spec_ParamListBySpecList.ashx?speclist=%s" % (
            car_id)
        response = requests.get(url=url2,headers=headers).json()#json数据
        groups = response["result"]["paramtypeitems"]
        group_name_list = []
        group_name_list2 = []
        for group in groups:#循环遍历自己想要的数据
            group_Name = group["name"]
            # print(group_Name)
            group1 = group["paramitems"]
            # print(group_Name,group1)
            for group2 in group1:
                group_Name_1 = group2["name"]
                group3 = group2["valueitems"]
                # print(group_Name,group_Name_1,group3)
                for group4 in group3:
                    carID = group4["specid"]
                    group_Name_2 = group4["value"]
                    # group_name_list.append(group_Name_1)
                    # group_name_list2.append(group_Name_2)
                    #print(car_id,group_Name_1,group_Name_2)
                    dicts = {
                        'car_id': car_id,
                        'parameter_name': group_Name_1,
                        'parameter_value': group_Name_2,

                    }#得出的值传入字典
                    try:
                        cursor.execute(sql1, tuple(dicts.values()))#元组数据插入表
                    except:
                        db.rollback()
                    else:
                        db.commit()
    except:
        pass



print("完成")

补充一句,engine.execute(sql)可以直接执行sql语句,可能会更快更方便。

五.总结

谢谢你百忙之中看到这里,辛苦了,上述的方法可能不是最好的方法,也可能数据取不全,只是分享了一些自己的看法,如有不足,一起交流学习。

欢迎关注公众号:Python爬虫数据分析挖掘

记录学习python的点点滴滴;

回复【开源源码】免费获取更多开源项目源码;

公众号每日更新python知识和【免费】工具;

本文已同步到【开源中国】和【腾讯云社区】;

posted @ 2020-10-09 18:51  Python研究者  阅读(1443)  评论(1编辑  收藏  举报