Estoy tratando de extraer el año, la marca, el modelo, el color y el número de placa de carjam.co.nz. Un ejemplo de una URL de la que estoy raspando es https://www.carjam.co.nz/car/?plate=JKY242 .
Si la placa ha sido solicitada recientemente, la respuesta será un documento HTML con los detalles del vehículo.
Resultado donde los detalles de la placa han sido solicitados recientemente.
Si los detalles de la placa no se han solicitado recientemente (como es el caso con la mayoría de las placas), la respuesta es un documento HTML con "Intentando obtener algunos datos del vehículo". Supongo que esta página se muestra mientras se obtiene la información de la base de datos, luego la página se vuelve a cargar para mostrar los detalles del vehículo. Esto parece ser del lado del servidor, no puedo ver ninguna solicitud de AJAX.
La URL es la misma para cada resultado.
Resultado donde el vehículo no ha sido solicitado recientemente.
¿Cómo 'espero' la información correcta?
Estoy usando request (en desuso, lo sé, pero es lo que me siento más cómodo usando) en un servidor Node.js con Express.
Mi código (muy reducido):
app.get("/:numberPlate", (req, res) => { request("https://www.carjam.co.nz/car/?plate=" + req.params.numberPlate, function(error, response, body) { const $ = cheerio.load(body); res.status(200).send(JSON.stringify({ year: $("[data-key=year_of_manufacture]").next().html(), make: toTitleCase($("[data-key=make]").next().html()), model: toTitleCase($("[data-key=model]").next().html()), colour: toTitleCase($("[data-key=main_colour]").next().html()), })); } }He considerado:
Estoy seguro de que este es un problema común con una respuesta fácil, pero no tengo suficiente experiencia para resolverlo yo mismo, ¡o para saber exactamente qué buscar en Google!
Para probar: Nueva Zelanda tiene lugares numéricos en el formato "ABC123": tres letras, tres números. Estos se lanzan en orden alfabético, actualmente no tenemos nada más allá de NLU999 (excluyendo placas de matrícula personalizadas, placas de matrícula emitidas fuera de secuencia, etc.).
Para reproducir el "Intento de obtener algunos datos del vehículo", debe encontrar una nueva placa de matrícula cada vez; la mayoría de las placas de matrícula anteriores en la secuencia que NLU999 deberían funcionar.
Este fragmento de código debería generar una placa de matrícula válida.
console.log(Math.random().toString(36).replace(/[^an]+/g, '').substr(0, 1).toUpperCase() + Math.random().toString(36).replace(/[^az]+/g, '').substr(0, 2).toUpperCase() + Math.floor(Math.random() * 10).toString() + Math.floor(Math.random() * 10).toString() + Math.floor(Math.random() * 10).toString());Después de pensarlo más, este pseudocódigo podría ser lo que busco, pero no estoy seguro de cómo implementarlo en la práctica.
request(url) { if (url body contains "Trying to get some vehicle data") { wait(2 seconds) request(url again) { return second_result } } else { return first_result } } then process(first_result or second_result) Mi dificultad aquí: estoy acostumbrado al formato request().then() , actuando directamente desde la solicitud.
Suponiendo que este enfoque sea correcto, ¿cómo llevaría a cabo lo siguiente?
A partir de este archivo javascript , el sitio web carga la página cada X segundos si no se encuentran los datos con un reintento máximo establecido en 10. Además, el valor de actualización en segundos se recupera del valor del encabezado Refresh http.
Puede reproducir este flujo, de modo que tenga exactamente el mismo comportamiento que el código de interfaz.
En el siguiente ejemplo estoy usando axios
const axios = require("axios"); const cheerio = require("cheerio"); const rootUrl = "https://www.carjam.co.nz/car/"; const plate = "NLU975"; const maxRetry = 10; const waitingString = "Waiting for a few more things"; async function getResult() { return axios.get(rootUrl, { params: { plate: plate, }, }); } async function processRetry(result) { const refreshSeconds = parseInt(result.headers["refresh"]); var retryCount = 0; while (retryCount < maxRetry) { console.log( `retry: ${retryCount} time, waiting for ${refreshSeconds} second(s)` ); retryCount++; await timeout(refreshSeconds * 1000); result = await getResult(); if (!result.data.includes(waitingString)) { break; } } return result; } (async () => { var result = await getResult(); if (result.data.includes(waitingString)) { result = await processRetry(result); } const $ = cheerio.load(result.data); console.log({ year: $("[data-key=year_of_manufacture]").next().html(), make: $("[data-key=make]").next().html(), model: $("[data-key=model]").next().html(), colour: $("[data-key=main_colour]").next().html(), }); })(); function timeout(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }enlace repl.it: https://replit.com/@bertrandmartel/ScrapeCarJam
Salida de muestra:
retry: 0 time, waiting for 1 second(s) retry: 1 time, waiting for 1 second(s) retry: 2 time, waiting for 1 second(s) { year: 'XXXX', make: 'XXXXXX', model: 'XX', colour: 'XXXX' }Utiliza async/await en lugar de promesa.
Tenga en cuenta que la solicitud está en desuso