¿Por qué descargar archivos con Puppeteer?
Hay muchos casos en los que se puede utilizar un rastreador de archivos, y StackOverflow está repleto de desarrolladores que buscan respuestas sobre cómo descargar archivos con Puppeteer. Y hay que tener en cuenta que los archivos incluyen imágenes, archivos PDF, documentos de Excel o Word, y muchos más. Es fácil comprender por qué todo esto puede proporcionar información muy importante para alguien.
Por ejemplo, hay empresas del sector del dropshipping que se basan en imágenes extraídas de fuentes externas, como los mercados online. 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 algunas páginas web utilizan JavaScript para mostrar el contenido. Esto significa que no podrías descargar los archivos con un cliente HTTP normal, ya que este no es capaz de ejecutar archivos JavaScript.
Cómo gestiona Puppeteer la descarga de archivos
Para entender cómo descargar archivos con Puppeteer, también debemos 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, se pueden descargar archivos:
- De forma manual, por ejemplo, con solo pulsar un botón
- A nivel de programación, a través del dominio de la página de CDP.
Además, existe una tercera técnica que se utiliza en el web scraping. Concretamente, incorpora un nuevo elemento: un cliente HTTP. De este modo, el programa de web scraping recopila los enlaces `href` de los archivos y, a continuación, se utiliza un cliente HTTP para descargarlos. Cada opción tiene sus propios casos de uso, por lo que analizaremos ambas formas.
Descargar archivos en Puppeteer con solo hacer clic en un botón
En el caso de que la página web de la 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 caso. 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:
- Abre el navegador
- Accede a la página web en cuestión
- Localiza el elemento «botón» (por ejemplo, mediante su selector CSS o su xPath)
- Haz clic en el botón
Solo hay que seguir cuatro sencillos pasos 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:
- \Users\<username>\Downloads for Windows
- /Users/<username>/Downloads for Mac
- /home/<username>/Downloads for Linux
Teniendo esto en cuenta, empecemos a programar. Imaginemos que somos astrofísicos y que necesitamos recopilar algunos datos de la NASA, que procesaremos más adelante. Por ahora, centrémonos en descargar los archivos .doc.
N.º 1: Identificar los elementos «en los que se puede hacer clic»
Ahora accederemos al sitio web de la NASA y examinaremos los elementos de la página. Nuestro objetivo es identificar los elementos en los que se puede hacer clic. Para encontrarlos, abre las Herramientas de desarrollador (Comando + Opción + I / Control + Mayús + I en Chrome):

N.º 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 estamos haciendo aquí es:
- Abre Puppeteer y ve a la página web de destino
- Selecciona todos los elementos `tr` que contengan el atributo `href` en el que queremos hacer clic más adelante
- Recorre los elementos del tr y a. Comprueba si el texto del elemento contiene la palabra «doc» b. Si es así, creamos el selector y hacemos clic en el elemento
- Cierra 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 los proyectos pequeños. Sin embargo, en el caso de 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 lograrlo, mantendremos el código actual y solo le añadiremos algunas cosas.
Lo primero que se nos ocurre es resolver la ruta al directorio actual. Por suerte, podemos utilizar el módulo integrado `path` de Node.js. Solo tenemos que importar el módulo `path` a nuestro proyecto y utilizar el método `resolve`, como verás en un momento.
El segundo aspecto consiste en configurar la ruta en nuestro navegador mediante CDP. Como he dicho antes, utilizaremos el método `.setDownloadBehavior` del dominio de la página. Así es como queda nuestro código actualizado con estas dos modificaciones:
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 sesión de CDPS para «hablar del protocolo Chrome DevTools sin filtrar».
- Estamos emitiendo el evento `Page.setDownloadBehavior`, en el que: a. `behavior` se establece en `allow` para permitir las descargas b. `downloadPath` se crea con `node:path` para que apunte 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 el archivo con Puppeteer y Axios
La tercera opción que hemos comentado consiste en recopilar enlaces a los sitios web de interés y utilizar un cliente HTTP para descargarlos. Personalmente, prefiero axios, pero también hay otras alternativas para el scraping web. Por lo tanto, a efectos de este tutorial, voy a utilizar axios y daré por hecho que estamos creando un scraper para imágenes de coches subastados.
N.º 1: Recopila 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 hacemos ahora es evaluar los elementos `img`, extraer su URL de origen, añadirla a una matriz y devolver la matriz. No hay nada especial en esta función.
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 por sí solo. Lo que hace es crear un flujo en el que se puede escribir. Puedes leer más al respecto aquí.
A continuación, utilizamos 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 utilizar `pipe()` para escribir la respuesta en nuestro stream.
Una vez configurado todo esto, solo queda combinar ambas funciones en un programa que se pueda ejecutar. 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
Es posible que la documentación de Puppeteer no sea del todo clara en lo que respecta a la descarga de archivos. A pesar de ello, hoy hemos descubierto varias formas de llevarlo a cabo. Pero hay que tener en cuenta que extraer archivos con Puppeteer no es precisamente una tarea fácil. Verás, Puppeteer ejecuta 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 dedicado mucho tiempo y esfuerzo a 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 sencillo como enviar una solicitud curl; 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 los objetivos iniciales que nos habíamos marcado. A partir de ahora, deberías ser capaz de crear tus propias herramientas y descargar archivos con Puppeteer.




