【Python爬虫】获取汽车之家车型配置附代码(2024.10)

参考大哥,感谢大哥:https://blog.csdn.net/weixin_43498642/article/details/136896338

【任务目标】

工作需要想更方便地下载汽车之家某车系配置清单;(垃圾汽车之家不给下载导出表格,配置页叉掉了车系要出来还要重新刷新,懂不懂用户体验)

每一个车系保存为一个Excel表格,表格命名为"品牌名+车系"。

同品牌的配置表保存到以品牌命名的文件夹中。

【实现效果】

【难点痛点】

1、(跳过这条发疯)真的好难找参考代码!可恶!找到的这个大哥文章还给锁了,痛失两百多,下头csdn你欠我的用什么还!你有本事后面别把我的锁成vip!

2、第一次爬不知道干啥,源代码找不到表内数据,不知道在哪找,参考大哥代码一步步做,发现数据似乎没有被加密,后面在响应里找到JSON格式的api文档,直接获取数据。

3、多数据在表内换行格式的调整,用'\n'链接多行数据,openpyxl 设置表内换行。

【逻辑整理】

1、在产品库中利用左侧品牌列表接口获取所有品牌车系名称和id值

2、解析各个车系的名称和id值,用于构建请求车系配置的url

3、通过响应页找到配置url,根据要找的车系id值构建url,从而得到配置数据

4、调整格式,导出文件

【代码实现】

根据需要安装第三方库,pip install xxx

python 复制代码
from random import random
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from datetime import time
from colorama import Fore
from openpyxl import load_workbook
from openpyxl.styles import Alignment

import re
import requests
import json
import os
import pandas as pd
import openpyxl

观察最左列车型列表

-- 步骤1

输入品牌名称,得到该品牌下的所有车系。

python 复制代码
def get_band_response(brand_id="0"):
    num = 1  # 用于统计请求次数
    while True:
        headers = {
            "user-agent": UserAgent().random  # 随机获取ua
        }
        url = "https://car.autohome.com.cn/AsLeftMenu/As_LeftListNew.ashx"
        params = {
            "typeId": "1 ",
            "brandId": brand_id,
            "fctId": "0 ",
            "seriesId": "0"
        }
        response = requests.get(url, headers=headers, params=params)
        if response.status_code == 200:
            return response
        else:
            if num >= 5:
                print("请求超过5次,退出程序")
                break
            else:
                print("请求失败,正在重新请求...")
                num += 1
                time.sleep(1)
 
 
def main():
    while True:
        band = input("请输入汽车品牌:").strip()
        response = get_band_response()
        band_pattern = f"<a href=([^>]*?)><i[^>]*?></i>{band}<em>"
        band_info = re.search(band_pattern, response.text)
        if not band_info:
            print("该品牌不存在,请重新输入")
            continue
        else:
            band_href = band_info.group(1)
            band_id = re.findall(r'/price/brand-(\d+).html', band_href)[0]
            print(F"{band} 品牌id为:", band_id)
            resp_brand = get_band_response(brand_id=band_id)
            # 上面得到了品牌页面的响应数据后,即可往下解析出该品牌下的各个车系的名称的id值
            parse_series(band, response=resp_brand)
            break
  
if __name__ == '__main__':
    main()

-- 步骤2

解析各个车系的名称和id值,用于构建请求车系配置的url

.(句点)匹配除了换行之外的所有一个字符, .*(点-星)匹配除了换行外的所有字符

"".join(list)是Python中的一个常用方法,用于将列表中的所有字符串元素连接起来,元素之间不添加任何分隔符

BeautifulSoup 是一个用于解析HTML和XML文档的Python库。它创建了一个解析树,每个节点代表文档中的一个元素、文本或指令。这使得程序可以轻松地访问和操作文档的各个部分。

CSS选择器来查找HTML文档中的元素".current > dl > dd > a" 这个选择器的意思是:选择所有在带有类名 current 的元素内部的 < dl> 元素的直接子元素 < dd> 内的 < a> 标签。

enumerate 是 Python 的一个内置函数,它用于将一个可迭代对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标。

python 复制代码
def parse_series(band, response):
    html = re.findall(r'document.writeln\("(.*)"\)', response.text)
    html = "".join(html)
    soup = BeautifulSoup(html, "html.parser")
    data_list = soup.select(".current > dl > dd > a")
    still_sell = [i for i in data_list if "停售" not in i.get_text(strip=True)]
    stop_sell = [i for i in data_list if "停售" in i.get_text(strip=True)]
    print(
        f"该品牌共找到{len(data_list)}个车型,其中,在售车型共{len(still_sell)}个,已停售车型共{len(stop_sell)}个车型(停售系列车型无配置信息)。")
    print("----------------------------------------------\n在售车型列表如下:\n----------------------------------------------")
    series_dict = {}
    for still_index, still_data in enumerate(still_sell, start=1):
        series_name = still_data.contents[0].text.strip()
        href = still_data.get("href")
        series_id = re.findall(r'/price/series-(\d+).html', href)[0]
        series_dict[series_id] = series_name
        print(f"序号:{still_index}\t车型:{series_name}\t车型id:{series_id}")
        ......

-- 步骤3

找到配置页,根据车系id值,构建配置url

python 复制代码
def get_response(series_id="0"):
    num = 1  # 用于统计请求次数
    while True:
        headers = {
            "user-agent": UserAgent().random  # 随机获取ua
        }
        url = "https://car-web-api.autohome.com.cn/car/param/getParamConf"
        params = {
            "mode": "1",
            "site": "1",
            "seriesid": series_id
        }
        response = requests.get(url, headers=headers, params=params)
        if response.status_code == 200:
            return response
        else:
            if num >= 5:
                print("请求超过5次,退出程序")
                break
            else:
                print("请求失败,正在重新请求...")
                num += 1
                time.sleep(1)
        
python 复制代码
def parse_series(band, response):
	......
    while True:
        choice = input(Fore.RED + "\n请输入需要下载的车型id,输入0则下载该品牌全部车型配置:").strip()
        if choice in series_dict.keys():
            # 以下为获取配置的逻辑函数
            # 构建配置页url
            series_name = series_dict[choice]
            series_url = "https://car.autohome.com.cn/config/series/{}.html".format(choice)
            print(Fore.CYAN + f"---正在下载{band}-{series_name},车型id为:{choice},配置链接为:{series_url}")
            # 获取当前车系的响应数据,即配置,此时的配置信息是不完整的,其中的部分数据是隐藏的,需要解密
            response = get_response(choice)
            if "抱歉" in response.text and "暂无相关数据" in response.text:
                print(Fore.RED + "该系列车暂无配置信息")
                
            # 字典格式的配置信息
            resp_dict=json.loads(response.text)
            # 获取多列配置数据
            all_info = get_car_config(resp_dict)
            df = pd.DataFrame(all_info)
            # 根据要求,提取出车系的上市年份,构建文件名
            excel_name = f"{band}_{series_name}.xlsx"
            # 保存到excel文件中
            save_to_excel(all_info, folder=band, filename=excel_name)
            
            break
        else:
            print("输入的车型id不存在,请重新输入。")
            continue
    input("请按任意键关闭程序...")

-- 步骤4

将JSON数据格式调整为列表,方面后面转换为dataframe(颜色不重要懒得处理了先)

python 复制代码
# 清洗数据
def get_car_config(config_dic):
    # 获取配置项列表
    allconfig = []
    # 初始化itemname列表
    configname_list = []
    # 遍历titlelist数组
    for title in config_dic['result']['titlelist']:
        # 遍历items数组
        for item in title['items']:
            # 提取itemname并添加到列表中
            configname_list.append(item['itemname'])

    allconfig.append(configname_list)
    
    #获取配置数据
    for data in config_dic['result']['datalist']:
        configvalue_list = []
        # 注意多个数据调整格式,颜色数据另外处理
        for valueitem in data['paramconflist']:
            value_list = []
            if valueitem.get('itemname') != '':
                configvalue_list.append(valueitem['itemname'])
            elif not valueitem.get('sublist'):
                configvalue_list.append('-')
            else:
                stri=[]
                for multivalue in valueitem['sublist']:
                    stri.append(multivalue['value'] + multivalue['name'])
                #连成一个文本串,不要列表形式防止多余'[]'
                stro='\n'.join(stri)
                configvalue_list.append(stro)
        allconfig.append(configvalue_list)
        # 颜色之后处理一下再
    return allconfig

-- 步骤5

保存数据,修改格式。

python 复制代码
def save_to_excel(data, folder, filename):
    if not os.path.exists(folder):
        os.mkdir(folder)
    df = pd.DataFrame(data)
    # df.T是将表格的行和列进行倒置的操作
    excel_path = f"{folder}/{filename}"
    df.T.to_excel(excel_path, index=False, header=False)

    # 使用openpyxl打开Excel文件,修改单元格对齐方式以启用换行
    workbook = load_workbook(excel_path)
    sheet = workbook.active
    
    # 遍历所有单元格,启用换行和垂直居中
    for row in sheet.iter_rows():
        for cell in row:
            cell.alignment = Alignment(wrap_text=True, vertical='center')
    # 设置列宽
    num_columns = df.shape[0]
    for col in range(1, num_columns + 1):
        sheet.column_dimensions[chr(64 + col)].width = 20
    
    # 保存对工作簿的更改
    workbook.save(excel_path)
    
    print(Fore.GREEN + "配置下载完成,保存到文件------> ", f"{folder}/{filename}")

【总结】

也算是好的开始,虽然懂的还是不多,但是依葫芦画瓢还是画出来了,其他的东西之后再优化,之后应该还要打包成软件给同事用,再研究研究发一篇。

------以上内容仅供学习,请勿用于违法行为,如有涉及侵权等问题,请及时与我联系。

相关推荐
深蓝电商API12 小时前
爬虫+大模型结合:让AI自动写XPath和清洗规则
人工智能·爬虫
寒山李白12 小时前
关于Python版本与supervisor版本的兼容性
windows·python·supervisord
梨落秋霜13 小时前
Python入门篇【基础语法】
开发语言·python
ada7_13 小时前
LeetCode(python)——543.二叉树的直径
数据结构·python·算法·leetcode·职场和发展
小白学大数据13 小时前
Python 多线程爬取社交媒体品牌反馈数据
开发语言·python·媒体
HAPPY酷13 小时前
压缩文件格式实战速查表 (纯文本版)
python
祝余Eleanor14 小时前
Day 31 类的定义和方法
开发语言·人工智能·python·机器学习
背心2块钱包邮14 小时前
第6节——微积分基本定理(Fundamental Theorem of Calculus,FTC)
人工智能·python·机器学习·matplotlib
larance14 小时前
修改jupyterlab 默认路径
python
老华带你飞14 小时前
汽车销售|汽车报价|基于Java汽车销售系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端·汽车