脚本在执行过程中卡在某处
Script gets stuck somewhere in it's execution process
我创建了一个脚本,使用 request
和 cheerio
库从这个 webpage and then use those urls to parse the links of different offices from here. Finally, use those office links to scrape title from here.
中抓取不同省份的链接
当我 运行 脚本时,我可以看到它会相应地执行它的工作,直到它在执行过程中卡在某个地方。当它卡住时,它不会抛出任何错误。
以下是图像中的步骤:
这是我试过的:
const request = require('request');
const cheerio = require('cheerio');
const link = 'https://www.egyptcodebase.com/en/p/all';
const base_link = 'https://www.egyptcodebase.com/en/';
let getLinks = (link) => {
const items = [];
return new Promise((resolve, reject) => {
request(link, function(error, response, html) {
let $ = cheerio.load(html);
if (error) return reject(error);
try {
$('.table tbody tr').each(function() {
items.push(base_link + $(this).find("a[href]").attr("href"));
});
resolve(items);
} catch (e) {
reject(e);
}
});
});
};
let getData = (links) => {
const nitems = [];
const promises = links
.map(nurl => new Promise((resolve, reject) => {
request(nurl, function(error, response, html) {
let $ = cheerio.load(html);
if (error) return reject(error);
try {
$('.table tbody tr').each(function() {
nitems.push(base_link + $(this).find("a[href]").attr("href"));
});
resolve(nitems);
} catch (e) {
reject(e);
}
})
}))
return Promise.all(promises)
}
let FetchData = (links) => {
const promises = links
.map(turl => new Promise((resolve, reject) => {
request(turl, function(error, response, html) {
if (error) return reject(error);
let $ = cheerio.load(html);
try {
const title = $(".home-title > h2").eq(0).text();
console.log({
title: title,
itemLink: turl
});
resolve(title);
} catch (e) {
reject(e);
}
})
}))
return Promise.all(promises)
}
(async function main() {
const result = await getLinks(link);
const resultSecond = await getData(result);
const merged = resultSecond.flat(1);
const resultFinal = await FetchData(merged);
for (const title of resultFinal) {
console.log(title);
}
})().catch(console.error);
如何让脚本完成它的执行过程?
PS虽然脚本看起来很大,但是除了选择器之外,里面使用的函数几乎完全相同。
好的,在测试这段代码时,我 运行 立即解决了两个问题:
resultSecond,包含来自 getData() 的数据,返回类数组对象,而不是数组,所以我无法使用 flat()。所以我创建了一个函数 toArray 将对象转换为数组并在 resultSecond 之后添加了另一个变量 resultThird 和在 resultSecond 上使用此函数,将其转换为数组。
flat() Array 原型中不存在,所以我不得不手动添加它。
解决这些问题后,我能够 运行 您的代码,并遇到了您所说的挂起。
发生 ECONNRESET 错误,然后在挂起之前继续发出可能的几千个请求。 ECONNRESET 通常是由于未处理异步网络错误 或 您请求的服务器决定终止连接。不确定请求模块将如何处理此类事件,但似乎该模块可能无法正确处理网络错误或终止连接。
问题是您向该站点发出了 15,000 个请求 API,因此 API 可能有一个速率限制器,看到了请求的数量并终止了其中的大部分请求,但允许了几个千通过,但由于您没有处理终止的连接——很可能是由于请求模块吞噬了这些错误——它“挂”在那里,节点进程没有退出。
因此,我使用异步模块将请求按 300 个间隔分批处理,效果非常好。没有终止连接,因为我没有达到速率限制。您可能会将间隔限制提高到 300 以上。
但是,我建议不要使用请求模块并使用另一个 http 模块,如 axios,它很可能会处理这些问题。当您执行大量异步请求时,您应该考虑使用异步。它有很多有用的方法。 Lmk 如果你需要更多关于异步模块在这里做什么的解释,但我建议先阅读文档:https://caolan.github.io/async/v3/docs.html#mapLimit
const request = require('request');
const cheerio = require('cheerio');
const _async = require('async');
const link = 'https://www.egyptcodebase.com/en/p/all';
const base_link = 'https://www.egyptcodebase.com/en/';
const toArray = (obj) => {
const arr = [];
for (const prop in obj) {
arr.push(obj[prop])
}
return arr;
}
Object.defineProperty(Array.prototype, 'flat', {
value: function(depth = 1) {
return this.reduce(function (flat, toFlatten) {
return flat.concat((Array.isArray(toFlatten) && (depth>1)) ? toFlatten.flat(depth-1) : toFlatten);
}, []);
}
});
let getLinks = (link) => {
const items = [];
return new Promise((resolve, reject) => {
request(link, function(error, response, html) {
let $ = cheerio.load(html);
if (error) return reject(error);
try {
$('.table tbody tr').each(function() {
items.push(base_link + $(this).find("a[href]").attr("href"));
});
resolve(items);
} catch (e) {
reject(e);
}
});
});
};
let getData = (links) => {
const nitems = [];
const promises = links
.map(nurl => new Promise((resolve, reject) => {
request(nurl, function(error, response, html) {
let $ = cheerio.load(html);
if (error) return reject(error);
try {
$('.table tbody tr').each(function() {
nitems.push(base_link + $(this).find("a[href]").attr("href"));
});
return resolve(nitems);
} catch (e) {
return reject(e);
}
})
}))
return Promise.all(promises)
}
let FetchData = (links) => {
const limit = 300;
return new Promise((resolve, reject) => {
const itr = (col, cb) => {
request(col, function(error, response, html) {
if (error) cb(error)
let $ = cheerio.load(html);
try {
const title = $(".home-title > h2").eq(0).text();
console.log({
title: title,
itemLink: col
});
cb(null, title);
} catch (e) {
cb(e);
}
})
}
_async.mapLimit(links, limit, itr, function(err, results) {
if (err) reject(err);
return resolve(results);
})
})
}
(async function main() {
const result = await getLinks(link);
const resultSecond = await getData(result);
const resultThird = toArray(resultSecond);
const merged = resultThird.flat(1);
const resultFinal = await FetchData(merged);
for (const title of resultFinal) {
console.log("title: ", title);
}
})().catch(err => console.log(err))
//good to listen to these
process.on('uncaughtException', err => { console.log(err) });
process.on('unhandledRejection', err => { console.log(err) });
我创建了一个脚本,使用 request
和 cheerio
库从这个 webpage and then use those urls to parse the links of different offices from here. Finally, use those office links to scrape title from here.
当我 运行 脚本时,我可以看到它会相应地执行它的工作,直到它在执行过程中卡在某个地方。当它卡住时,它不会抛出任何错误。
以下是图像中的步骤:
这是我试过的:
const request = require('request');
const cheerio = require('cheerio');
const link = 'https://www.egyptcodebase.com/en/p/all';
const base_link = 'https://www.egyptcodebase.com/en/';
let getLinks = (link) => {
const items = [];
return new Promise((resolve, reject) => {
request(link, function(error, response, html) {
let $ = cheerio.load(html);
if (error) return reject(error);
try {
$('.table tbody tr').each(function() {
items.push(base_link + $(this).find("a[href]").attr("href"));
});
resolve(items);
} catch (e) {
reject(e);
}
});
});
};
let getData = (links) => {
const nitems = [];
const promises = links
.map(nurl => new Promise((resolve, reject) => {
request(nurl, function(error, response, html) {
let $ = cheerio.load(html);
if (error) return reject(error);
try {
$('.table tbody tr').each(function() {
nitems.push(base_link + $(this).find("a[href]").attr("href"));
});
resolve(nitems);
} catch (e) {
reject(e);
}
})
}))
return Promise.all(promises)
}
let FetchData = (links) => {
const promises = links
.map(turl => new Promise((resolve, reject) => {
request(turl, function(error, response, html) {
if (error) return reject(error);
let $ = cheerio.load(html);
try {
const title = $(".home-title > h2").eq(0).text();
console.log({
title: title,
itemLink: turl
});
resolve(title);
} catch (e) {
reject(e);
}
})
}))
return Promise.all(promises)
}
(async function main() {
const result = await getLinks(link);
const resultSecond = await getData(result);
const merged = resultSecond.flat(1);
const resultFinal = await FetchData(merged);
for (const title of resultFinal) {
console.log(title);
}
})().catch(console.error);
如何让脚本完成它的执行过程?
PS虽然脚本看起来很大,但是除了选择器之外,里面使用的函数几乎完全相同。
好的,在测试这段代码时,我 运行 立即解决了两个问题:
resultSecond,包含来自 getData() 的数据,返回类数组对象,而不是数组,所以我无法使用 flat()。所以我创建了一个函数 toArray 将对象转换为数组并在 resultSecond 之后添加了另一个变量 resultThird 和在 resultSecond 上使用此函数,将其转换为数组。
flat() Array 原型中不存在,所以我不得不手动添加它。
解决这些问题后,我能够 运行 您的代码,并遇到了您所说的挂起。
发生 ECONNRESET 错误,然后在挂起之前继续发出可能的几千个请求。 ECONNRESET 通常是由于未处理异步网络错误 或 您请求的服务器决定终止连接。不确定请求模块将如何处理此类事件,但似乎该模块可能无法正确处理网络错误或终止连接。
问题是您向该站点发出了 15,000 个请求 API,因此 API 可能有一个速率限制器,看到了请求的数量并终止了其中的大部分请求,但允许了几个千通过,但由于您没有处理终止的连接——很可能是由于请求模块吞噬了这些错误——它“挂”在那里,节点进程没有退出。
因此,我使用异步模块将请求按 300 个间隔分批处理,效果非常好。没有终止连接,因为我没有达到速率限制。您可能会将间隔限制提高到 300 以上。
但是,我建议不要使用请求模块并使用另一个 http 模块,如 axios,它很可能会处理这些问题。当您执行大量异步请求时,您应该考虑使用异步。它有很多有用的方法。 Lmk 如果你需要更多关于异步模块在这里做什么的解释,但我建议先阅读文档:https://caolan.github.io/async/v3/docs.html#mapLimit
const request = require('request');
const cheerio = require('cheerio');
const _async = require('async');
const link = 'https://www.egyptcodebase.com/en/p/all';
const base_link = 'https://www.egyptcodebase.com/en/';
const toArray = (obj) => {
const arr = [];
for (const prop in obj) {
arr.push(obj[prop])
}
return arr;
}
Object.defineProperty(Array.prototype, 'flat', {
value: function(depth = 1) {
return this.reduce(function (flat, toFlatten) {
return flat.concat((Array.isArray(toFlatten) && (depth>1)) ? toFlatten.flat(depth-1) : toFlatten);
}, []);
}
});
let getLinks = (link) => {
const items = [];
return new Promise((resolve, reject) => {
request(link, function(error, response, html) {
let $ = cheerio.load(html);
if (error) return reject(error);
try {
$('.table tbody tr').each(function() {
items.push(base_link + $(this).find("a[href]").attr("href"));
});
resolve(items);
} catch (e) {
reject(e);
}
});
});
};
let getData = (links) => {
const nitems = [];
const promises = links
.map(nurl => new Promise((resolve, reject) => {
request(nurl, function(error, response, html) {
let $ = cheerio.load(html);
if (error) return reject(error);
try {
$('.table tbody tr').each(function() {
nitems.push(base_link + $(this).find("a[href]").attr("href"));
});
return resolve(nitems);
} catch (e) {
return reject(e);
}
})
}))
return Promise.all(promises)
}
let FetchData = (links) => {
const limit = 300;
return new Promise((resolve, reject) => {
const itr = (col, cb) => {
request(col, function(error, response, html) {
if (error) cb(error)
let $ = cheerio.load(html);
try {
const title = $(".home-title > h2").eq(0).text();
console.log({
title: title,
itemLink: col
});
cb(null, title);
} catch (e) {
cb(e);
}
})
}
_async.mapLimit(links, limit, itr, function(err, results) {
if (err) reject(err);
return resolve(results);
})
})
}
(async function main() {
const result = await getLinks(link);
const resultSecond = await getData(result);
const resultThird = toArray(resultSecond);
const merged = resultThird.flat(1);
const resultFinal = await FetchData(merged);
for (const title of resultFinal) {
console.log("title: ", title);
}
})().catch(err => console.log(err))
//good to listen to these
process.on('uncaughtException', err => { console.log(err) });
process.on('unhandledRejection', err => { console.log(err) });