【问题标题】:Using request(), returned page doesn't contain needed data yet – incomplete page is returned instead. How do I 'wait'?使用 request(),返回的页面还不包含所需的数据——而是返回不完整的页面。我该如何“等待”?
【发布时间】:2021-04-29 22:51:25
【问题描述】:

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

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

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

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

每个结果的 URL 都是相同的。

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

我如何“等待”正确的信息?

我在带有 Express 服务器的 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()),
  }));
  }
}

我考虑过:

  • 发出请求并将其丢弃,休眠 2 - 3 秒,然后发出第二个请求。这种方法的优点是每个请求都可以工作。缺点是每个请求都需要 2 - 3 秒(太慢)。
  • 发出请求并检查正文是否包含“尝试获取某些车辆数据”。如果是这样,请休眠几秒钟,发出另一个请求并对第二个请求的结果采取行动(但如何?)。

我确信这是一个很容易回答的常见问题,但我没有足够的经验来自己弄清楚,或者确切地知道谷歌应该做什么!


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

要重现“尝试获取一些车辆数据”,您需要每次都找到一个新的车牌 - 序列中比 NLU999 更早的大多数车牌应该可以工作。

此代码 sn-p 应该会生成一个有效的车牌号。

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. 处理响应

【问题讨论】:

  • 很高兴看到一个经过深思熟虑的综合问题 +

标签: node.js express web-scraping request


【解决方案1】:

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

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

在以下示例中,我使用的是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 链接: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

【讨论】:

  • 优秀的答案,易于集成到现有代码中——谢谢!
猜你喜欢
  • 2016-03-26
  • 2011-08-03
  • 1970-01-01
  • 1970-01-01
  • 2021-06-02
  • 2012-06-02
  • 1970-01-01
  • 2018-08-27
  • 1970-01-01
相关资源
最近更新 更多