You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python爬虫爬取Cars.com时重复抓取车辆问题求助

解决Cars.com爬虫重复抓取数据的问题

问题定位

1. 反爬机制触发(核心原因)

Cars.com有反爬策略,你的代码直接用requests.get发起请求,未设置请求头(User-Agent),也没有添加请求间隔,频繁请求会被判定为机器人,网站返回重复页面内容,导致抓取到重复数据。爬取大量页面时,这种限制会更明显。

2. 代码逻辑错误

  • zip_longest调用时机错误:你在初始化空列表后立即执行exoprted = zip_longest(*all_lists),此时所有列表为空,生成的迭代器也为空。后续填充列表后,写入的还是这个空迭代器,等于无效写入,反而可能因文件追加模式导致重复内容。
  • 拼写错误:exoprted应为exported,虽不影响运行,但属于潜在问题。
  • 外层循环冗余:用for c in range(0,18000)完全没必要,应直接用while循环精准控制爬取页数,避免无效循环。

3. 缺少异常处理

车辆详情页可能存在字段缺失(比如部分二手车未标注变速箱信息),此时代码会直接报错中断,导致page_num无法递增,下次循环继续爬取同一页,造成重复数据。

解决方案

1. 添加反爬规避措施

  • 设置请求头模拟浏览器请求
  • 添加随机请求间隔,避免频繁请求
  • 可选:限制爬取速度或使用代理IP(若限制严重)

2. 修正代码逻辑

  • 调整zip_longest调用位置,确保填充完所有车辆数据后再生成迭代器
  • 替换外层循环为while循环,精准控制爬取页数
  • 修正拼写错误

3. 添加异常处理

对车辆详情页的字段查找添加try-except块,避免程序中断,同时记录异常车辆URL以便排查

优化后的完整代码

import csv
import time
import random
import requests as r
from bs4 import BeautifulSoup as b
from itertools import zip_longest

# 配置参数
car_count = 0
page_num = 1
max_page = 100
# 模拟浏览器请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'
}

# 爬取循环,直到达到最大页数
while page_num <= max_page:
    urls = []
    urls_list = []
    stocks_list = []
    names_list = []
    miles_list = []
    prices_list = []
    fueltype_list = []
    tran_list = []
    engine_list = []
    
    print(f"开始爬取第 {page_num} 页...")
    # 构造列表页URL
    list_url = f"https://www.cars.com/shopping/results/?dealer_id=&keyword=&list_price_max=&list_price_min=&makes[]=&maximum_distance=all&mileage_max=&no_accidents=true&page={page_num}&page_size=100&sort=best_match_desc&stock_type=used&year_max=&year_min=&zip="
    
    try:
        # 请求列表页,添加随机延迟
        time.sleep(random.uniform(1, 3))
        list_response = r.get(list_url, headers=headers)
        list_response.raise_for_status()  # 检查请求是否成功
        list_soup = b(list_response.content, "lxml")
        cards = list_soup.find_all("div", class_="vehicle-card")
        
        if not cards:
            print(f"第 {page_num} 页未找到车辆卡片,可能被反爬限制")
            page_num += 1
            continue
        
        # 提取所有车辆详情页URL
        for card in cards:
            url = card.find("a").get("href")
            full_url = "https://www.cars.com" + url
            urls.append(full_url)
        
        # 爬取每个车辆详情页
        for idx, car_url in enumerate(urls):
            try:
                time.sleep(random.uniform(0.5, 2))
                car_response = r.get(car_url, headers=headers)
                car_response.raise_for_status()
                car_soup = b(car_response.content, "lxml")
                
                # 提取字段,添加异常处理
                stock = car_soup.find("p", {"class": "new-used"}).text if car_soup.find("p", {"class": "new-used"}) else "未知"
                name = car_soup.find("h1", {"class": "listing-title"}).text if car_soup.find("h1", {"class": "listing-title"}) else "未知"
                
                miless = car_soup.find("dt", string="Mileage")
                milesss = miless.find_next_sibling("dd").get_text().strip(" mi.") if miless else "未知"
                
                gear = car_soup.find("dt", string="Transmission")
                tran = gear.find_next_sibling("dd").get_text() if gear else "未知"
                
                oneengine = car_soup.find("dt", string="Engine")
                twoengine = oneengine.find_next_sibling("dd").get_text() if oneengine else "未知"
                
                onefuel = car_soup.find("dt", string="Fuel type")
                twofuel = onefuel.find_next_sibling("dd").get_text() if onefuel else "未知"
                
                price = car_soup.find("span", {"class": "primary-price"}).text.strip("$") if car_soup.find("span", {"class": "primary-price"}) else "未知"
                
                # 添加到列表
                urls_list.append(car_url)
                stocks_list.append(stock)
                names_list.append(name)
                miles_list.append(milesss)
                prices_list.append(price)
                fueltype_list.append(twofuel)
                tran_list.append(tran)
                engine_list.append(twoengine)
                
                car_count += 1
                print(f"已抓取 {car_count} 辆车(第{page_num}页第{idx+1}辆)")
                
            except Exception as e:
                print(f"爬取车辆 {car_url} 出错: {str(e)}")
                continue
        
        # 生成要写入的数据(必须在填充完列表后调用)
        exported = zip_longest(stocks_list, names_list, miles_list, prices_list, fueltype_list, tran_list, engine_list, urls_list)
        
        # 写入CSV,追加模式
        with open(r"C:\Users\khale\OneDrive\سطح المكتب\car\cars33.csv", "a", newline="", encoding="utf-8") as myfile:
            writer = csv.writer(myfile)
            # 如果是第一页,写入表头
            if page_num == 1:
                writer.writerow(["车辆类型", "车辆名称", "里程", "价格", "燃料类型", "变速箱", "发动机", "详情URL"])
            writer.writerows(exported)
        
        print(f"第 {page_num} 页数据写入完成")
        page_num += 1
        
    except Exception as e:
        print(f"爬取列表页 {page_num} 出错: {str(e)}")
        # 遇到错误时等待更久,避免被封
        time.sleep(random.uniform(5, 10))
        continue

print("所有页面爬取完成")

额外注意事项

  • 如果仍出现重复数据,可尝试增加请求间隔,或使用代理IP轮换
  • 不要设置过大的max_page,Cars.com可能限制总爬取页数
  • 定期检查CSV文件,避免重复写入表头

内容的提问来源于stack exchange,提问作者KONICHUA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 17:18:08