Estoy tratando de crear un script que tome una lista de URL, vaya al sitio y tome una captura de pantalla.

He logrado que esto funcione con titiriteros. Sin embargo, el problema que he tenido es que cuando digo 50 URL en la lista, intentará iniciar sesiones de títeres para todos a la vez, lo que significa que la mayoría del tiempo de espera se carga antes de que se cargue el sitio y puede tomar una captura de pantalla.

Descubrí que puedo ejecutar con éxito 10 a la vez, por lo que quiero configurar un sistema de colas para hacer esto.

parser.on('readable', function(){
  while(record = parser.read()){
      counter +=1;
      console.log(record.URL);


      (async (url = record.URL, name = record.shortURL, counter1 = counter) => {
      const browser = await puppeteer.launch( {defaultViewport: {width: 1024, height:768} } );
      const page = await browser.newPage();
      await page.goto(url);
      title = await page.title();
      domainRegex = /^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img;
      match = domainRegex.exec(url);

      width = 1024;//await page.viewport().width;
      height = 1000;//await page.viewport.height();
      await page.screenshot({path: "Screenshots/"+counter1+". "+match[1] + "- " +title.replace(/[\W_]+/g,"")+".jpg", clip : {x:0, y:0, width: width, height: height}});

      await browser.close();    
      })();

  }
});
1
ashfp 9 sep. 2018 a las 22:07

4 respuestas

La mejor respuesta

Es posible que desee echar un vistazo a puppeteer-cluster (descargo de responsabilidad: soy el autor) .

Puedes hacerlo así:

(async () => {
    // create a cluster that handles 10 parallel browsers
    const cluster = await Cluster.launch({
        concurrency: Cluster.CONCURRENCY_BROWSER,
        maxConcurrency: 10,
    });

    // define the task
    await cluster.task(async ({ page, data: { counter, record} }) => {
        const url = record.URL;

        await page.goto(url);
        title = await page.title();
        domainRegex = /^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img;
        match = domainRegex.exec(url);

        width = 1024;//await page.viewport().width;
        height = 1000;//await page.viewport.height();
        await page.screenshot({path: "Screenshots/"+counter+". "+match[1] + "- " +title.replace(/[\W_]+/g,"")+".jpg", clip : {x:0, y:0, width: width, height: height}});
    });

    // queue your jobs
    parser.on('readable', function () {
        while (record = parser.read()) {
            counter += 1;
            cluster.queue({ counter, record });
        }
    });
})();

Esto manejará 10 instancias de navegador paralelas y también se encargará de los bloqueos del navegador y el manejo de errores.

1
Thomas Dondorf 10 sep. 2018 a las 15:22

El siguiente código lanzará inicialmente 10 sesiones. Una vez que finalice cada sesión, eliminará el siguiente registro y lanzará otro, hasta que no queden más registros. Esto asegurará que se ejecute un máximo de 10 al mismo tiempo.

parser.on('readable', async () => {
    const maxNumberOfSessions = 10;
    let counter = 0;

    await Promise.all(Array.from({length: maxNumberOfSessions}, dequeueRecord));
    console.log("All records have been processed.");

    function dequeueRecord() {
        const nextRecord = parser.read();
        if(nextRecord) return processRecord(nextRecord).then(dequeueRecord);
    }

    async function processRecord(record) {
        const number = ++counter;
        console.log("Processing record #" + number + ": " + record.URL);

        const browser = await puppeteer.launch({defaultViewport: {width: 1024, height: 768}});
        const page = await browser.newPage();
        await page.goto(record.URL);
        const title = await page.title();
        const domainRegex = /^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img;
        const match = domainRegex.exec(record.URL);

        const width = 1024; // await page.viewport().width;
        const height = 1000; // await page.viewport().height;
        await page.screenshot({path: "Screenshots/" + number + ". " + match[1] + "- " + title.replace(/[\W_]+/g, "") + ".jpg", clip: {x: 0, y: 0, width, height}});

        await browser.close();    
    }
});
2
11 sep. 2018 a las 20:37

Si desea ejecutar un conjunto de promesas en secuencia, puede utilizar Promise.mapSeries del paquete Bluebird. Entiendo que esto significaría agregar un paquete adicional, pero es simple y no necesita que cree un sistema de colas.

http://bluebirdjs.com/docs/api/promise.mapseries.html

1
Vinit Sarvade 9 sep. 2018 a las 19:43

Si desea ejecutarlos todos en serie, puede convertir esto en una función asíncrona y esperarlo. De esta manera, se ejecutará uno por uno.

// let's separate it for readability
async function getRecord(record, counter) {
    const url = record.URL,
        name = record.shortURL,
        counter1 = counter;
    const browser = await puppeteer.launch({
        defaultViewport: {
            width: 1024,
            height: 768
        }
    });
    const page = await browser.newPage();
    await page.goto(url);
    title = await page.title();
    domainRegex = /^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img;
    match = domainRegex.exec(url);

    width = 1024; //await page.viewport().width;
    height = 1000; //await page.viewport.height();
    await page.screenshot({
        path: "Screenshots/" + counter1 + ". " + match[1] + "- " + title.replace(/[\W_]+/g, "") + ".jpg",
        clip: {
            x: 0,
            y: 0,
            width: width,
            height: height
        }
    });

    await browser.close();
}

parser.on('readable', async function() { // <-- here we make it async
    while (record = parser.read()) {
        counter += 1;
        console.log(record.URL);
        await getRecord(record, counter) // <-- and we await each call
    }
});

Hay otras formas como Promise.map y for..of, pero mantengamos esto más simple por ahora.

2
Md. Abu Taher 9 sep. 2018 a las 19:43