Estoy usando Playwright para raspar algunos datos. ¿Cómo hago clic en todos los enlaces de la página que coinciden con un selector?
const { firefox } = require('playwright'); (async () => { const browser = await firefox.launch({headless: false, slowMo: 50}); const page = await browser.newPage(); await page.goto('https://www.google.com'); page.pause(); // allow user to manually search for something const wut = await page.$$eval('a', links => { links.forEach(async (link) => { link.click(); // maybe works? console.log('whoopee'); // doesn't print anything page.goBack(); // crashes }); return links; }); console.log(`wut? ${wut}`); // prints 'wut? undefined' await browser.close(); })();Algunos asuntos:
console.log dentro de $$eval no hace nada.page.goBack() y page.pause() dentro de la evaluación provocan un bloqueo.$$eval no está undefined (si comento page.goBack() para obtener un valor de retorno). Si devuelvo links.length en lugar de links , es correcto (es decir, es un número entero positivo). ¿Eh?Obtengo resultados similares con:
const links = await page.locator('a'); await links.evaluateAll(...)Está claro que no sé lo que estoy haciendo. ¿Cuál es el código correcto para lograr algo como esto?
( Alerta de problema XY : en realidad no me importa si hago esto con $$eval , Playwright o, francamente, incluso Javascript; todo lo que realmente quiero hacer es hacer que esto funcione en cualquier idioma o herramienta).
const { context } = await launch({ slowMo: 250 }); const page = await context.newPage(); await page.goto('https://stackoverflow.com/questions/70702820/how-can-i-click-on-all-links-matching-a-selector-with-playwright'); const links = page.locator('a:visible'); const linksCount = await links.count(); for (let i = 0; i < linksCount; i++) { await page.bringToFront(); try { const [newPage] = await Promise.all([ context.waitForEvent('page', { timeout: 5000 }), links.nth(i).click({ modifiers: ['Control', 'Shift'] }) ]); await newPage.waitForLoadState(); console.log('Title:', await newPage.title()); console.log('URL: ', page.url()); await newPage.close(); } catch { continue; } }Hay varias maneras de hacer esto, pero me gusta más este enfoque. Hacer clic en un enlace, esperar a que se cargue la página y luego volver a la página anterior tiene muchos problemas; lo más importante es que, para muchas páginas, los enlaces pueden cambiar cada vez que se carga la página. Ctrl+shift+clic se abre en una nueva pestaña, a la que puede acceder usando el patrón Promise.all y capturando el evento 'page' .
Solo intenté esto en esta página, así que estoy seguro de que hay muchos otros problemas que pueden surgir. Pero para esta página en particular, era necesario usar 'a:visible' para evitar quedarse atascado en enlaces ocultos. Toda la operación de hacer clic está envuelta en un intento/captura porque algunos de los enlaces no son enlaces reales y no abren una nueva página.
Dependiendo de su caso de uso, puede ser más fácil obtener todos los hrefs de cada enlace:
const links = page.locator('a:visible'); const linksCount = await links.count(); const hrefs = []; for (let i = 0; i < linksCount; i++) { hrefs.push(await links.nth(i).getAttribute('href')); } console.log(hrefs);Pruebe este enfoque. Usaré mecanografiado.
await page.waitForSelector(selector,{timeout:10000}); const links = await page.$$(selector); for(const link of links) { await link.click({timeout:8000}); //your additional code }Ver más en https://youtu.be/54OwsiRa_eE?t=488