【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}")

【总结】

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

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

相关推荐
沐霜枫叶2 小时前
解决pycharm无法识别miniconda
ide·python·pycharm
途途途途2 小时前
精选9个自动化任务的Python脚本精选
数据库·python·自动化
蓝染然2 小时前
jax踩坑指南——人类早期驯服jax实录
python
许野平2 小时前
Rust: enum 和 i32 的区别和互换
python·算法·rust·enum·i32
问道飞鱼2 小时前
【Python知识】Python进阶-什么是装饰器?
开发语言·python·装饰器
AI视觉网奇3 小时前
Detected at node ‘truediv‘ defined at (most recent call last): Node: ‘truediv‘
人工智能·python·tensorflow
GuYue.bing3 小时前
网络下载ts流媒体
开发语言·python
牛顿喜欢吃苹果3 小时前
linux创建虚拟串口
python
数据小爬虫@3 小时前
如何利用PHP爬虫获取速卖通(AliExpress)商品评论
开发语言·爬虫·php
-Mr_X-4 小时前
FFmpeg在python里推流被处理过的视频流
python·ffmpeg