使用 request(),返回的页面还不包含所需的数据——而是返回不完整的页面。我如何'wait'?

Using request(), returned page doesn't contain needed data yet – incomplete page is returned instead. How do I 'wait'?

我正在尝试从 carjam.co.nz 中提取年份、品牌、型号、颜色和车牌号。我正在抓取的 URL 的一个例子是 https://www.carjam.co.nz/car/?plate=JKY242.

如果最近请求了车牌,则响应将是包含车辆详细信息的 HTML 文档。

最近请求车牌详细信息的结果。

如果车牌详细信息最近没有被请求(大多数车牌都是这种情况),响应是 HTML 文档,其中包含“Trying to get some车辆数据”。我猜这个页面是在从数据库中获取信息时显示的,然后页面被重新加载以显示车辆详细信息。这似乎是在服务器端呈现的,我看不到任何 AJAX 请求。

每个结果的URL都是一样的。

最近没有请求车辆的结果。

如何'wait'获得正确的信息?

我在 Node.js 上使用 request(我知道已弃用,但这是我最习惯使用的)。

我的(非常精简)代码:

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()),
  }));
  }
}

我考虑过:

我确定这是一个很容易回答的常见问题,但我没有足够的经验来自己解决这个问题,也不知道该 Google!


待测:新西兰的数字位格式为“ABC123”——三个字母,三个数字。这些是按字母顺序发布的,目前我们没有超过 NLU999 的东西(不包括自定义车牌、乱序发布的车牌等)。

要重现“尝试获取一些车辆数据”,您需要每次都找到一个新的车牌号——序列中早于 NLU999 的大多数车牌号都应该可以使用。

此代码段应生成有效的车牌。

console.log(Math.random().toString(36).replace(/[^a-n]+/g, '').substr(0, 1).toUpperCase() + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 2).toUpperCase() + Math.floor(Math.random() * 10).toString() + Math.floor(Math.random() * 10).toString() + Math.floor(Math.random() * 10).toString());


2021 年 5 月 5 日更新

经过进一步思考,这个伪代码可能就是我所追求的——但不确定如何实际实施。

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)

我这里的难点:我习惯了request().then()的格式,直接从请求中采取行动。

假设这种方法是正确的,我将如何进行以下操作?

  1. 发送请求,然后
  2. 评估反应,然后
  3. 传递此响应,发送另一个请求然后传递该响应
  4. 处理响应

this javascript file 开始,如果未找到最大重试次数设置为 10 的数据,网站将每隔 X 秒加载一次页面。此外,从 Refresh http 检索以秒为单位的刷新值header 值。

您可以重现此流程,从而获得与前端代码完全相同的行为。

在下面的示例中,我使用了 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));
}

repl.it link: https://replit.com/@bertrandmartel/ScrapeCarJam

示例输出:

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' }

它使用 async/await 而不是 promise。

注意 request is deprecated