Estoy tratando de raspar los precios de varias páginas usando titiritero. Lo que tengo problemas es escribir un solo archivo JSON con todos los datos raspados. El problema es que si trato de escribir el archivo con las variables desde dentro de la async function
, aparece un error que dice que esa variable no se ha declarado.
async function scrapeVMZ(url) { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url); const [vmzel1] = await page.$x('//*[@id="__layout"]/div/div[1]/section/div/div/div[2]/div[2]/div[1]/div/div/div[2]/div/div[1]/span[2]'); const vmztxt1 = await vmzel1.getProperty('textContent'); const vmzRawTxt1 = await vmztxt1.jsonValue(); const [vmzel2] = await page.$x('//*[@id="__layout"]/div/div[1]/section/div/div/div[2]/div[2]/div[1]/div/div/div[2]/div/div[1]/span[4]/b'); const vmztxt2 = await vmzel2.getProperty('textContent'); const vmzRawTxt2 = await vmztxt2.jsonValue(); console.log({vmzRawTxt1, vmzRawTxt2}); const vmz01 = JSON.stringify(vmzRawTxt1); const vmz02 = JSON.stringify(vmzRawTxt2); console.log(vmz01, vmz02); browser.close(); } scrapeVMZ('https://www.vmzviagens.com.br/ingressos/orlando/walt-disney-orlando'); async function scrapeMB(url) { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url); const [mbel1] = await page.$x('/html/body/section[3]/div/div/div[2]/div[1]/div/div[2]/a[1]/span[2]/span/div/div[2]/span'); const mbtxt1 = await mbel1.getProperty('textContent'); const mbRawTxt1 = await mbtxt1.jsonValue(); const [mbel2] = await page.$x('/html/body/section[3]/div/div/div[2]/div[1]/div/div[2]/a[1]/span[2]/span/div/div[4]/span'); const mbtxt2 = await mbel2.getProperty('textContent'); const mbRawTxt2 = await mbtxt2.jsonValue(); console.log({mbRawTxt1, mbRawTxt2}); const mb01 = JSON.stringify(mbRawTxt1); const mb02 = JSON.stringify(mbRawTxt2); console.log(mb01, mb02); browser.close(); } scrapeMB('https://www.ingressosmagicblue.com.br/produtos/?mpage=2');
¿Cómo puedo escribir un archivo, usando el código anterior, para almacenar dentro de mi archivo JSON, las variables vmz01, vmz02
y mb01, mb02
, como el ejemplo a continuación?
let abc = { "MB": { preco: mb01, preco2: mb02 }, "VMZ": { preco: vmz01, preco2: vmz02 } };
Cuando console.log
aparece en una función en lugar de devolver resultados, es un callejón sin salida. Devuelve los resultados si quieres usarlos más tarde. Dado que está devolviendo promesas, puede await
en la persona que llama, ya sea en serie o en paralelo .
También hay mucho código repetido en sus funciones, y probablemente no necesite 2 navegadores. Aquí hay un refactor rápido que se ejecuta en paralelo en un solo navegador (las teclas preco
son un poco incómodas; sugeriría una matriz aquí potencialmente).
const fs = require("fs").promises; const puppeteer = require("puppeteer"); // ^14.3.0 const vmzPaths = [ '//*[@id="__layout"]/div/div[1]/section/div/div/div[2]/div[2]/div[1]/div/div/div[2]/div/div[1]/span[2]', '//*[@id="__layout"]/div/div[1]/section/div/div/div[2]/div[2]/div[1]/div/div/div[2]/div/div[1]/span[4]/b', ]; const mbPaths = [ "/html/body/section[3]/div/div/div[2]/div[1]/div/div[2]/a[1]/span[2]/span/div/div[2]/span", "/html/body/section[3]/div/div/div[2]/div[1]/div/div[2]/a[1]/span[2]/span/div/div[4]/span", ]; const scrape = async (browser, url, paths) => { const page = await browser.newPage(); await page.goto(url); return Promise.all(paths.map(async p => (await page.waitForXPath(p)).evaluate(e => e.textContent) )); }; let browser; (async () => { browser = await puppeteer.launch({headless: true}); const text = await Promise.all([ scrape(browser, "https://www.ingressosmagicblue.com.br/produtos/?mpage=2", mbPaths), scrape(browser, "https://www.vmzviagens.com.br/ingressos/orlando/walt-disney-orlando", vmzPaths), ]); const names = ["MB", "VMZ"]; const collected = Object.fromEntries(text.map((e, i) => [ names[i], Object.fromEntries(e.map((e, i) => [`preco${i === 0 ? "" : (i + 1)}`, e] )) ])); await fs.writeFile("out.json", JSON.stringify(collected, null, 2)); })() .catch(err => console.error(err)) .finally(() => browser?.close()) ;
Aparte, no soy un gran admirador de las rutas y los selectores hiperprecisos generados por el navegador . Estos tienden a ser súper frágiles y casi siempre hay una mejor manera de elegir un selector. Pero no he mirado la página con el fin de centrarme en el tema de las promesas, así que lo dejaré como ejercicio para el lector.