Make Model What?
If you like me were tasked with loading a database of recent car makes/models/years, you would start by looking on the web and seeing if someone else just has it out there, readily available, hopefully for free, but perhaps for a tiny nominal fee.?
If only it was that simple...
I looked and looked, and couldn't find anything that would fit the above requirements. So I thought, who would know about US car models better than Kelly Blue Book? So I went on their site, and sure enough they have a javascript file that lists all known to them makes and models of used cars. Since the file is public, I figured it's not really "evil" if I scrape and parse it for my own benefit. Disagree? Have a better source? Then leave a comment.
Anyway, to cut the long story short, I'm hoping to save a day or so to someone else who may, like me, be looking for this information. The ruby module shown below retrieves and parses the javascript from KBB site into a Ruby data structure of the following form - basically a hash, keyed on make, then on model with list of years as a value:
>> Constants::Auto::DB.keys.sort[0..5] => ["AMC", "Acura", "Alfa Romeo", "Audi", "BMW", "Bertone"] >> Constants::Auto::DB["Subaru"].keys.sort[0..5] => ["B9 Tribeca", "Baja", "DL", "Forester", "GL", "GL-10"] >> Constants::Auto::DB["Audi"]["A4"] => ["1999", "2007", "1998", "2006", "2005", "1996", "2004", "2003", "2002", "1997", "2001", "2000"] >> Constants::Auto::DB["BMW"]["X5"] => ["2003", "2002", "2001", "2000", "2005", "2007", "2006", "2004"]
The idea is that you could load the initial hash:
@models = KBB::Parser.new.to_hash
and then save the output of @models.inspect in your local constants file - hence me using Constants::Auto::DB (I actually have a Rake task for doing this -- let me know if I should post it too). Then you would just re-run this every time you think new car models are added/changed on KBB. Realize, that hitting their site every time you need the data is clearly evil. So use this class to load the data initially, save the result of inspect() call into a ruby file, and use that cached version in your app. Re-run the load every time you want to update your database.
Please let me know if you find this code useful, or if you find a better/cleaner/more comprehensive way of maintaining car make/model/year database.
# # author: Konstantin Gredeskoul © 2008 # license: public domain # require 'net/http' require 'uri' module KBB MODELS_URL = "http://scripts.kbb.com/kbb/ymmData.axd?VehicleClass=UsedCar" class Models def initialize(js) @models = {} @makes = {} n = /ymUsed_\[\d{4}\]\s*=\s*'([^']+)'/ m = /ymmUsed_\["(\d+)~(\d+)"\]\s*=\s*"([^"]+)"/ js.split(/\n/).each do |line| next if line.strip.blank? if matched = n.match(line) matched[1].split(/,/).each do |token| id, name = token.split('|') @makes[id.to_i] = name end end if matched = m.match(line) year, make_id, models = matched[1], matched[2], matched[3] models.split(/,/).each do |t| id, model_name = t.split('|') make_name = @makes[make_id.to_i] @models[make_name] ||= {} @models[make_name][model_name] ||= [] @models[make_name][model_name] << year end end end end def to_hash @models end end class Parser def initialize @m = Models.new(Net::HTTP.get(URI.parse(MODELS_URL))) end def to_hash @m.to_hash end end end
Comments
thank you for this - what a great idea
i was wondering what language is that in?
i was given the task to compile this database in one week, but spent a lot of time looking for a list, but no dice...
so if i can make this work, then you saved my day
thanks
kristine
thought i'd ask... do you have a php version of it by chance?
either way, this is super helpful!
thanks again ya
kristine
here is a basic PHP array:
$file = file('./ymmData.axd');
$patternMake = '/ymUsed_\[\d{4}\]\s*=\s*\'([^\']+)\'/';
$patternModel = '/ymmUsed_\["(\d+)~(\d+)"\]\s*=\s*"([^"]+)"/';
foreach($file as $row) {
if(preg_match($patternMake,$row,$matched))
{
$tmpMakes = explode(',',$matched[1]);
foreach($tmpMakes as $str) {
list($id,$name) = explode("|",$str);
$arrMakes[$id] = $name;
}
}
unset($str);
if(preg_match($patternModel,$row,$matched))
{
$year = $matched[1];
$make_id = $matched[2];
$models = $matched[3];
$tmpModels = explode(',',$models);
foreach($tmpModels as $str) {
list($id,$model_name) = explode("|",$str);
$make_name = $arrMakes[$make_id];
$arrModels[$make_name][$model_name][$year] = $year;
}
}
}
ksort($arrModels);
echo
For other people who are making ASP.NET websites or don't want to parse a .axd file, it looks like the Selection Service is directly queryable and addable from here
Seekda has a better description, that allows you to see the results of an HTTP-POST
here
That being said, I still need to figure out how to actually use these properly (Web Service n00b here), but it's good to know they're out there if you don't want to do brute force parsing on the javascript!
What an awesome thing! Thanks!
Could someone "comment" the code to explain what each line is doing?
Thanks!
Contact: jojobinkus@hotmail.com
Thanks
PS - the main URL for this file has changed to: http://scripts.kbb.com/kbb/ymmData.axd?VehicleClass=UsedCar
Anyone know where kbb keeps its pricing data?
Awesome work.
http://scripts.kbb.com/kbb/ymmData.axd?VehicleClass=UsedCar
I loaded this into a MySQL DB:
require "rubygems"
require "activerecord"
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "localhost",
:username => "username",
:password => "password",
:socket => "/var/run/mysqld/mysqld.sock",
:database => "vehicleDev")
class Make < ActiveRecord::Base
end
class Model < ActiveRecord::Base
end
class Year < ActiveRecord::Base
end
class PopulateCarsDB
def initialize
@cars = Parser.new
end
#To load data into car Makes table
def loadMakes
@cars.to_hash.keys.each do |makestmp|
Make.create(:make => makestmp)
end
end
def loadModels
all_makes = Make.find(:all)
all_makes.each do |make|
@cars.to_hash[make.make].keys.each do |modelKey|
model = Model.create(:make_id => make.id, :model => modelKey)
load_model_years(model.id, @cars.to_hash[make.make][modelKey])
end#end for models keys loop
end#end for makes loop
end
def load_model_years(model_id, model_yrs_array)
#puts model_id.to_s + " | " + model_yrs_array.inspect
model_yrs_array.each do |year|
Year.create(:model_id => model_id, :year => year)
end#end for model's year loop
end
end
cars = PopulateCarsDB.new
cars.loadMakes
cars.loadModels
$file = file_get_contents('http://scripts.kbb.com/kbb/ymmData.axd?VehicleClass=UsedCar');
$file = explode("\n",$file);
$patternMake = '/ymUsed_\[\d{4}\]\s*=\s*\'([^\']+)\'/';
$patternModel = '/ymmUsed_\["(\d+)~(\d+)"\]\s*=\s*"([^"]+)"/';
foreach($file as $row) {
if(preg_match($patternMake,$row,$matched)){
$tmpMakes = explode(',',$matched[1]);
foreach($tmpMakes as $str) {
list($id,$name) = explode("|",$str);
$arrMakes[$id] = $name;
}
}
unset($str);
if(preg_match($patternModel,$row,$matched)){
$year = $matched[1];
$make_id = $matched[2];
$models = $matched[3];
$tmpModels = explode(',',$models);
foreach($tmpModels as $str) {
list($id,$model_name) = explode("|",$str);
$make_name = $arrMakes[$make_id];
$arrModels[$make_name][$model_name][$year] = $year;
}
}
}
ksort($arrModels);
print_r($arrModels);
GUTO
contact me at admin(@)karqin(dot)com
A little modification for the PHP version to sort the results alphabetically:
function getCarsDB()
{
$file = file_get_contents('http://scripts.kbb.com/kbb/ymmData.axd?VehicleClass=UsedCar');
$file = explode("\n",$file);
$patternMake = '/ymUsed_\[\d{4}\]\s*=\s*\'([^\']+)\'/';
$patternModel = '/ymmUsed_\["(\d+)~(\d+)"\]\s*=\s*"([^"]+)"/';
foreach($file as $row) {
if(preg_match($patternMake,$row,$matched)){
$tmpMakes = explode(',',$matched[1]);
foreach($tmpMakes as $str) {
list($id,$name) = explode("|",$str);
$arrMakes[$id] = $name;
}
}
unset($str);
if(preg_match($patternModel,$row,$matched)){
$year = $matched[1];
$make_id = $matched[2];
$models = $matched[3];
$tmpModels = explode(',',$models);
foreach($tmpModels as $str) {
list($id,$model_name) = explode("|",$str);
$make_name = $arrMakes[$make_id];
$arrModels[$make_name][$model_name][$year] = $year;
}
}
}
ksort($arrModels);
foreach ($arrModels as &$make)
{
ksort($make);
foreach ($make as &$model)
ksort($model);
}
return $arrModels;
}
I wonder if anyone has the model trim information too? (3 series - 328, 330, 335, etc)
error_reporting(E_ALL);
$cars = getCarsDB();
$mk = 1;
$mdl = 2;
$yr = 3;
$i = 1;
foreach ($cars as $make => $value)
{
if($make == 'BMW' || $make == 'Mercedes-Benz' || $make == 'Audi' || $make == 'Porsche' || $make == 'Volkswagen')
{
//echo "Make: $make
\n";
$mk = $i;
echo "INSERT INTO drupal_term_data (vid,name,description) VALUES ('2','$make', '$make');
";
echo "INSERT INTO drupal_term_hierarchy (tid,parent) VALUES ('" . $i++ . "','0');
";
//$mk++;
}
else
{
continue;
}
foreach ($value as $model => $value1)
{
//echo "-------Model: $model
\n";
$mdl = $i;
echo "INSERT INTO drupal_term_data (vid,name,description) VALUES ('2','$model', '$model');
";
echo "INSERT INTO drupal_term_hierarchy (tid,parent) VALUES ('" . $i++ . "','" . $mk . "');
";
foreach ($value1 as $year)
{
//echo "----------->>>>>Year: $year
\n";
echo "INSERT INTO drupal_term_data (vid,name,description) VALUES ('2','$year', '$year');
";
echo "INSERT INTO drupal_term_hierarchy (tid,parent) VALUES ('" . $i++ . "','" . $mdl . "');
";
}
$mdl++;
}
$mk++;
}
function getCarsDB()
{
$file = file_get_contents('http://scripts.kbb.com/kbb/ymmData.axd?VehicleClass=UsedCar');
$file = explode("\n",$file);
$patternMake = '/ymUsed_\[\d{4}\]\s*=\s*\'([^\']+)\'/';
$patternModel = '/ymmUsed_\["(\d+)~(\d+)"\]\s*=\s*"([^"]+)"/';
foreach($file as $row)
{
if(preg_match($patternMake,$row,$matched))
{
$tmpMakes = explode(',',$matched[1]);
foreach($tmpMakes as $str)
{
list($id,$name) = explode("|",$str);
$arrMakes[$id] = $name;
}
}
unset($str);
if(preg_match($patternModel,$row,$matched))
{
$year = $matched[1];
$make_id = $matched[2];
$models = $matched[3];
$tmpModels = explode(',',$models);
foreach($tmpModels as $str)
{
list($id,$model_name) = explode("|",$str);
$make_name = $arrMakes[$make_id];
$arrModels[$make_name][$model_name][$year] = $year;
}
}
}
ksort($arrModels);
foreach ($arrModels as &$make)
{
ksort($make);
foreach ($make as &$model)
ksort($model);
}
return $arrModels;
}
Does anyone know where to find a file for new (2009) car makes and models?
thanks
http://scripts.kbb.com/kbb/ymmData.axd?VehicleClass=NewCar
btw this is great info.
thanks
Sincerely.
You can contact me on my email id logintomontu@gmail.com . I can also sens you some samples, so you can decide.
http://www.webuyanycar.com/Secure/MakeAndModel.aspx
php would be ideal
Thanks,
eddie@blunt1.com
Year/Make/Model/Trim of vehicles from 1960 and up.
Can anyone help me out with this please?
Thanks!
thank you very very much
John
Does anyone have a file that has a table for make, model, and year for cars?
or does anyone know how the original poster accessed that javascript file?
sorry if the question is amateur in nature, am very new to this type of stuff =D
Thanks.
Also, I have an excel database for all Year, Make, Model, if anyone is interested. I am not a programmer, so can trade the database for the code to get the dropdowns on our website.
my e-mail is csargisov@gmail.com
thanks
mike
Years: 1904-2012
Makes
Models
Trims (for those years that had them)
I have this data which was updated yesterday and is ready in MySQL format and CSV format.
I will also include a complete get your started PHP/Ajax script for your site as can be seen here in the demo:
http://www.carlistdb.com/demo/
Please email me at: dev[at]idealws.net if your interested in purchasing this for your site.
Regards,
Ray
It's still pretty early on in development, but maybe it can help some people here. If you have any ideas about how it could be more useful, please contact me through the form on my site.
http://www.carqueryapi.com/
http://www.kbb.com/jsdata/2.1.37.1_40678/_makes?vehicleclass=UsedCar
Models:
http://www.kbb.com/jsdata/2.1.37.1_40678/_modelsyears?vehicleclass=UsedCar
Obviously this is KBB's data and I'm sure they try their hardest to keep it under wraps so no telling how long it will reside there. You probably shouldn't use this data if you're building a competing application with KBB services. Doubt they'd know but you shouldn't feel good about it any.