車両リストのスクレイピング: Cars.com、Autotrader、CarGurusでの価格情報収集
ディーラーの価格情報はCars.com、Autotrader、CarGurusにまたがって存在します。同じ車がZIPコードによって異なる価格で掲載され、どこにでもクロスポストされています。これらをスクレイピングし、VINで重複を排除し、アンチボットの壁を突破する方法をご紹介します。
ディーラーの価格情報は一箇所に集約されていません。同じ中古車がCars.com、Autotrader、CarGurusに同時に掲載され、購入者のZIPコードによって異なる価格が設定され、各サイトはアンチボット保護でページを守っています。これをうまくスクレイピングするには、ページを解析するだけでなく、3つのことが重要です: 検索をフィルタリングしてすべてのリストにアクセスできるようにすること、VINでソース間の同じ車両を重複排除すること、IPが焼かれないように壁を突破すること。このガイドでは、これら3つすべてをカバーし、どの大手リストサイトにも適応できるコードを提供します。
車両リストが提供するもの
車両リストを解析すると、年式、メーカー、モデル、トリム、価格、走行距離、VIN、ボディタイプ、駆動方式、燃料、トランスミッション、シリンダー数、ドア数、内装と外装の色、ストック番号、機能リスト、画像、ディーラーの詳細、そしてサイトによっては取引評価が得られます。これは1行ごとに完全な評価データセットです。パイプラインを構築する際の主要なフィールドは価格、走行距離、VIN、ディーラーで、その他は付加情報です。
まずフィルタリング: すべてのリストにアクセスする
リストサイトは固定ページサイズ(通常は1ページあたり20件)でページネーションされ、単一の検索がどれだけ深く行けるかに制限があります—通常は合計約1,000件の結果です。検索がそれ以上の車に一致する場合、追加のものはページネーションを通じて到達できません。解決策はセグメンテーションです: 広範な検索をメーカー、モデル、年式範囲、価格帯、またはZIPで狭めたものに分割し、それぞれが制限を超えないようにし、結果を統合します。Cars.comはこれをURLにエンコードしているため、プログラム的に検索を構築できます。
from urllib.parse import urlencode
def search_url(zip_code, make="", model="", max_price="", year_min=""):
params = {
"stock_type": "used",
"makes[]": make,
"models[]": model,
"list_price_max": max_price,
"year_min": year_min,
"maximum_distance": "all",
"zip": zip_code,
"page_size": 20,
"sort": "listed_at_desc",
}
return "https://www.cars.com/shopping/results/?" + urlencode(params)
print(search_url("10001", make="toyota", model="camry", year_min=2020))

壁を突破する: レジデンシャルプロキシ
Cars.comはCloudflareの背後にあります。他の自動車サイトはImpervaを使用し、負荷がかかるとhCaptchaやreCAPTCHAが発動します。データセンターIPがリストページを叩くとすぐにチャレンジされます。実際のISPからのレジデンシャルプロキシは普通の買い物客のように見え、回転させることでリクエストを分散させ、単一のアドレスがレート制限を超えることを防ぎます。リクエスト間に2-5秒の遅延を保つこと—ペース配分はIPと同じくらい重要です。小規模で時折の取得にはデータセンタープールで十分ですが、大規模な連続監視にはレジデンシャルがCAPTCHAで止まることなく完了するかどうかの違いです。
import requests, time, random
PROXY = "http://USER:PASS@gate.quantumproxies.io:8000"
proxies = {"http": PROXY, "https": PROXY}
HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
def fetch(url):
r = requests.get(url, proxies=proxies, headers=HEADERS, timeout=20)
r.raise_for_status()
time.sleep(random.uniform(2, 5)) # polite, and harder to fingerprint
return r.text
ターゲットがJavaScriptでリストをレンダリングしたり、持続的なCAPTCHAを投げる場合、Scraper APIが実際のブラウザフィンガープリントを持ち、アンチボット層を解決してくれるので、自分でブラウザファームを維持するよりも壊れにくいです。2026年にCloudflareを突破する方法のガイドでは、その境界がどこにあるかをカバーしています。
ソース間でVINによる重複排除
自動車データで最も有用な列はVINです。ディーラーは同じ車をすべてのマーケットプレイスにクロスポストするため、未加工のスクレイプには別々のリストのように見える重複がたくさんあります。VINは物理的な車両のグローバルに一意な識別子であるため、それをキーにすることで重複を1つのレコードにまとめることができます—そして同じ車がCars.com、Autotrader、CarGurusでどのように価格設定されているか、またはどのディーラーによるものかを比較できます。VINごとに最安値を保持するか、測定しているものに応じてソースごとにタグ付けしてすべてを保持します。
def dedupe_by_vin(rows):
best = {}
for r in rows:
vin = r.get("vin")
if not vin:
continue
price = int(r["price"])
if vin not in best or price < int(best[vin]["price"]):
best[vin] = r # keep the cheapest listing per car
return list(best.values())

地理的価格: 同じ車、異なるZIP
自動車の価格は地域によって異なります。同じモデルでも、検索するZIPによって異なる価格と在庫が表示されます。ディーラーや需要が地域によって異なるためです。この差を捉えるために、ZIPのセットをスイープし、サイトが訪問者の位置に基づいてパーソナライズする場合は、各リクエストを一致する地域の出口を通じてルーティングします。国と都市のターゲティングが可能なレジデンシャルプールを使用することで、地元の購入者が見る価格を確認できます—アービトラージ、ディーラーベンチマーク、または一つの市場に偏らない評価モデルを構築するために不可欠です。
zips = ["10001", "90001", "60601", "77001"] # NY, LA, Chicago, Houston
spread = {}
for z in zips:
url = search_url(z, make="toyota", model="camry", year_min=2021)
rows = parse_listings(fetch(url)) # exit geo-matched to the ZIP
spread[z] = [int(r["price"]) for r in rows]
# now compare median price by market
for z, prices in spread.items():
prices.sort()
print(z, "median", prices[len(prices)//2])
これは競合価格監視や不動産データ収集の背後にある同じ位置優先のパターンです—価格が購入者の位置に依存する場所では、地理的にターゲットを絞った出口が不可欠です。
オークションと卸売のソース
小売リストサイトはディーラーが求める価格を教えてくれますが、Copartのようなオークションプラットフォームは卸売レベルで実際に車が取引される価格を教えてくれます—価格モデルのもう一方の半分です。これらのサイトはより厳重に防御されています: 例えば、Copartは行動フィンガープリントを使用したImpervaウェブ保護を実行し、高いリクエストボリュームでhCaptchaやreCAPTCHAチャレンジが表示されます。ページネーションも厳しく、1ページあたり20台で約50ページのハードリミットがあり、単一の検索は約1,000ロットで上限に達します。完全な在庫に到達するには、小売サイトと同様に、メーカー、モデル、年式範囲、オークションヤード、または状態でクエリをセグメント化し、それぞれを独自に実行する必要があります。
これらのプラットフォームはJavaScriptで結果とページネーションをロードし、疑わしいトラフィックにチャレンジをかけるため、レジデンシャルIPとリクエスト間に最低2-5秒のギャップを必要とします。データセンタープロキシは小規模で時折の取得を処理しますが、持続的なものはレジデンシャル出口で行うべきです。得られるのは実際のシグナルです: 入札履歴、販売状況、ロットの状態が需要予測や輸出入分析に役立ち、小売の希望価格だけでは得られない情報を提供します。
監視、単なる一度のスクレイピングではない
一度のデータ取得は、リストが毎日更新される市場ではすぐに古くなります。持続可能なパターンは、ターゲット検索をスナップショットし、連続した実行を比較するスケジュールされたスクレイピングです—新しいリスト、価格の下落、売れた車。各行をVINでキーにして、同じ車両の価格変更が明確になるようにし、すべてのスナップショットにタイムスタンプを付けることで、特定のモデルがどれだけ早く減価償却するか、どのディーラーが価格を下げるか、在庫がどこで増えているかをトレンド化できる時系列を持つことができます。これらのスケジュールされた実行を回転するレジデンシャルIPを通じてルーティングすることで、同じアドレスからの毎日のジョブが徐々にブロックされることを防ぎます。
よくある質問
Cars.comとAutotraderをスクレイピングできますか?
はい—リストページは公開されている車両データを表示しています。実際の課題はアンチボット保護です: Cars.comはCloudflareを使用しており、他はCAPTCHA付きのImpervaを使用しています。レジデンシャルプロキシ、現実的なヘッダー、リクエスト間の2-5秒の遅延がスクレイピングを続けるのに役立ちます。各サイトの利用規約を尊重し、個人データの収集を避けてください。
車両リストをスクレイピングする際にブロックを避ける方法は?
レジデンシャルIPを回転させて、単一のアドレスが過剰なリクエストを送らないようにし、リクエストを2-5秒のランダムな遅延でペース配分し、実際のブラウザのUser-Agentを送信し、数千件のページングを避けて大規模な検索を小さなものにセグメント化します。CAPTCHAが持続する場合は、アンチボット層とJavaScriptレンダリングを処理するScraper APIに切り替えます。
スクレイピング時に重複した車が出るのはなぜですか?
ディーラーは同じ車両を複数のマーケットプレイスにクロスポストするため、1台の物理的な車が複数のリストとして表示されます。サイトに関係なく車両を一意に識別するVINで重複を排除します。VINをキーにすることで、重複をソース間で同じ車の価格比較に変えることができます。
車の価格は本当に場所によって変わりますか?
はい。在庫と価格は地域によって異なり、多くのサイトは検索者のZIPに結果をパーソナライズします。実際の広がりを捉えるために、複数のZIPを検索し、各リクエストを一致する地域の出口を通じてルーティングし、地元の価格を確認するために地理的にターゲットを絞ったレジデンシャルプロキシを使用します。
自動車リストのスクレイピングは、すべての車に到達すること(約1,000件の結果の上限を超えてセグメント化)、アンチボットの壁を突破すること(回転するレジデンシャルIP、丁寧なペース配分)、VINで重複を排除すること、位置ターゲットを絞った出口で地理的な広がりを捉えることに帰結します。価格、走行距離、VIN、ディーラーを中心にパイプラインを構築すれば、すべての主要マーケットプレイスでディーラーの価格情報を一度に得ることができます。