Automatizando las búsquedas en Google gracias a Python - BoomerNiX

martes, 30 de abril de 2019

Automatizando las búsquedas en Google gracias a Python

Día a día trabajamos con Google, ya no nos imaginamos la vida sin este buscador. Por ello hoy vamos a ver cómo crear nuestro buscador de Google a través de Python. En menos de 100 líneas se puede conseguir, y el resultado que obtendremos será Título de la página y su enlace.

¿Que necesitamos?
Python 3 con la librería requests y BeautifulSoup.

¿Cómo se ejecutará el buscador? 
Nosotros solo tendremos que agregar a la llama algunos argumentos:
  • La búsqueda (único obligatorio) 
  • La paginación máxima (por defecto solo se consulta la primera página, los 10 primeros resultados) 
  • El tiempo entre petición para Google (por defecto 2 segundos).
  • Fichero donde guardar el resultado (por defecto se muestra por pantalla) 

Alternativas o cambios en el script que pueden interesar 
Como siempre se dice, el límite es tu imaginación, pero por destacar algunos puntos.
  • En lugar de realizar una sola búsqueda, podemos automatizar la búsqueda de varias consultas. 
  • Mostrar también la descripción que da Google. 
  • Obtener los enlaces vídeos de youtube que se muestran en resultados. 

¿Cómo se ha implementado? 
La idea con la que se ha llevado a cabo el script es el siguiente: hacemos una búsqueda en Google con “google.com/search?q=”, en ella a través de la librería beautifulsoup buscamos los elementos, a nosotros nos interesan los enlaces que se encuentran dentro de un h3 con clase “r”, de ahí obtenemos los títulos y los enlaces (esto puede variar si cambia el html de Google, la última modificación nos hace buscar un div con clase "jfp3ef"). 

Figura 1: Petición a Google

Para agregar la paginación, se buscan los elementos a con clase “fl” y sacamos su valor del href, para hacer la petición, se debe saber que es una ruta relativa y necesitamos agregar la parte de google.com. 


Figura 2: Aplicando paginación

Por si no queda claro a que me refiero con la paginación de Google, lo que busco es los números que se ven en la figura 3. 
Figura 3: Paginación de Google


Cuando obtenemos los enlaces, podemos ver que nos encontramos al inicio con “/url?q=” y en ocasiones al final con “&sa=….”, por ello antes de guardar los enlaces, se hace una limpia del enlace. 


Figura 4: Limpiando las URLs
Y todo esto es utilizado en una función que se encarga de hacer el trabajo, no es muy larga, se muestra en la figura 5.

Figura 5: Función para tratar las búsquedas en Google
Quedaría por ver la parte en la que se recogen los argumentos (librería argparse) y la comprobación de guardar los resultados en un fichero, ya que no aportan valor. Para ejecutar el script, bastaría con las siguientes 2 líneas:

gs = GoogleSearch()
results = gs.request_data(args.search, pages=args.pages, pause=args.time)

Con esta explicación a alto nivel se puede entender el funcionamiento y/o crear nuestro propio script. 

Prueba de concepto 
A continuación, para que se pueda ver en funcionamiento, dejo un vídeo probando el script. 




Algunas limitaciones 
Puede llegar el momento en el que Google nos tache de robot y no nos deje trabajar bien, hasta el momento no he tenido el problema. Pero si llega el caso, siempre se puede tirar de Selenium.

Referencias a librería existentes 
Existen algunas librerías creadas, para Python puedes probar la siguiente: https://github.com/MarioVilas/googlesearch 

Por último, puedes comprobar cómo aplicamos en nuestro Business card reader esta búsqueda para sacar Curriculums de Google, en este post de elladodelmal. Realizar este script no lleva mucho tiempo, y nos da el poder de hacer lo que queramos. ¡Hasta pronto! 

7 comentarios:

  1. Saludos, Josué.
    Se presentó este problema al momento de compilar el código:
    Traceback (most recent call last):
    File "busquedas.py", line 59, in
    results = gs.request_data("Exploiting site:boomernix.com", 2, 2)
    File "busquedas.py", line 40, in request_data
    soup, results = self.__request(url)
    File "busquedas.py", line 11, in __request
    soup = BeautifulSoup(response.text, features="html.parser")
    TypeError: 'module' object is not callable

    Y por otra parte, la variable de clase domain en que momento le das valor o la inicializas como cadena vacía?

    Saludos.

    ResponderEliminar
    Respuestas
    1. Ya conteste en el otro comentario lo del error, pero lo del domain le doy el valor en el __init__ al igual que haces en el script del otro comentario, sorry! que se me paso ponerlo en el post.

      Eliminar
  2. import requests
    import BeautifulSoup
    import argparse

    class GoogleSearch:
    def __init__(self):
    self.domain = "https://www.google.com/"

    def __request(self, url):
    response = requests.get(url)
    print response
    soup = BeautifulSoup(response.text, 'html.parser')
    data = soup.find_all("h3", {"class":"r"})
    results = []
    if data:
    for d in data:
    a = d.findChildren("a")[0]
    link = self.__parse_url(a.get("href"))
    title = a.getText()
    if "/search?ie" in link:
    link = self.domain + link
    results.append({title:link})
    return soup, results

    def __pagination(self, soup, page):
    try:
    link = self.domain + self.__parse_url(soup.find_all("a",{"class":"fl"})[page].get("href"))
    except:
    link = None
    return link

    def __parse_ulr(self, url):
    try:
    return url.replace("/url?p=","").split("&sa=")[0]
    except:
    return url

    def request_data(self, search, pages = 1, pause = 2):
    print("Buscando...")
    print("Maxima paginacion: {}".format(pages))
    results = []

    url = self.domain + '/search?q=' + search
    soup, results = self.__request(url)
    total_results = soup.find(id="resultStats").getText()
    print("Resultados encontrados en google: {}".format(total_results))

    for i in range(1, pages):
    sleep(pause)
    url = self.__pagination(soup, i)
    if url is None:
    break
    s, r = self.__request(url)
    results.extend(r)
    print("Total de resultados: {}".format(len(results)))

    return results

    gs = GoogleSearch()
    results = gs.request_data("Exploiting site:redes neuronales", 2, 2)



    Agradecería mucho si me pudieras explicar en donde está mi error, gracias!!!!

    Me aparece este error:
    Buscando...
    Maxima paginacion: 2

    Traceback (most recent call last):
    File "busquedas.py", line 60, in
    results = gs.request_data("Exploiting site:redes neuronales", 2, 2)
    File "busquedas.py", line 44, in request_data
    soup, results = self.__request(url)
    File "busquedas.py", line 12, in __request
    soup = BeautifulSoup(response.text)
    TypeError: 'module' object is not callable

    ResponderEliminar
    Respuestas
    1. A simple vista (teniendo en cuenta Python3), el problema viene del import de BeautifulSoup, cambialo por el siguiente "from bs4 import BeautifulSoup". Te falta también un import >> "from time import sleep".

      La siguiente línea te va a producir un error si find no devuelve resultados: "total_results = soup.find(id="resultStats").getText()". Usa primero el find y comprueba si hay algo antes de usar getText.

      Luego al ser un script para scraping, si cambian los elementos de la web, se necesita ir actualizando la búsqueda, con Google ya me ha pasado en 2 ocasiones.

      Perdón por tardar en contestar, agosto es un mes muy malo.
      Un saludo

      Eliminar
  3. Hola Josué. Gracias por el post. Estoy intentando hacerlo. Hago la misma búsqueda que haces en el vídeo. "Exploiting site:boomernix.com". A ti te devuelve 146 resultados. Si busco directamente en Gloogle hoy me devuelve 149. Pero cuando ejecuto el programa no me devuelve ningún resultado. ¿Que estoy haciendo mal? Te pego el código. Creo que es el mismo que el que has puesto en el post, excepto que controlo si no me devuelve resultados para que no rompa el programa. Muchas gracias por tu ayuda.

    import requests
    from bs4 import BeautifulSoup
    from time import sleep
    import argparse

    class GoogleSearch:
    def __init__(self):
    self.domain = "https://www.google.com/"

    def __request(self, url):
    response = requests.get(url)
    print (response)
    soup = BeautifulSoup(response.text, 'html.parser')
    data = soup.find_all("h3", {"class":"r"})
    results = []
    if data:
    for d in data:
    a = d.findChildren("a")[0]
    link = self.__parse_url(a.get("href"))
    title = a.getText()
    if "/search?ie" in link:
    link = self.domain + link
    results.append({title:link})
    return soup, results

    def __pagination(self, soup, page):
    try:
    link = self.domain + self.__parse_url(soup.find_all("a",{"class":"fl"})[page].get("href"))
    except:
    link = None
    return link

    def __parse_ulr(self, url):
    try:
    return url.replace("/url?q=","").split("&sa=")[0]
    except:
    return url

    def request_data(self, search, pages = 1, pause = 2):
    print("Buscando...")
    print("Maxima paginacion: {}".format(pages))
    results = []

    url = self.domain + '/search?q=' + search
    soup, results = self.__request(url)
    total_results = soup.find(id="resultStats")
    if total_results == None:
    print ("No hay resultados para esta palabra")
    else:
    total_results_text = total_results.getText()
    print("Resultados encontrados en google: {}".format(total_results_text))

    for i in range(1, pages):
    sleep(pause)
    url = self.__pagination(soup, i)
    if url is None:
    break
    s, r = self.__request(url)
    results.extend(r)
    print("Total de resultados: {}".format(len(results)))

    return results

    gs = GoogleSearch()
    results = gs.request_data("Exploiting site:boomermix.com", 2, 2)

    ResponderEliminar
  4. Referente a mi comentario anterior. La variable soup si tiene datos pero cuando ejecuto esta línea:

    data = soup.find_all('h3', {'class':'r'})

    parece que no encuentra nada y data esta vacío.

    Por cierto, en el código que he pegado en el comentario anterior la url estaba mal era "Exploiting site:boomernix.com" y no "Exploiting site:boomermix.com"

    ResponderEliminar
    Respuestas
    1. Hola Emilio, al ser un scraping a las búsquedas de Google, el código de la web cambia, y por ello es posible que la línea en que se busca h3 con clase r se vea afectada.Puedes hacer una pequeña búsqueda del código que devuelve en el interprete de Python:

      import requests

      r = requests.get("https://www.google.com/search?q=github")
      r.text


      Y finalmente actualizas las partes de find en el código.
      Un saludo

      Eliminar