Node.js Puppeter使用唯一数据启动多个实例?
我一直在尝试让Puppeter为存储在.json文件中的每个概要文件启动一个唯一的实例。这是因为目前我一直在创建一个新文件夹,其中包含我所有的代码,并为我想要运行的每个帐户/实例创建一个唯一的.json文件。我更愿意将我的所有信息存储在1.json文件中,然后让我的代码为每个概要文件启动一个唯一的实例 目标:Node.js Puppeter使用唯一数据启动多个实例?,node.js,json,puppeteer,Node.js,Json,Puppeteer,我一直在尝试让Puppeter为存储在.json文件中的每个概要文件启动一个唯一的实例。这是因为目前我一直在创建一个新文件夹,其中包含我所有的代码,并为我想要运行的每个帐户/实例创建一个唯一的.json文件。我更愿意将我的所有信息存储在1.json文件中,然后让我的代码为每个概要文件启动一个唯一的实例 目标: 在.json文件中输入所有配置文件信息 让代码为列表中的每个概要文件启动一个唯一的实例 每个唯一实例应仅使用配置文件代码 示例:木偶师实例1使用配置文件1启动,木偶师实例2使用配置文件2启
[
{
"email": "email1@gmail.com"
},
{
"email": "email2@gmail.com"
},
{
"email": "email3@gmail.com"
}
]
main.js的示例
const fs = require('fs');
const puppeteer = require('puppeteer');
const profile = JSON.parse(fs.readFileSync('./settings.json'));
var id = 0
while (id <= 2) {
emailInfo = profile[id].email;
console.log(emailInfo)
botRun()
id++;
}
function botRun() {
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.waitForTimeout(500)
console.log('function ' + emailInfo) //pretend this is page.type --> it would result in 'email3@gmail.com' for all instances since this is what the var is now but I want it to stay with the info in the loop
await browser.close();
})();
}
这是一张澄清的图片。我的目标是将选项卡保留在各自的浏览器中,而不是突然将所有选项卡都扔到一个浏览器中:
简而言之,不要让并行运行的异步操作使用范围更大的“共享”变量。这就是问题的症结所在,因为有一个异步操作循环试图全部使用
emailInfo
变量,这样它们就会互相践踏
不要让emailInfo
像你一样成为一个范围更广的变量(实际上,更糟糕的是,你根本没有声明它,这使得它成为一个隐式全局变量——非常糟糕)。将其作为函数参数传递到要在中使用它的特定函数中,或在要在中使用它的范围内使用let
声明它。然后,它将在每个使用它的地方有单独的值。你的问题是你有一个变量和许多异步的东西都试图使用它。这总是会导致Javascript出现问题
另外,不要再使用var
。使用let
或const
。这两个都是阻塞作用域,而不是函数作用域,因此您可以更精细地控制它们的作用域。如果确实需要函数范围的变量,则始终可以在函数顶部声明一个带有let
的变量
如果您试图解决的真正问题是希望在botRun()
中使用emailInfo
,则只需传入该值:
const fs = require('fs');
const puppeteer = require('puppeteer');
const profile = JSON.parse(fs.readFileSync('./settings.json'));
let id = 0;
while (id <= 2) {
console.log(profile[id].email);
botRun(profile[id].email);
id++;
}
async function botRun(emailInfo) {
let browser;
try {
browser = await puppeteer.launch();
const page = await browser.newPage();
await page.waitForTimeout(500);
console.log('function ' + emailInfo);
} catch(e) {
console.log(e);
// decide what you're doing upon errors here
} finally {
if (browser) {
await browser.close();
}
}
}
const fs=require('fs');
const puppeter=require('puppeter');
constprofile=JSON.parse(fs.readFileSync('./settings.JSON');
设id=0;
while(id将循环放入木偶师代码中,或将emailInfo
作为参数传递给函数
如果要连续运行任务,请执行以下操作:
const fs = require("fs");
const puppeteer = require("puppeteer");
(async () => {
const profile = JSON.parse(fs.readFileSync("./settings.json"));
const browser = await puppeteer.launch();
for (const {email: emailInfo} of profile) {
const page = await browser.newPage();
await page.waitForTimeout(500)
// do stuff with emailInfo
await page.close();
}
await browser.close();
})();
如果要并行运行所有任务,请执行以下操作:
(async () => {
const profile = JSON.parse(fs.readFileSync("./settings.json"));
const browser = await puppeteer.launch();
await Promise.all(profile.map(async ({email: emailInfo}) => {
const page = await browser.newPage();
await page.waitForTimeout(500)
// do stuff with emailInfo
await page.close();
}));
await browser.close();
})();
如果//do stuff with emailInfo
是一段很长的代码,请使用一个函数(正如您最初尝试的那样)并将其作为参数emailInfo
。这与您最初想要的最接近(每封电子邮件打开一个新浏览器):
或者一次运行所有电子邮件:
// botRun is the same as above
(async () => {
const profile = JSON.parse(fs.readFileSync("./settings.json"));
await Promise.all(profile.map(({email}) => botRun(email)));
})();
前两个代码段的语义与您的代码稍有不同,但我怀疑为每个请求生成并销毁整个浏览器过程是否有意义。除非您有充分的理由,否则最好在当前浏览器中打开新页面(选项卡)
同样,如果你有大量的输入,这两种模式都不太好——顺序方法可能太慢,并行方法可能太快(一次打开4000个浏览器并不有趣)。考虑一个任务队列,这样你就可以做一些并行的工作,但是把它绑定到一个合理的程度。
已勾选了除此之外的大多数关键点(避免全局、避免var
等)但我还想补充一点,您几乎不需要带计数器变量的循环。如果您确实使用带计数器的循环,则更喜欢for
for
而不是。带计数器的循环非常冗长,往往会导致与off-by-one错误相关的错误。JS提供了许多迭代抽象,如map
、forEach
和for..of
循环,这些循环干净、语义清晰且不易出错
此外,上面的代码省略了错误处理,但是调用可能超时的Puppeter函数时,try
-catch
是非常必要的。如果操作时间比您预期的稍长,或者服务器停机,您不希望应用程序意外崩溃。感谢您的响应这正是我需要的!我知道实际上,必须运行单独的浏览器实例来执行反bot措施。我的代码也会清除缓存和cookies,因此我不希望这会干扰其他实例。单独的浏览器是否会有一个特定的解决方法?当然,您希望请求一次运行一个或一次运行所有浏览器实例,还是是,这样我就不会加载任何浏览器了超过10-20个配置文件,但我仍然认为我会设置队列以最小化负载。另外,还有一件事。如果我还想存储登录密码,然后按顺序调用它(如电子邮件)我怎样才能添加这些内容?也感谢您对循环和错误处理的说明。没问题。如果您有更多属性,只需将它们添加到循环中:for(const{email,password,somethingElse}of profile){
。如果您使用的是函数版本,请添加更多参数,或者根据需要传入整个对象并拉出属性。如果您不习惯于分解结构,可以对(const user of profiles){const email=user.email;/*…etc…*/}执行
对于10-20个配置文件,首先从一次一个版本开始,然后升级。这太完美了!我现在似乎遇到的一个问题是,我的一些命令会溢出到其他浏览器中。例如,如果您添加了page.close()最后,再打开一个新标签。所有标签都将在1个浏览器上打开。有什么解决方法吗?@TheCuriousMarketer-这回答了你的问题吗?
const botRun = async emailInfo => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.waitForTimeout(500)
// do stuff with emailInfo
await browser.close();
};
(async () => {
const profile = JSON.parse(fs.readFileSync("./settings.json"));
for (const {email} of profile) {
await botRun(email); // one at a time
}
})();
// botRun is the same as above
(async () => {
const profile = JSON.parse(fs.readFileSync("./settings.json"));
await Promise.all(profile.map(({email}) => botRun(email)));
})();