Node.js Puppeter使用唯一数据启动多个实例?

Node.js Puppeter使用唯一数据启动多个实例?,node.js,json,puppeteer,Node.js,Json,Puppeteer,我一直在尝试让Puppeter为存储在.json文件中的每个概要文件启动一个唯一的实例。这是因为目前我一直在创建一个新文件夹,其中包含我所有的代码,并为我想要运行的每个帐户/实例创建一个唯一的.json文件。我更愿意将我的所有信息存储在1.json文件中,然后让我的代码为每个概要文件启动一个唯一的实例 目标: 在.json文件中输入所有配置文件信息 让代码为列表中的每个概要文件启动一个唯一的实例 每个唯一实例应仅使用配置文件代码 示例:木偶师实例1使用配置文件1启动,木偶师实例2使用配置文件2启

我一直在尝试让Puppeter为存储在.json文件中的每个概要文件启动一个唯一的实例。这是因为目前我一直在创建一个新文件夹,其中包含我所有的代码,并为我想要运行的每个帐户/实例创建一个唯一的.json文件。我更愿意将我的所有信息存储在1.json文件中,然后让我的代码为每个概要文件启动一个唯一的实例

目标:

  • 在.json文件中输入所有配置文件信息
  • 让代码为列表中的每个概要文件启动一个唯一的实例
  • 每个唯一实例应仅使用配置文件代码
  • 示例:木偶师实例1使用配置文件1启动,木偶师实例2使用配置文件2启动,等等

    settings.json示例

    [
    {
        "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)));
    })();