Scraping en Java (JSoup), con ejemplos


El proyecto de este post los puedes descargar pulsando AQUI.

El Web Scraping (o Scraping) son un conjunto de técnicas que se utilizan para obtener de forma automática el contenido que hay en páginas web a través de su código HTML. El uso de estas técnicas tienen como finalidad recopilar grandes cantidades de datos de diferentes páginas web cuyo uso posterior puede ser muy variado: homgenización de datos, tratamiento de contenido para la extracción de conocimiento, complementar datos en una web, etc. Decir por último en esta introducción que las técnicas de Scraping se pueden enmarcar dentro de los campos de la Inteligencia Artificial y del Big Data en la primera fase de recolección de datos para su posterior almacenamiento, tratamiento y visualización.

En este tutorial vamos ha "scrapear" (o a obtener los datos) de los articulos y tutoriales de esta página web, utilizando la librería "JSoup" (para Java) que es una libreria que nos facilita muchisimo la labor de scrapeo. La finalidad de este tutorial no es hacer un tutorial para expertos, sino hacer una introducción de como hacer las conexiones HTTP y como tratar y extraer el contenido que nos interesa de los HTMLs que obtenemos con la libreria JSoup. Para este tutorial es necesario que se tengan conocimientos mínimos de HTML y CSS (no hace falta ser un experto) ya que a la hora de scrapear tenemos que tener conocimiento de las etiquetas de HTML como pueden ser head, body, h1, h2, …, h6, p, div, span, table, etc. y de los estilos CSS que se pueden aplicar a estas etiquetas con id y class.

Dadas las directrices y los conocimientos mínimos necesarios para este tutorial vamos a proceder a explicar como scrapear y obtener los datos de las entradas de jarroba.com. En primer lugar vamos a utilizar la libreria JSoup, por tanto nos la debemos importar, bien sea con el ".jar" o a través de Maven. En este caso nos hemos creado un proyecto Maven e importamos el JSoup añadiendo a nuestro fichero pom.xml lo siguiente:

<!-- JSoup -->
<dependency>
	<groupId>org.jsoup</groupId>
	<artifactId>jsoup</artifactId>
	<version>1.8.1</version>
</dependency>

En segundo lugar, se ha de estudiar la estructura del HTML de la web para ver de que forma podemos extaer su contenido. En este caso en el que queremos extraer la información de cada entrada de este web (título, autor y fecha), podemos ver como todas las entradas están entre etiquetas 'div' con la clase CSS 'col-md-4 col-xs-12'; es decir (tal y como vemos en la siguiente imagen), debemos de obtener el contenido que hay entre "<div class="col-md-4 col-xs-12"> contenido </div>"

scraping_jarroba_1

Como podemos ver, puede resultar relativamente sencillo crearse una expresión regular y obtener todas las apareciones que hay entre las etiquetas 'div' con la clase CSS mencionada anteriormente (por cierto son clases de CSS Bootstrap), pero la libreria JSoup ya nos aporta los métodos necesarios (y muy bien optimizados ya que el Patter y Matcher son procesos muy pesados y lentos) para obtener el contenido que hay entre las etiquetas HTML.

Dentro de esas etiquetas, tenemos que seguir extrayendo los datos del título, autor y fecha. Si vemos más en detalle su HTML, vemos lo siguiente:

scraping_html_jarroba

Por tanto con esto ya hemos hecho un estudio del HTML y ya sabemos a partir de que etiquetas tenemos que scrapear los datos, por tanto vamos a pasar ahora a explicar como hacer el scrapeo con la libreria JSoup.

En primer lugar tenemos que hacer una petición HTTP a la página web para obtener su HTML. Previamente, seria recomendable (aunque no necesario, pero ya de paso lo explico), saber el estado o el "status code" de la petición a esa web. Para ello JSoup tiene los métodos necesarios para darnos el "status code" y a continuación podeis ver un método que dado una url nos devuelve su "status code":

/**
 * Con esta método compruebo el Status code de la respuesta que recibo al hacer la petición
 * EJM:
 * 		200 OK			300 Multiple Choices
 * 		301 Moved Permanently	305 Use Proxy
 * 		400 Bad Request		403 Forbidden
 * 		404 Not Found		500 Internal Server Error
 * 		502 Bad Gateway		503 Service Unavailable
 * @param url
 * @return Status Code
 */
public static int getStatusConnectionCode(String url) {
		
    Response response = null;
	
    try {
	response = Jsoup.connect(url).userAgent("Mozilla/5.0").timeout(100000).ignoreHttpErrors(true).execute();
    } catch (IOException ex) {
	System.out.println("Excepción al obtener el Status Code: " + ex.getMessage());
    }
    return response.statusCode();
}

JSoup nos proporciona los métodos necesarios para obtener el HTML de una url; o dicho de otra mamera, hace la petición HTTP dada una url. En el siguiente fragmento de código vemos como dada una url nos devuelve un objeto de la clase "Document" con el contenido del HTML:

/**
 * Con este método devuelvo un objeto de la clase Document con el contenido del
 * HTML de la web que me permitirá parsearlo con los métodos de la librelia JSoup
 * @param url
 * @return Documento con el HTML
 */
public static Document getHtmlDocument(String url) {

    Document doc = null;
	try {
	    doc = Jsoup.connect(url).userAgent("Mozilla/5.0").timeout(100000).get();
	    } catch (IOException ex) {
		System.out.println("Excepción al obtener el HTML de la página" + ex.getMessage());
	    }
    return doc;
}

La clase "Document" es una clase que guarda el HTML obtenido de una petición y que posteriormente nos servirá para obtener el contenido entre etiquetas HTML y clases CSS.

Por último vamos a pasar a mostrar el código de scrapeo de las entradas de la página principal de Jarroba.com y posteriormente explicaremos sus detalles:

public class Scraping {
	
    public static final String url = "https://jarroba.com/";
	
    public static void main (String args[]) {
		
        // Compruebo si me da un 200 al hacer la petición
        if (getStatusConnectionCode(url) == 200) {
			
            // Obtengo el HTML de la web en un objeto Document
            Document document = getHtmlDocument(url);
			
            // Busco todas las entradas que estan dentro de: 
            Elements entradas = document.select("div.col-md-4.col-xs-12").not("div.col-md-offset-2.col-md-4.col-xs-12");
            System.out.println("Número de entradas en la página inicial de Jarroba: "+entradas.size()+"\n");
			
            // Paseo cada una de las entradas
            for (Element elem : entradas) {
                String titulo = elem.getElementsByClass("tituloPost").text();
                String autor = elem.getElementsByClass("autor").toString();
                String fecha = elem.getElementsByClass("fecha").text();
				
                System.out.println(titulo+"\n"+autor+"\n"+fecha+"\n\n");
				
                // Con el método "text()" obtengo el contenido que hay dentro de las etiquetas HTML
                // Con el método "toString()" obtengo todo el HTML con etiquetas incluidas
            }
				
        }else
            System.out.println("El Status Code no es OK es: "+getStatusConnectionCode(url));
    }
}

En primer lugar definimos la url para obtener el HTML de la misma. En segundo lugar comprobamos que el "status code" es igual a 200 y si es así obtenemos en un objeto de la clase "Document" el HTML de la web. Con ese objeto de la clase Document, vamos a poder obtener con los métodos pertinentes de JSoup, los elementos de la web. En este caso vamos a obtener todas las entradas que las guardaremos en un objeto de la clase "Elements" que corresponda a una serie de etiquetas y estilos CSS. En este caso guardaremos todos los fragmentos HTML que correspondan a cada entrada (ver primer imagen de la entrada):

// Obtengo el HTML de la web en un objeto Document
Document document = getHtmlDocument(url);
			
// Busco todas las historias de meneame que estan dentro de: 
Elements entradas = document.select("div.col-md-4.col-xs-12").not("div.col-md-offset-2.col-md-4.col-xs-12");

En este caso utilizamos el método "select" que lo que hace es coger todos los fragmentos del HTML que correpondan a un etique div, seguido de las clases 'col-md-4' y 'col-xs-12'. Dado que en la web hay otro fragmento de código que contiene la clase CSS 'col-md-4', seguido de otras clases y no corresponden a ninguna entra y por tanto no lo queremos seleccionar, le decimos con el método "not", que no nos seleccione ese fragmento de HTML. Ahora ya tenemos en una lista (entradas), todas las entradas en HTML. Ahora debemos de recorrer esa lista y obtener de esos fragmentos de código los datos del título, autor y fecha. De la misma forma esa información esta dentro de etiquetas HTML y clases CSS que debemos de tratar (como se muestra en la segunda imagen de la entrada). JSoup nos da los métodos para obtener el contenido pasandole el nombre de la clase CSS. Esto se hace de la siguiente forma:

// Paseo cada una de las entradas
for (Element elem : entradas) {
    String titulo = elem.getElementsByClass("tituloPost").text();
    String autor = elem.getElementsByClass("autor").toString();
    String fecha = elem.getElementsByClass("fecha").text();
				
    System.out.println(titulo+"\n"+autor+"\n"+fecha+"\n\n");
				
    // Con el método "text()" obtengo el contenido que hay dentro de las etiquetas HTML
    // Con el método "toString()" obtengo todo el HTML con etiquetas incluidas
}

Como vemos a los métodos "getElementsByClass" se les pasa el nombre de la clase CSS y te obtiene el contenido que hay entre esas etiquetas. Por último señalar lo que dejo en el comentario del fragmento de código anterior y esque no es lo mismo utilizar el método "toString()" y el método "text()", que este último si que es propio de la libreria JSoup y te da el contenido que hay entre las etiquetas HTML, mientras que el método "toString()" te da el contenido más el HTML. Veamos el resultado de la ejecución del programa para que veais el resultado:

Número de entradas en la página inicial de Jarroba: 9

Diccionario en python, con ejemplos
Por: Ricardo Moya
05-may-2015


DataSet de resultados de partidos de fútbol para su predicción (Machine Learning)
Por: Ricardo Moya
26-abr-2015


Intalar Ubuntu en una máquina virtual con VirtualBox (Video)
Por: Ricardo Moya
15-abr-2015

............
............

Esta libreria JSoup, es una libreria muy potente para hacer scraping. En esta entrada hemos visto como hacer una conexión HTTP y obtener el contenido HTML para almacenarlos en un objeto de la clase Dcoument para posteriormente sacar elementos o fragmentos de código HTML (clase Element o Elements si son varios) como si de un parseo con expresiones regulares se tratase pero sin meterse en el "engorro" de trabajar con expresiones regulares. JSoup también tiene métodos para sacar el contenido de etiquetas como los h1, h2, …, p, img, etc. que en este caso no hemos visto en el ejemplo pero que si revisais su javadoc veréis los métodos que permiten hacer esas acciones.

Para terminar dejo el programa que permite obtener todas las entradas de Jarroba.com (título, autor y fecha) y no solo los de la página principal como en el ejmplo mostrado:

package com.jarroba.scraping.Scraping_jarroba_java;

import java.io.IOException;

import org.jsoup.Connection.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Scraping_allPost {
	
    public static final String url = "https://jarroba.com/page/%s/";
    public static final int maxPages = 20;
	
	
    public static void main (String args[]) {
		
        for (int i=1; i<maxPages; i++){
			
            String urlPage = String.format(url, i);
            System.out.println("Comprobando entradas de: "+urlPage);
			
            // Compruebo si me da un 200 al hacer la petición
            if (getStatusConnectionCode(urlPage) == 200) {
				
                // Obtengo el HTML de la web en un objeto Document2
                Document document = getHtmlDocument(urlPage);
				
                // Busco todas las historias de meneame que estan dentro de: 
                Elements entradas = document.select("div.col-md-4.col-xs-12").not("div.col-md-offset-2.col-md-4.col-xs-12");
				
                // Paseo cada una de las entradas
                for (Element elem : entradas) {
                    String titulo = elem.getElementsByClass("tituloPost").text();
                    String autor = elem.getElementsByClass("autor").toString();
                    String fecha = elem.getElementsByClass("fecha").text();
					
                    System.out.println(titulo+"\n"+autor+"\n"+fecha+"\n");
					
                }
		
            }else{
                System.out.println("El Status Code no es OK es: "+getStatusConnectionCode(urlPage));
                break;
            }
        }
    }
	
	
    /**
     * Con esta método compruebo el Status code de la respuesta que recibo al hacer la petición
     * EJM:
     * 		200 OK			300 Multiple Choices
     * 		301 Moved Permanently	305 Use Proxy
     * 		400 Bad Request		403 Forbidden
     * 		404 Not Found		500 Internal Server Error
     * 		502 Bad Gateway		503 Service Unavailable
     * @param url
     * @return Status Code
     */
    public static int getStatusConnectionCode(String url) {
		
        Response response = null;
		
        try {
            response = Jsoup.connect(url).userAgent("Mozilla/5.0").timeout(100000).ignoreHttpErrors(true).execute();
        } catch (IOException ex) {
            System.out.println("Excepción al obtener el Status Code: " + ex.getMessage());
        }
        return response.statusCode();
    }
	
	
    /**
     * Con este método devuelvo un objeto de la clase Document con el contenido del
     * HTML de la web que me permitirá parsearlo con los métodos de la librelia JSoup
     * @param url
     * @return Documento con el HTML
     */
    public static Document getHtmlDocument(String url) {

        Document doc = null;

        try {
            doc = Jsoup.connect(url).userAgent("Mozilla/5.0").timeout(100000).get();
        } catch (IOException ex) {
            System.out.println("Excepción al obtener el HTML de la página" + ex.getMessage());
        }

        return doc;

    }
}
Comparte esta entrada en:
Safe Creative #1401310112503
Scraping en Java (JSoup), con ejemplos por "www.jarroba.com" esta bajo una licencia Creative Commons
Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.
Creado a partir de la obra en www.jarroba.com

14 comentarios en “Scraping en Java (JSoup), con ejemplos”

  1. Hola Jarroba, necesito hacer un post, a una pagina para logearme, pero el formato de los datos son JSON, y por mas que el .data(“FORMATO JSON”), me da error y no logra entrar. ¿Alguna idea de porque no puedo logearme? CUando no es JSON el formato de los datos del login, lo hago sin ningun tipo de problema.
    Gracias, un saludo

  2. Hola buenos noches estoy trabado de extraer datos de sitio tripAdvisor pero el problema esque no puedo extraer los datos de todas las paginas ya que esta de la siguiente manera ayuda por favor ..

    1

  3. Hola, ¿qué tal? me esta siendo de mucha ayuda tu ejemplo de scraping, me ha funcionado leyendo datos de otra página sin problemas, esto lo estoy empleando en una app que estoy haciendo con android studio.
    Pero el tema es el siguiente: mi app tiene varios botones, se conecta a la página de la tienda Ripley en Perú. Un botón debe obtener por ejemplo los datos de los televisores que tienen en tienda por marca, el segundo boton por función, etc. si apreto el primer botón me muestra los datos, pero si apreto el segundo botón ya no me muestra nada, imprimo en terminal la página web que debería estar leyendo, y esta es la correcta, ya que la copio y pego en mi explorador y muestra lo que debe mostrar, pero lo que obtengo como respuesta de response.statusCode () es 400, sabes ¿cómo puedo solucionar esto, o qué puede estar pasando? Gracias

  4. Hola Ricardo, estoy intentando hacer Scraping de mis datos de la web de Zara, para hacerme una app que me avise cada vez que compro algo.

    Intento hacer el login instantaneo y no lo consigo.¿podrías ayudarme?

    1. Hola Luis,

      Me imagino que ya es tarde y sabrás la respuesta, pero aún así voy a contestar por si acaso te ayuda.

      Fíjate en que, justo debajo del bucle "for", utiliza el método String.format. Este método sirve para construir Strings con parámetros.
      En esta cadena introduce "%s" para que el método String.format() sepa que ahí tiene que añadir un String.

      String url = "http://jarroba.com/page/%s/";

      En esta linea, en cada iteración, sustituye %s por el valor de i y crea la url completa.

      String urlPage = String.format(url, i);

       

      Un saludo

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies

ACEPTAR
Aviso de cookies