Crawl avec Selenium

Selenium permet de piloter plusieurs navigateurs. Créé à l'origine pour faire des test sur des sites de façon automatique, il permet aussi d'autres usages comme le crawl ou l'extraction de données.

Dans ce test, nous allons automatiser la fastidieuse tâche de vérification des pages indexées d'un site sur un moteur de recherche. Normalement, on peut connaître les pages indexées à l'aide de la commande "site:ndd.tld" (toutes les pages) et "site:ndd.tld/&" (index principal). Seulement, passer les pages de résultats une à une et récupérer les URLs des pages, n'est pas passionnant. Voici une application qui fait ce travail... en partie.

En effet, il y a un petit hic, Google ne donne pas toutes les pages indiquées. Si par exemple la commande site: annonce 500 résultats, il est fort probable qu'à partir de vous n'ayez accès qu'à une partie des pages. En tout cas cela reste utile pour connaître les principales pages du site.


package com.tirop.ggindex;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

public class GGIndex
{
	/*
	 * Souvent Google propose de s'inscrire aux Webmaster Tools
	 * Cette String permet de reconnaître ce type de liens
	 */
	private static final String WMT = "http://www.google.com/intl/gl/webmasters/";
	private static final long WAIT_4_CAPTCHA = 5000;
	 
	public static void main(String[] args)
	{ 
		GGIndex g = new GGIndex();
		String domain = "tirop.com";
		// Nombre de SERPs, domaine, et sufixe du fichier texte à écrire
		g.getURLsFromGG(30, domain, "_site.txt");
	}

	public void getURLsFromGG(int numberOfSERP, String kw, String outFileName)
	{
		ArrayList listAll = new ArrayList();
		ArrayList listPrimary = new ArrayList();
		ArrayList listSecondary = new ArrayList();
		
		// On lancera Firefox
		WebDriver driver = new FirefoxDriver();

		try
		{ 
//			KW
//			-----------------------------------
			String kwAll = "site:" + kw;
			String kwPrimary;
			
			if (kw.endsWith("/"))
			{
				kwPrimary = "site:" + kw + "&";
			}
			else
			{
				kwPrimary = "site:" + kw + "/&";
			}
			

//			ALL
//			-----------------------------------			
			driver.get("http://www.google.fr"); 
			
			sleep(2000L); 				// Laisser le temps de charger la page avant d'attaquer le DOM 
			checkIfSorry(driver); 		// Google peut afficher un captcha s'il pense qu'il est consulté par un robot 
			WebElement element = driver.findElement(By.name("q"));// Trouver le formulaire 
			element.clear(); 			// Vider le formulaire 
			element.sendKeys(kwPrimary);// Remplir avec notre requête 
			element.submit();			// Envoyer

			int i = 1;  
			WebElement next;			// Bouton suivant
			
			while (i < numberOfSERP)
			{
				checkIfSorry(driver);
				sleep(2000L);
				extractH3R(driver, listPrimary);	// Ajouter dans la liste les liens trouvés
				sleepRandom(5000L);
				next = getNextSERP(driver, i);		// Aller chercher la page suivante
				if (next == null)
				{
					i = numberOfSERP; 
				}
				else
				{
					next.click();
				}
				i++; 
			} 


//			SECONDARY
//			-----------------------------------	
			i = 1;
			
			WebElement reSearchDuplicate = driver.findElement(By.cssSelector("#ofr a"));
			
			if (reSearchDuplicate != null)
			{
				System.out.println("\nRefaire la recherche avec des résultats dupliqués ...");
				reSearchDuplicate.click();
				
				while (i < numberOfSERP)
				{
					checkIfSorry(driver);
					sleep(2000L);
					extractH3R(driver, listAll);
					sleepRandom(5000L);
					next = getNextSERP(driver, i);
					if (next == null)
					{
						i = numberOfSERP; 
					}
					else
					{
						next.click();
					}
					i++;
					System.out.println("All : " + listAll.size());
				}
			}
			else
			{
				System.out.println("\nRefaire la recherche ...");
				WebElement elementSERP = driver.findElement(By.name("q"));
				elementSERP.clear();
				elementSERP.sendKeys(kwAll);
				elementSERP.submit();
				i = 1;
			
				while (i < numberOfSERP)
				{
					sleep(2000L);
					extractH3R(driver, listAll);
					sleepRandom(5000L);
					next = getNextSERP(driver, i);
					if (next == null)
					{
						i = numberOfSERP; 
					}
					else
					{
						next.click();
					}
					i++;
					System.out.println("All : " + listAll.size());
				}
			}

			System.out.println("\nAll : " + listAll.size() + "\n------------------------------------");
			for (String s : listAll)
			{
				System.out.println(s);
				
				if (listPrimary.contains(s))
				{
//					System.out.println("listPrimary.contains " + s);
				}
				else
				{
//					System.out.println("listSecondary.add " + s);
					listSecondary.add(s);
				}
			}

			System.out.println("\nPrimary : " + listPrimary.size() + "\n------------------------------------");
			for (String s : listPrimary)
			{
				System.out.println(s);
			}


			System.out.println("\nSecondary : " + listSecondary.size() + "\n------------------------------------");
			for (String s : listSecondary)
			{
				System.out.println(s);
			}
			
		}
		catch (StaleElementReferenceException d)
		{
			d.printStackTrace();
		}

		FileWriter fw;
		try
		{
			fw = new FileWriter(kw + outFileName);
			for (String h : listPrimary)
			{
				fw.write(h);
				fw.write(System.getProperty("line.separator"));
			}
			fw.close();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	private void checkIfSorry(WebDriver driver)
	{
		if (driver.getCurrentUrl().startsWith("http://sorry.google.com/"))
		{
			System.out.println("Captcha ! ... aïe " + WAIT_4_CAPTCHA + " seconds");
			try
			{
				Thread.sleep(WAIT_4_CAPTCHA);
				checkIfSorry(driver);
				System.out.println("Captcha solved !");
			}
			catch (InterruptedException e)
			{ 
			}
		}
	}

	private WebElement getNextSERP(WebDriver driver, int iteration)
	{
		try
		{
			WebElement next = driver.findElement(By.id("pnnext"));
			
			if (next != null)
			{
				return next;
			}
		}
		catch (NoSuchElementException n)
		{
			System.out.println("pas de bouton next ...");
			List navendList = driver.findElements(By
					.cssSelector(".navend .pn"));
			System.out.println("\ngetNextSERP\n---------------------");
			
			if (navendList.size() == 0)
			{
				System.out.println("\tnavend == 0");
				return null;
			}
			
			if (iteration == 1)
			{
				System.out.println("\titeration == 1, returning navend 0");
				return navendList.get(0); 
			}
			else
			{
				System.out.println("\titeration != 1, " + iteration);
				if (navendList.size() > 1)
				{
					System.out.println("\titeration != 1, " + iteration);
					navendList.get(1);
				}
			}
		} 
		
		return null;
	}

	public void sleepRandom(Long millis)
	{
		try
		{
			Long l = (long) (millis + (Math.random() * millis));
			System.out.println("sleep random : " + l);
			Thread.sleep(l);
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}

	private void sleep(Long millis)
	{
		try
		{
			Thread.sleep(millis);
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}

	private void extractH3R(WebDriver driver, ArrayList hrefList)
	{
		List linkElements = driver.findElements(By
				.cssSelector("h3.r a"));

		for (WebElement link : linkElements)
		{
			if (!link.getAttribute("href").startsWith(WMT))
			{
				System.out.println(link.getAttribute("href"));
				hrefList.add(link.getAttribute("href"));
			}
			else
			{
				System.out.println("WMT : " + link.getAttribute("href"));
			}
		}
	} 
}