使用 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()),
}));
}
}
我考虑过:
- 发出请求并放弃它,休眠 2 - 3 秒,然后发出第二个请求。这种方法的优点是每个请求都会起作用。缺点是每个请求需要 2 - 3 秒(太慢)。
- 发出请求并检查正文是否包含“尝试获取一些车辆数据”。如果是这样,请稍等片刻,发出另一个请求并根据第二个请求的结果采取行动(但是如何?)。
我确定这是一个很容易回答的常见问题,但我没有足够的经验来自己解决这个问题,也不知道该 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()
的格式,直接从请求中采取行动。
假设这种方法是正确的,我将如何进行以下操作?
- 发送请求,然后
- 评估反应,然后
- 传递此响应,或发送另一个请求然后传递该响应
- 处理响应
从 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。
我正在尝试从 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()),
}));
}
}
我考虑过:
- 发出请求并放弃它,休眠 2 - 3 秒,然后发出第二个请求。这种方法的优点是每个请求都会起作用。缺点是每个请求需要 2 - 3 秒(太慢)。
- 发出请求并检查正文是否包含“尝试获取一些车辆数据”。如果是这样,请稍等片刻,发出另一个请求并根据第二个请求的结果采取行动(但是如何?)。
我确定这是一个很容易回答的常见问题,但我没有足够的经验来自己解决这个问题,也不知道该 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()
的格式,直接从请求中采取行动。
假设这种方法是正确的,我将如何进行以下操作?
- 发送请求,然后
- 评估反应,然后
- 传递此响应,或发送另一个请求然后传递该响应
- 处理响应
从 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。