¿Por qué descargar archivos con Puppeteer?
Hay muchos casos de uso para un scraper de archivos y StackOverflow está lleno de desarrolladores que buscan respuestas sobre cómo descargar archivos con Puppeteer. Y debemos entender que los archivos incluyen imágenes, PDF, documentos de Excel o Word, y muchos más. Es fácil ver por qué todos estos pueden proporcionar información muy importante para alguien.
Por ejemplo, hay empresas del sector del dropshipping que dependen de imágenes extraídas de fuentes externas, como los marketplaces. Otro buen ejemplo de uso de un rastreador de descarga de archivos es el de las empresas que supervisan documentos oficiales. O incluso proyectos pequeños. Yo mismo tengo un script que descarga facturas de la página web de un socio.
Ahora bien, en lo que respecta al uso de Puppeteer para descargar archivos, he observado que la mayoría de la gente lo elige principalmente por dos razones:
- Está diseñado para Node JS, y Node JS es uno de los lenguajes de programación más populares, tanto para el front-end como para el back-end.
- Abre un navegador real y algunos sitios web dependen de JavaScript para mostrar el contenido. Esto significa que no podrías descargar los archivos utilizando un cliente HTTP normal, que no es capaz de ejecutar archivos JavaScript.
Cómo gestiona Puppeteer la descarga de archivos
Para entender cómo se descargan archivos con Puppeteer, también tenemos que saber cómo lo hace Chrome. Esto se debe a que, en esencia, Puppeteer es una biblioteca que «controla» Chrome a través del Protocolo de herramientas de desarrollo de Chrome (CDP).
En Chrome, los archivos se pueden descargar:
- Manualmente, por ejemplo, con un clic en un botón
- De forma programada, a través del dominio de la página desde el CDP.
Y también existe una tercera técnica utilizada en el web scraping. Concretamente, integra un nuevo actor: un cliente HTTP. De esta forma, el web scraper recopila los `hrefs` de los archivos y, a continuación, se utiliza un cliente HTTP para descargarlos. Cada opción tiene sus casos de uso particulares, por lo que exploraremos ambas formas.
Descargar archivos en Puppeteer con un clic en un botón
En el afortunado caso de que el sitio web del que quieres extraer archivos utilice botones, lo único que tienes que hacer es simular el evento de clic en Puppeteer. La implementación de un descargador de archivos es bastante sencilla en este escenario. Puppeteer incluso documenta el método Page.click() y puedes encontrar más información aquí.
Dado que se trata de un comportamiento «similar al humano», lo que tenemos que hacer es:
- Abrir el navegador
- Navegar hasta la página web de destino
- Localizar el elemento del botón (por su selector CSS o xPath, por ejemplo)
- Hacer clic en el botón
Solo hay cuatro sencillos pasos que debemos implementar en nuestro script. Sin embargo, antes de ponernos a programar, déjame decirte que, al igual que tu navegador habitual, la instancia de Chrome controlada por Puppeteer guardará el archivo descargado en la carpeta de descargas predeterminada, que es:
- \Usuarios\<nombre de usuario>\Descargas para Windows
- /Users/<nombre de usuario>/Descargas para Mac
- /home/<nombre de usuario>/Descargas para Linux
Teniendo esto en cuenta, empecemos a programar. Supongamos que somos astrofísicos y necesitamos recopilar algunos datos de la NASA, que procesaremos más adelante. Por ahora, centrémonos en descargar los archivos .doc.
#1: Identificar elementos «clicables»
Navegaremos hasta el dominio de la NASA aquí e inspeccionaremos los elementos de la página. Nuestro objetivo es identificar los elementos «clicables». Para encontrar los elementos, abre las Herramientas de desarrollador (Comando + Opción + I / Control + Mayús + I en Chrome):

#2: Programar el proyecto
import puppeteer from "puppeteer"
(async () => {
const browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
await page.goto('https://www.nasa.gov/centers/dryden/research/civuav/civ_uav_doc-n-ref.html',
{ waitUntil: 'networkidle0' })
const tr_elements = await page.$x('html/body/div[1]/div[3]/div[2]/div[2]/div[5]/div[1]/table[2]/tbody/tr')
for (let i = 2; i<=tr_elements.length; i ++) {
const text = await tr_elements[i].evaluate(el => el.textContent)
if (text.toLocaleLowerCase().includes('doc')) {
try {
await page.click(`#backtoTop > div.box_710_cap > div.box_710.box_white.box_710_white > div.white_article_wrap_detail.text_adjust_me > div.default_style_wrap.prejs_body_adjust_detail > table:nth-child(6) > tbody > tr:nth-child(${i}) > td:nth-child(3) > a`)
}catch {}
}
}
await browser.close()
})()
Lo que vamos a hacer aquí es:
- Iniciar Puppeteer y navegar hasta nuestro sitio web de destino
- Seleccionar todos los elementos `tr`, que contienen el `href` en el que queremos hacer clic más tarde
- Recorrer los elementos `tr` y a. Comprobar si el texto dentro del elemento contiene la palabra «doc» b. Si es así, creamos el selector y hacemos clic en el elemento
- Cerrar el navegador
Y eso es todo. Descargar archivos con Puppeteer no puede ser más sencillo.
Descargar archivos en Puppeteer con CDP
Sé que la carpeta de descargas predeterminada no supone un gran problema para proyectos pequeños. Sin embargo, en proyectos más grandes, seguramente querrás organizar los archivos descargados con Puppeteer en diferentes directorios. Y ahí es donde entra en juego CDP. Para lograr este objetivo, mantendremos el código actual y solo le añadiremos elementos.
Lo primero que se nos ocurre es resolver la ruta al directorio actual. Por suerte, podemos usar el módulo integrado `node:path`. Todo lo que tenemos que hacer es importar el módulo `path` a nuestro proyecto y usar el método `resolve`, como verás en un momento.
El segundo aspecto es configurar la ruta en nuestro navegador utilizando CDP. Como dije antes, utilizaremos el método `.setDownloadBehavior` de Page Domain. Así es como queda nuestro código actualizado con las dos adiciones:
import puppeteer from "puppeteer"
import path from 'path'
(async () => {
const browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
const client = await page.target().createCDPSession()
await client.send('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: path.resolve('./documents')
});
await page.goto('https://www.nasa.gov/centers/dryden/research/civuav/civ_uav_doc-n-ref.html',
{ waitUntil: 'networkidle0' })
const tr_elements = await page.$x('html/body/div[1]/div[3]/div[2]/div[2]/div[5]/div[1]/table[2]/tbody/tr')
for (let i = 1; i<=tr_elements.length; i ++) {
const text = await tr_elements[i].evaluate(el => el.textContent)
if (text.toLocaleLowerCase().includes('doc')) {
try {
await page.click(`#backtoTop > div.box_710_cap > div.box_710.box_white.box_710_white > div.white_article_wrap_detail.text_adjust_me > div.default_style_wrap.prejs_body_adjust_detail > table:nth-child(6) > tbody > tr:nth-child(${i}) > td:nth-child(3) > a`)
} catch {}
}
}
await browser.close()
})()
Esto es lo que hacemos con el código añadido:
- Estamos creando una nueva CDPSession para «comunicarnos mediante el protocolo Chrome Devtools sin procesar»
- Estamos emitiendo el evento `Page.setDownloadBehavior` donde a. `behavior` se establece en `allow` para permitir descargas b. `downloadPath` se construye con `node:path` para apuntar a la carpeta donde almacenaremos nuestros archivos
Y eso es todo lo que tienes que hacer si quieres cambiar el directorio en el que se guardan los archivos con Puppeteer. Además, también hemos logrado nuestro objetivo de crear un rastreador web para la descarga de archivos.
Descargar archivos con Puppeteer y Axios
La tercera opción que hemos comentado es recopilar enlaces a los sitios de interés y utilizar un cliente HTTP para descargarlos. Personalmente, prefiero Axios, pero también hay alternativas a la hora de realizar scraping web. Así pues, a efectos de este tutorial, voy a utilizar Axios y daré por hecho que estamos creando un scraper para imágenes de coches subastados.
#1: Recopilar los enlaces a nuestros archivos
const get_links = async (url) => {
const hrefs = []
const browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
await page.goto(url, { waitUntil: 'networkidle0' })
const images = await page.$$('img')
for (let i = 1; i<=images.length; i ++) {
try {
hrefs.push(await images[i].evaluate(img => img.src))
} catch {}
}
await browser.close()
return hrefs
}
Estoy seguro de que a estas alturas ya estás familiarizado con la sintaxis de Puppeteer. A diferencia de los scripts anteriores, lo que estamos haciendo ahora es evaluar los elementos `img`, extraer su URL de origen, añadirla a una matriz y devolver la matriz. No hay nada complicado en esta función.
#2: Guardar archivos con axios
const download_file = async (url, save) => {
const writer = fs.createWriteStream(path.resolve(save))
const response = await axios({
url,
method: 'GET',
responseType: 'stream'
})
response.data.pipe(writer)
return new Promise((resolve, reject) => {
writer.on('finish', resolve)
writer.on('error', reject)
})
}
Esta función es un poco más compleja, ya que introduce dos nuevos paquetes: `fs` y `axios`. El primer método del paquete `fs` se explica prácticamente por sí mismo. Lo que hace es crear un flujo de escritura. Puedes leer más al respecto aquí.
A continuación, usamos axios para «conectarnos» a la URL del servidor y le indicamos a axios que la respuesta será de tipo «stream». Por último, dado que estamos trabajando con un stream, vamos a usar `pipe()` para escribir la respuesta en nuestro stream.
Con esta configuración, solo queda combinar las dos funciones en un programa ejecutable. Para ello, basta con añadir las siguientes líneas de código:
let i = 1
const images = await get_links('https://www.iaai.com/Search?url=PYcXt9jdv4oni5BL61aYUXWpqGQOeAohPK3E0n6DCLs%3d')
images.forEach(async (img) => {
await download_file(img, `./images/${i}.svg`)
i += 1
})Conclusiones
La documentación de Puppeteer puede resultar poco clara en lo que respecta a la descarga de archivos. A pesar de ello, hoy hemos descubierto bastantes formas de implementarla. Pero ten en cuenta que extraer archivos con Puppeteer no es realmente una tarea fácil. Verás, Puppeteer inicia un navegador sin interfaz gráfica y estos suelen bloquearse muy rápidamente.
Si buscas una forma discreta de descargar archivos mediante programación, quizá te interese un servicio de web scraping. En Web Scraping API hemos invertido mucho tiempo y esfuerzo en ocultar nuestras huellas para que no nos detecten. Y eso se refleja en nuestra tasa de éxito. Descargar archivos con Web Scraping API puede ser tan fácil como enviar una solicitud curl, y nosotros nos encargamos del resto.
Dicho esto, espero de verdad que el artículo de hoy te haya ayudado en tu proceso de aprendizaje. Además, espero que hayamos cumplido nuestros dos objetivos iniciales. A partir de ahora, deberías ser capaz de crear tus propias herramientas y descargar archivos con Puppeteer.




