Scrape vehicle listings from Mobile.de, Germany's largest car marketplace with 1.4M+ listings. Extract price, brand, model, year, mileage, fuel type, dealer info, specs, and photos. Filter by any parameter. Essential for automotive market intelligence in the German market.
Changelog — Mobile.de Scraper — Germany Car Marketplace
Unreleased — convención tiempo_uso
tiempo_uso (ftp28, live y preloaded) = horas "N h"; si no hay, km "N km" (formato intacto). Live alineado con preloaded (fallback a km; "0 h" no oculta km). Nuevas ext_tiempo_uso_unidad (h|km|null) y ext_tiempo_uso_calidad (cero|horas_igual_km|km_posible_artefacto|unidad_sin_confirmar). Regla artefacto: km 1-999 en ConstructionMachine/AgriculturalVehicle/ForkliftTruck (maquinaria/agricola/elevacion). Idempotente sobre snapshots ya publicados. English: hours sin cambios, solo ext_*. Declaradas en dataset_schema.
2026-10 — checkpoint en la ruta HTTP
Dockerfile: navegador Camoufox fijado a 152.0.4-beta.30 (MAX_VERSION='beta.31' antes de fetch + assert en el build). Sin el pin, un rebuild baja 156.0.1-beta.33 y rompe (Unknown property navigator.appCodeName).
Checkpoint por mes/categoria/hoja (KV mobile-de-checkpoints, claves CHECKPOINT_{mes}_{unidad}): una hoja done o una categoria done se salta sin warmup ni total_de; una hoja a medias reanuda en la tanda siguiente a la ultima completa. El registro se escribe siempre despues de volcar el dataset.
vistos_por_marca se persiste entre runs; la cobertura y el corte del 99,5 % del segundo eje cuentan lo que ya habia en el dataset.
Resumen [ckpt] por categoria y global (hojas incompletas) y CKPT_SUMMARY en el KV del run. Una hoja cortada ya no se da por buena: la categoria queda sin done y el siguiente run la completa.
Puntero: itemCount = len(seen_ids) (antes _total + len(seen_ids) contaba dos veces los items nuevos).
forceRestart borra los checkpoints del mes (o del dataset de prueba). _snapshot_dataset_name solo acepta nombres zz-*.
2026-09-18 — build FTP-28: M1
T6 (push 2026-09-18): input_schema.json prefill/default outputSchema -> "ftp28" para modo live (fallback interno en main.py sigue en "english", pendiente de autorizacion futura).
0.4 — 2026-08-25 (tag beta)
Ruta de API (_snapshot_method: "http") operativa y particionada. El defecto sigue en
browser_v2 hasta cerrar los cuatro puntos abiertos.
Añadido
Cabeceras x-mobile-* en el fetch a /api/s/. Sin ellas el endpoint sirve una versión
antigua y desde el 24/08/2026 responde 400.
Partición por dos ejes: año de construcción año a año, y marca para los que no publican año.
Cobertura del 16,6% al 97,2% en ConstructionMachine.
_FILTROS_VALIDOS — lista blanca de los nueve filtros que la API respeta. Lo que no esté se
descarta con aviso.
_snapshot_dataset_name — dirige un run de prueba a un dataset desechable y no publica el
puntero.
_probar_filtros y _analizar_filtros — diagnóstico de bandas y de filtros soportados.
Candado en la escritura al dataset. Elimina el 25% de duplicados que producía la carrera
entre corrutinas.
Corregido
dom_item_to_ftp28 no emitía ningún campo canónico FTP-28.
marca llegaba con el distintivo pegado ("NUEVOKubota"), corrompida en el 42,9%.
estado invertido para todo anuncio nuevo: buscaba "neu" sobre "NUEVO".
ubicacion normalizada al formato del cliente.
Execution context was destroyed: se elimina la navegación previa al fetch.
proxy_pool = ["apify_residential"] * 10 se usaba literalmente como URL de proxy.
numResultsFound → numResultsTotal, con raise si no aparece.
Sin _dataset_id el run aborta en vez de barrer hacia un sumidero.
El fax deja de mapearse como teléfono.
vendedor_id emite None, no "".
Pendiente
323 anuncios (2,8%) que ni el año ni la marca alcanzan.
Verificar sobre una categoría grande (VanUpTo7500).
precio_neto al 85,7% frente al 100% del DOM.
km: la maquinaria no trae ml; la anomalía de agosto sigue sin explicarse.
Formato: que cambio, por que, y la medicion que lo respalda. Un cambio sin numero detras es
una opinion; en este repo no cuenta como entrada de changelog.
Las versiones son las de Apify (mayor.menor). La plataforma no admite numero de parche.
[0.3] — 21 de agosto de 2026
Anadido — resolucion del ID canonico (src/id_utils.py)
mobile.de usa dos sistemas de identificador para el mismo anuncio: uno publico de 9 digitos, visible
en la URL canonica (/lkw-inserat/slug/323829529.html), y uno interno de 14 que aparece en el
parametro ?id= y en el endpoint /api/s/. El esquema FTP-28 espera el publico.
Las tres rutas de extraccion del actor producian formatos distintos:
Ruta
Fichero
ID que obtiene
API REST /api/s/
snapshot_http.py
14 digitos, interno
DOM del navegador
main.py + browser_transport.py
14 digitos, interno
SSR HTML
consumer_client.py
9 digitos, canonico
extract_canonical_id(ad_id, url, canonical_url) recupera el ID publico a partir de la URL
canonica. Si el ID ya tiene 10 digitos o menos, lo devuelve intacto. La URL de navegacion sigue
usando el ID interno a proposito: el servidor de mobile.de lo necesita para resolver la ficha.
Cobertura real, medida despues de desplegar. El fix funciona solo cuando el registro trae una
URL canonica. En un run en vivo posterior al despliegue, 11 de 20 registros seguian saliendo con
14 digitos, porque la tarjeta del listado solo expone el enlace ?id= y no hay ninguna URL
canonica que leer. Queda como defecto abierto, no como resuelto.
El dato de agosto no se puede reparar desde el dataset. De los 15.995 registros afectados
(14,6% de 109.362), el 100% guarda unicamente la URL ?id=. Recuperar su ID publico exige
volver a pedir cada ficha, con DataDome de por medio.
Pendiente
Comprobar si fetchDetails: true resuelve el canonico en la ruta de listado. Si lo hace, el
snapshot mensual puede activarlo y el defecto se cierra sin tocar codigo.
ext_debug_leaf_texts es un campo de depuracion y se esta entregando al cliente en el output.
El output duplica campos canonicos con otro nombre: precio junto a valor, imagen_url junto
a imagenes. Dos nombres para el mismo dato acaban divergiendo.
El puntero del registro declara datasetId: 8ps3gu3yLBcVyGGud, pero el nombre resuelve a
CAsBbtPNulwSo4COg. El datasetId esta obsoleto.