Presentamos JSoup
El web scraping puede considerarse como una búsqueda del tesoro digital. Se recorre un sitio web y se extrae toda la información necesaria. Es una técnica que se utiliza para todo tipo de cosas, como encontrar los precios más bajos, analizar la opinión de los clientes o recopilar datos para la investigación.
Java se considera un lenguaje de programación ideal para el web scraping, ya que cuenta con una amplia variedad de bibliotecas y marcos de trabajo que pueden facilitar el proceso. Una de las bibliotecas más conocidas para el web scraping en Java es JSoup. JSoup te permite navegar y buscar en el código HTML de un sitio web, así como extraer todos los datos que necesites.
Al combinar Java con JSoup, puedes crear fantásticas aplicaciones de web scraping capaces de extraer datos de sitios web de forma rápida y sencilla. En este artículo, te explicaré los conceptos básicos del web scraping con JSoup.
Configuración de un proyecto JSoup
En esta sección, crearemos un nuevo proyecto Java con Maven y lo configuraremos para ejecutarlo desde la línea de comandos utilizando el plugin exec-maven-plugin. Esto te permitirá empaquetar y ejecutar fácilmente tu proyecto en un servidor, lo que facilitará la automatización y la escalabilidad del proceso de extracción de datos. A continuación, instalaremos la biblioteca JSoup.
Creación de un proyecto Maven
Maven es una herramienta de automatización de compilaciones para proyectos Java. Gestiona las dependencias, las compilaciones y la documentación, lo que facilita la gestión de proyectos Java complejos. Con Maven, puedes gestionar y organizar fácilmente el proceso de compilación, las dependencias y la documentación de tu proyecto. Además, permite una fácil integración con otras herramientas y marcos de trabajo.
La instalación de Maven es un proceso sencillo que se puede llevar a cabo en unos pocos pasos.
En primer lugar, descarga la última versión de Maven desde la página web oficial (https://maven.apache.org/download.cgi).
Una vez completada la descarga, extrae el contenido del archivo en el directorio que prefieras.
A continuación, tendrás que configurar las variables de entorno.
En Windows, configura la variable JAVA_HOME con la ruta de tu JDK y añade la carpeta «bin» de la instalación de Maven a la variable PATH.
En Linux/macOS, tendrás que añadir las siguientes líneas al archivo ~/.bashrc o ~/.bash_profile:
export JAVA_HOME=ruta/al/jdk
export PATH=$PATH:ruta/a/maven/bin
Comprueba que Maven está instalado ejecutando el comando «mvn --version» en un terminal.
Una vez instalado Maven, ya puedes crear un nuevo proyecto Java con Maven:
mvn archetype:generate -DgroupId=com.project.scraper
-DartifactId=jsoup-scraper-project
-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
Esto crea una nueva carpeta llamada «jsoup-scraper-project» que contiene el contenido del proyecto.
El punto de entrada de la aplicación (la clase principal) estará en el paquete «com.project.scraper».
Ejecutar el proyecto desde la línea de comandos
Para ejecutar un proyecto Java de Maven desde la línea de comandos, utilizaremos el plugin exec-maven-plugin.
To install the plugin, you need to add it to the project's pom.xml file. This can be done by adding the following code snippet to the <build><plugins> section of the pom.xml file:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.project.scraper.App</mainClass>
</configuration>
</plugin>
</plugins>
</build>
Asegúrate de seleccionar la ruta correcta para la clase principal del proyecto.
Escribe «mvn package exec:java» en la terminal (desde el directorio del proyecto) para ejecutar el proyecto.
Instalación de la biblioteca JSoup
Para instalar la biblioteca JSoup, añade la siguiente dependencia al archivo pom.xml de tu proyecto:
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.3</version>
</dependency>
Visita https://mvnrepository.com/artifact/org.jsoup/jsoup para consultar la última versión.
Análisis de HTML en Java con JSoup
En esta sección, exploraremos el sitio web https://www.scrapethissite.com/pages/forms/ y veremos cómo podemos extraer la información sobre los equipos de hockey. Al analizar un sitio web real, comprenderás los conceptos y técnicas que se utilizan en el web scraping con JSoup y cómo podrías aplicarlos a tus propios proyectos.
Recuperación del código HTML
Para obtener el código HTML de la página web, es necesario enviar una solicitud HTTP a la misma. En JSoup, el método connect() se utiliza para establecer una conexión con una URL determinada. Devuelve un objeto Connection, que puede utilizarse para configurar la solicitud y recuperar la respuesta del servidor.
Veamos cómo podemos utilizar el método connect() para recuperar el código HTML de nuestra URL y, a continuación, guardarlo en un archivo HTML local (hockey.html):
package com.project.scraper;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.*;
import java.io.IOException;
public class App
{
public static void main( String[] args )
{
String RAW_HTML;
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")
.get();
RAW_HTML = document.html();
FileWriter writer = new FileWriter("hockey.html");
writer.write(RAW_HTML);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Ahora podemos abrir el archivo y examinar la estructura del HTML con las Herramientas de desarrollo:

Los datos que necesitamos se encuentran en una tabla HTML de la página. Ahora que hemos accedido a la página, podemos proceder a extraer el contenido de la tabla utilizando selectores.
Seleccionadores de escritura
Los selectores de JSoup presentan similitudes con los selectores de JavaScript. Ambos tienen una sintaxis similar y permiten seleccionar elementos de un documento HTML en función de su nombre de etiqueta, clase, identificador y propiedades CSS.
Estos son algunos de los principales selectores que puedes utilizar con JSoup:
- getElementsByTag(): Selecciona elementos en función del nombre de su etiqueta.
- getElementsByClass(): Selecciona elementos en función de su nombre de clase.
- getElementById(): Selecciona un elemento en función de su identificador.
- select(): Selecciona elementos basándose en un selector CSS (similar a querySelectorAll)
Ahora utilicemos algunos de ellos para extraer todos los nombres de los equipos:
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")
.get();
Elements rows = document.getElementsByTag("tr");
for(Element row : rows) {
Elements teamName = row.getElementsByClass("name");
if(teamName.text().compareTo("") != 0)
System.out.println(teamName.text());
}
} catch (IOException e) {
e.printStackTrace();
}
// Prints the team names:
Boston Bruins
Buffalo Sabres
Calgary Flames
Chicago Blackhawks
Detroit Red Wings
Edmonton Oilers
Hartford Whalers
...
Recorrimos todas las filas y, en cada una de ellas, mostramos el nombre del equipo utilizando el selector de clase «name».
El último ejemplo pone de relieve la flexibilidad y la posibilidad de aplicar métodos de selección varias veces a los elementos que se han extraído. Esto resulta especialmente útil cuando se trabaja con documentos HTML complejos y de gran tamaño.
Aquí tienes otra versión que utiliza flujos de Java y el método select() para mostrar todos los nombres de los equipos:
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")
.get();
Elements teamNamesElements = document.select("table .team .name");
String[] teamNames = teamNamesElements.stream()
.map(element -> element.text())
.toArray(String[]::new);
for (String teamName : teamNames) {
System.out.println(teamName);
}
} catch (IOException e) {
e.printStackTrace();
}
// Also prints the team names:
Boston Bruins
Buffalo Sabres
Calgary Flames
...
Ahora imprimamos todos los encabezados y las filas de la tabla:
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")
.get();
Elements tableHeadersElements = document.select("table th");
Elements tableRowsElements = document.select("table .team");
String[] tableHeaders =
tableHeadersElements.stream()
.map(element -> element.text())
.toArray(String[]::new);
String[][] tableRows =
tableRowsElements.stream()
.map(
table_row -> table_row
.select("td")
.stream()
.map(row_element -> row_element.text())
.toArray(String[]::new)
)
.toArray(String[][]::new);
for (int i = 0; i < tableHeaders.length; i++) {
System.out.print(tableHeaders[i] + " ");
}
for (int i = 0; i < tableRows.length; i++) {
for (int j = 0; j < tableRows[i].length; j++) {
System.out.print(tableRows[i][j] + " ");
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
// Prints
Team Name Year Wins Losses OT Losses Win ...
Boston Bruins 1990 44 24 0.55 299 264 35
Buffalo Sabres 1990 31 30 0.388 292 278 14
Calgary Flames 1990 46 26 0.575 344 263 81
Chicago Blackhawks 1990 49 23 0.613 284 211 73
Detroit Red Wings 1990 34 38 0.425 273 298 -25
...
Fíjate en que hemos utilizado flujos para almacenar las filas. Aquí tienes una forma más sencilla de hacerlo, utilizando bucles «for»:
String[][] tableRows = new String[tableRowsElements.size()][];
for (int i = 0; i < tableRowsElements.size(); i++) {
Element table_row = tableRowsElements.get(i);
Elements tableDataElements = table_row.select("td");
String[] rowData = new String[tableDataElements.size()];
for (int j = 0; j < tableDataElements.size(); j++) {
Element row_element = tableDataElements.get(j);
String text = row_element.text();
rowData[j] = text;
}
tableRows[i] = rowData;
}Gestión de la paginación
Al extraer datos de un sitio web, es habitual que la información se distribuya en varias páginas. Para recopilar todos los datos relevantes, es necesario enviar solicitudes a cada una de las páginas del sitio web y extraer la información de cada una de ellas. Podemos implementar fácilmente esta función en nuestro proyecto.

Lo único que tenemos que hacer es cambiar el parámetro de consulta «page_num» en la URL y realizar otra solicitud HTTP con el método connect().
int pageLimit = 25;
String [] tableHeaders = new String[0];
Vector<String[][]> rowsGroups = new Vector<String [][]>();
for (int currentPage=1; currentPage<pageLimit; currentPage++) {
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/?page_num=" + currentPage)
.get();
if(currentPage == 1) {
Elements tableHeadersElements = document.select("table th");
tableHeaders = tableHeadersElements.stream()
.map(element -> element.text())
.toArray(String[]::new);
}
Elements tableRowsElements = document.select("table .team");
String[][] tableRows = new String[tableRowsElements.size()][];
for (int i = 0; i < tableRowsElements.size(); i++) {
Element table_row = tableRowsElements.get(i);
Elements tableDataElements = table_row.select("td");
String[] rowData = new String[tableDataElements.size()];
for (int j = 0; j < tableDataElements.size(); j++) {
Element row_element = tableDataElements.get(j);
String text = row_element.text();
rowData[j] = text;
}
tableRows[i] = rowData;
}
rowsGroups.add(tableRows);
} catch (IOException e) {
e.printStackTrace();
}
// do something with the headers and the the table rows groups
}
Dado que las tablas de cada página tienen los mismos encabezados, debes asegurarte de no extraerlas varias veces.
El código completo
Aquí tienes el código completo que extrae todas las tablas de la página web https://www.scrapethissite.com/pages/forms/. También he incluido una función que guarda los datos en un archivo CSV:
package com.project.scraper;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.io.IOException;
import java.util.Vector;
public class App
{
public static void main( String[] args )
{
int pageLimit = 25;
String [] tableHeaders = new String[0];
Vector<String[][]> rowsGroups = new Vector<String [][]>();
for (int currentPage=1; currentPage<pageLimit; currentPage++) {
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/?page_num=" + currentPage)
.get();
if(currentPage == 1) {
Elements tableHeadersElements = document.select("table th");
tableHeaders = tableHeadersElements.stream()
.map(element -> element.text())
.toArray(String[]::new);
}
Elements tableRowsElements = document.select("table .team");
String[][] tableRows = new String[tableRowsElements.size()][];
for (int i = 0; i < tableRowsElements.size(); i++) {
Element table_row = tableRowsElements.get(i);
Elements tableDataElements = table_row.select("td");
String[] rowData = new String[tableDataElements.size()];
for (int j = 0; j < tableDataElements.size(); j++) {
Element row_element = tableDataElements.get(j);
String text = row_element.text();
rowData[j] = text;
}
tableRows[i] = rowData;
}
rowsGroups.add(tableRows);
} catch (IOException e) {
e.printStackTrace();
}
}
writeFullTableToCSV(rowsGroups, tableHeaders, "full_table.csv");
}
public static void writeFullTableToCSV(Vector<String[][]> rowsGroups, String[] headers, String fileName) {
File file = new File(fileName);
try {
FileWriter writer = new FileWriter(file);
// write the headers first
for (int i = 0; i < headers.length; i++) {
writer.append(headers[i]);
if (i != headers.length - 1) {
writer.append(",");
}
}
writer.append("\n");
// write all the rows groups
for (String [][] rowsGroup : rowsGroups) {
for (String[] row : rowsGroup) {
for (int i = 0; i < row.length; i++) {
writer.append(row[i]);
if (i != row.length - 1) {
writer.append(",");
}
}
writer.append("\n");
}
}
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}Conclusión
En este artículo, hemos explicado cómo instalar Maven y crear un nuevo proyecto Java con Maven, así como cómo ejecutar el proyecto desde la línea de comandos. También hemos visto cómo instalar la biblioteca JSoup añadiendo la dependencia al archivo pom.xml del proyecto. Por último, hemos visto un ejemplo de cómo utilizar JSoup para analizar HTML y extraer datos de un sitio web. Siguiendo los pasos descritos en el artículo, deberías tener una base sólida para configurar un proyecto JSoup y empezar a extraer datos de sitios web. JSoup ofrece una amplia gama de opciones y posibilidades para el web scraping, y te animo a que las explores y las apliques a tus propios proyectos.
Como habrás visto, los datos suelen compartirse entre varias páginas web. Realizar solicitudes rápidas al mismo dominio puede provocar que tu dirección IP sea bloqueada. Con nuestro producto, WebScrapingAPI, nunca tendrás que preocuparte por este tipo de problemas. Nuestra API te garantiza que podrás realizar tantas solicitudes como necesites. Y lo mejor de todo es que puedes probarla gratis.




