Tackling interesting problems is why I love web development. In this post, I will outline what the problem was, and what I did to fix it. If I succeed in this post, it will be digestible to any business owner. Although the problem was incredibly specific, my goal is to convey that our team is capable of solving your own complex problems, even if they are completely unrelated to what you read in this post.
As always, if you find value in this article, please do not hesitate to book a meeting through our website and tell us about an issue you are dealing with.
To start, I was able to generate an XML datafeed in the system. Here is the format of the datafeed.
<Unit>
<DealerId>ID Here</DealerId>
<VIN>VIN Here</VIN>
<Year>2024</Year>
<ID>ID Here</ID>
<Exterior_Color>Color Here</Exterior_Color>
<Interior_Color/>
<Mileage>10</Mileage>
<Stock_Number>Number Here</Stock_Number>
<Price>Price Here</Price>
<MSRP>MSRP Here</MSRP>
<Invoice>Invoice Here</Invoice>
<Internet_Price/>
<Is_Certified>YES</Is_Certified>
<Options>Long Option Description Here</Options>
<Type>Motorcycle</Type>
<Make>Harley-Davidson</Make>
<Model>Model Here</Model>
<Trim>Trim Here</Trim>
<Transmission>Transmission Here</Transmission>
<Condition>New</Condition>
<Body_Type/>
<Engine_Type>Engine Type Here</Engine_Type>
<Drive_Type>Drive Type Here</Drive_Type>
<Fuel_Type>Fuel Type Here</Fuel_Type>
<Description/>
<Inventory_Date>Inventory Date Here</Inventory_Date>
<MPG_Hwy/>
<MPG_City/>
<Doors>0</Doors>
<Model_Number>Model Number Here</Model_Number>
<SubModel>SubModel Here</SubModel>
<Class>On-Off Road</Class>
<Sale_Price>Sale Price Here</Sale_Price>
<Warranty>Manufacturer's w/ Extended Available</Warranty>
<InventoryUrl>Product-URL-Here.com</InventoryUrl>
<Images>
<Image order="1">url-here.jpg</Image>
</Images>
<Monthly_Payment>0</Monthly_Payment>
<Marketing_Callout>Description Here</Marketing_Callout>
<Vehicle_Tags/>
<Video_Url/>
<Instant_Price/>
<Currency_Type>USD</Currency_Type>
</Unit>
The above XML data is for one single bike, but the feed contained about 75 bikes. In order to convert the XML data into a CSV, I needed a working example. I copy/pasted the CSV from Facebook's own documentation, but I got errors. Later, I figured out that this was because I was referring to their general datafeed example, but my catalogue was specifically automotive. Automotive catalogues have different rules. I wasn't able to find a good example directly from Facebook for automotive datafeeds, so I decided to keep creating a single product feed until I got it approved. Here is what the single product CSV looked like.
vehicle_id,title,description,price,url,mileage.value,mileage.unit,address,make,model,year,image,body_style,state_of_vehicle
id,title,"Description here",1.0000 USD,https://website.com,10,MI,"{""addr1"": ""1 Hacker Way"", ""city"": ""Menlo Park"", ""region"": ""CA"",
""postal_code"": ""94025"", ""country"": ""US""}",Harley-Davidson,Pan America,2023,"[{""url"":""https://file-url-here.jpg""}]",none,New
Once I had the above template approved, it was time to get started on converting the XML datafeed to match the single-item CSV example.
import xml.etree.ElementTree as ET
import csv
import requests
import json
# The URL of the XML data feed
url = 'https://feed-here.com?api-key'
# Use requests to fetch the XML data
response = requests.get(url)
xml_data = response.text
# Parse the XML data
root = ET.fromstring(xml_data)
# Define the output file
output_file = 'facebook_catalog.csv'
# Define the headers for the modified Facebook Catalog CSV
headers = [
'vehicle_id', 'title', 'description', 'price', 'url',
'mileage.value', 'mileage.unit', 'address', 'make', 'model',
'year', 'image', 'body_style', 'state_of_vehicle'
]
# Open the CSV file for writing
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=headers)
writer.writeheader()
for unit in root.findall('.//Unit'):
model = unit.find('Model_Number').text or 'Unknown'
# Skip units with the model number redacted
if model == "Redacted":
continue
description_element = unit.find('Description')
if description_element is not None and description_element.text:
description_text = description_element.text.strip()
description = description_text[:4997] + '...' if len(description_text) > 5000 else description_text
else:
description = "Click here for more information."
vehicle_id = unit.find('ID').text or ''
title = f"{unit.find('Year').text or ''} {unit.find('Make').text or ''} {model}"
price = f"{unit.find('Price').text} USD" if unit.find('Price') is not None else '0 USD'
url = unit.find('InventoryUrl').text or ''
mileage_value = unit.find('Mileage').text or '0'
mileage_unit = "MI"
address_dict = {
"addr1": "111 Road",
"city": "City Here",
"region": "WV",
"postal_code": "11111",
"country": "US"
}
address = json.dumps(address_dict)
make = unit.find('Make').text or 'Unknown'
year = unit.find('Year').text or 'Unknown'
# Assuming the first image is enough for now
first_image_url = unit.find('.//Images/Image').text if unit.find('.//Images/Image') is not None else "No image available"
image_json = json.dumps([{"url": first_image_url}])
image = image_json
state_of_vehicle = "CPO" if (unit.find('Condition').text or '').lower() == "pre-owned" else unit.find('Condition').text or 'New'
writer.writerow({
'vehicle_id': vehicle_id,
'title': title,
'description': description,
'price': price,
'url': url,
'mileage.value': mileage_value,
'mileage.unit': mileage_unit,
'address': address,
'make': make,
'model': model,
'year': year,
'image': image,
'body_style': 'none',
'state_of_vehicle': state_of_vehicle
})
print(f"CSV file '{output_file}' generated successfully.")
And there you have it! Running that python script converted the original XML data into a CSV that could be imported into Facebook as a catalogue. It supports product exclusions by model number, has the correct fixed address, and most importantly, converts their entire catalogue into a datafeed that will help connect them to new customers! The client was extremely happy with the result.