首页 > 解决方案 > 异步/等待 - 在 MySQL 回调中将数据传递给 Puppeteer

问题描述

我需要将每个客户端都放在一个表中,这样我就可以遍历它们,并使用 Puppeteer 来抓取一些数据。我需要 MySQL 查询,因为我必须通过查询字符串传递一些参数。

我正在使用 Puppeteer、Puppeteer-cluster(由于有数百行)和 MySQL 驱动程序。

const mysql = require('mysql');
const { Cluster } = require('puppeteer-cluster');

const sql = "select an.id_clientes, ano, c.nome, c.cpf, c.dt_nasc from clientes_anos an inner join clientes c on an.id_clientes = c.id limit 3";
db.query(sql, async (err, result) => {
    //console.log(result);

    const cluster = await Cluster.launch({
        concurrency: Cluster.CONCURRENCY_CONTEXT,
        maxConcurrency: 2,
        puppeteerOptions: {
            headless: false
        },
        //monitor: true,
        timeout: 90000,
    });

    for (const elem of result){
        const cpf = elem.cpf;
        const dt = elem.dt_nasc.toLocaleDateString();

        const url = `https://servicos.receita.fazenda.gov.br/Servicos/ConsRest/Atual.app/paginas/index.asp?cpf=${cpf}&dtnasc=${dt}`;
        console.log(url);


        (async()=>{

            cluster.on('taskerror', (err, data) => {
                //console.log(`Error crawling ${data}: ${err.message}`);
            });
            
            await cluster.task(async ({ page, data: url }) => {
                console.log(dt, cpf);
                await page.goto(url);
                await page.type('#data_nascimento', dt);
                await page.type('input[name=CPF]', cpf);
                await page.waitForNavigation();

                try{
                    const msg = await page.$eval(
                    `#rfb-main-container table tbody tr:nth-of-type(1) td:nth-of-type(1)`, divs => divs.innerText.trim()
                    );
            
                    if(msg.includes('Resultado encontrado: Saldo inexistente de imposto a pagar ou a restituir.')){
                        console.log('situação: '+ msg);
                    }else{
                        console.log('0');
                    }
            
                }catch(exception){
                    console.log('Error');
                }

            });
            
            await cluster.queue(url);
            
            await cluster.idle();
            await cluster.close();

        })();
    }

});

我遇到的问题是,当页面加载时,await page.type('#data_nascimento', dt); await page.type('input[name=CPF]', cpf);没有返回正确的值(来自查询的值是正确的),它们返回了最后一行的值。我打赌它是异步的,但我无法弄清楚。

有什么想法可以解决这个问题吗?

标签: javascriptnode.jsasynchronousasync-awaitpuppeteer

解决方案


该死的男孩,我有话要说:)

  1. 我认为你的问题的主要原因是循环/回调/集群之间的交互这里是一个例子来澄清我对循环的观点
for (var i = 0; i<3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// shows : 3 3 3
// because when console is finally executed "i" has been fully looped

如果您使用 const 或 let 而不是 var,则代码可以正常工作并显示 0 1 2。

如果您不使用回调而只等待这种情况会更清楚。

async function wait(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

for (var i = 0; i<3; i++) {
  await wait(1000);
  console.log(i);
}

您可以将此应用于您的 mysql 调用以展平您的代码。

function query(sql) {
  return new Promise((resolve, reject) => {
    db.query(sql, function(err, result) {
      if (err) return reject(err);

      resolve(result);
    })
  });
}

//then you call it like this
const sql = "select an.id_clientes, ano, c.nome, c.cpf, c.dt_nasc from clientes_anos an inner join clientes c on an.id_clientes = c.id limit 3";
const result = await query(sql)
  1. 本着扁平化代码的精神,您可以删除异步自动执行功能吗?它看起来非常不必要。"(async()=>{" ... "})();"

  2. 最后是集群部分:它没有写出来,但是从阅读文档中我猜想你想在集群功能中使用的任何数据都需要被推送到队列中(XXX)。

cluster.queue(XXX)
=> 
await cluster.task(async ({ page, data: XXX}) ...)

如果您需要许多不同的数据,那么您不仅应该推送您的网址,还应该推送整个对象。

cluster.queue({cpf, dt, url})
=> 
await cluster.task(async ({ page, data: {cpf, dt, url}}) ...)

我从来没有尝试过使用这个模块,但这对我来说很有意义。你能试试吗?

PS:另外,完全不使用集群可以大大简化事情。


推荐阅读