• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

The Machine Learners

Descubriendo la IA

  • Inicio
  • Libros ML
  • Contacto
  • Sobre Nosotros

Cómo extraer datos de Instagram usando técnicas de Web scrapping

En este post hablaremos de cómo automatizar la recogida de datos de Instagram. Aplicaremos técnicas de Web Scrapping implementadas con Python para extraer imágenes, comentarios y likes de Instagram buscando por un hashtag.

Se trata por tanto de un artículo técnico, para el que se debe tener nociones básicas de HTML y programación en Python.

Cuando estamos entrenando un algoritmo de Machine Learning o visualizando un problema muchas veces vemos que nos hace falta más información para completar o enriquecer el análisis. Es aquí donde entra en juego las técnicas de recogida de datos de fuentes externas, usando metodologías como el Web Scrapping.

Índice

  • 1 ¿Qué es el Web Scrapping?
  • 2 Librerías de Web Scrapping
  • 3 Funciones
    • 3.1 Cargar Instagram
    • 3.2 Inicio de sesión
    • 3.3 Búsqueda de información
      • 3.3.1 Imágenes
      • 3.3.2 Número de likes
      • 3.3.3 Comentarios
  • 4 Procesamiento de la información
  • 5 Descarga de la información

¿Qué es el Web Scrapping?

Antes de comenzar vamos a introducir brevemente este concepto que hemos utilizado en la introducción. Se trata de un tecnicismo que anglosajón cuya traducción literal es «escarbado o raspado web».

Es decir, son técnicas que automatizan la extracción de información de las páginas web. Básicamente se trata de programas, o scripts, que simulan el comportamiento humano navegando por la web.

Por tanto, mediante estas técnicas programamos los pasos que seguiríamos nosotros en la web, de forma que se pueda ejecutar por una máquina mucho más rápido.

Librerías de Web Scrapping

Como hemos comentado usaremos como lenguaje base Python. Además usaremos librerías que nos facilitan la implementación de estas técnicas de Web Scrapping:

  • Selenium: librería que facilita el uso de un Webdriver, el cuál se utiliza para simular acciones de un ser humano en una web, tales como un click.
  • Chromedriver: herramienta de código abierto que proporciona la ejecución de las funciones sobre el navegador Google Chrome. Será necesario descargarnos un ejecutable y guardarlo en una carpeta al que apuntaremos en nuestro programa Python. En este link podéis descargaros dicho ejecutable. Usaremos una variable de entorno para definir ese path, por ejemplo:
chromedriver_path = '/Users/Ignacio/documents/chromedriver'

IMPORTANTE: deberemos tener instalado Google Chrome para que se pueda ejecutar todo correctamente.

Ahora ya sí copio el import de las librerías necesarias:

#Importing all needed libraries
from selenium import webdriver
import time
import os
import time
import requests
from pprint import pprint
import pandas as pd

import json
from lxml import html
import re
import csv
import numpy as np

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import (
    NoSuchElementException,
    TimeoutException,
    WebDriverException,
    )

import pandas as pd

Funciones

En esta sección vamos a detallar las funciones implementadas para la extracción de la información. En todo momento vamos a simular el comportamiento o la interacción de un humano de forma automátizada.

Cargar Instagram

Esta función carga la página de Instagram. Nada más cargar la página nos salta un pop-up preguntándonos si aceptamos las cookies o no, tal y cómo se muestra en la siguiente imagen.

Implementamos o simulamos el click en aceptar con código:

 # Accept cookies
    cookies = WebDriverWait(browser, WAIT_TIME_3).until(
        EC.element_to_be_clickable((By.XPATH,'//button[contains(text(), "Aceptar")]'))).click()

Y esta es la función completa con el código que acepta las cookies incluído:

def load_instagram():
    """
    Function to initialize Instagram and launch it in a browser using Selenium
    """
    # Chrome driver should be in
    executable_path=os.path.join(chromedriver_path)

    options = webdriver.ChromeOptions()
    options.add_argument('--ignore-certificate-errors')
    options.add_argument('--disable-notifications')

    # 1-Allow, 2-Block, 0-default
    preferences = {
        "profile.default_content_setting_values.notifications" : 2,
        "profile.default_content_setting_values.location": 2,
        # We don't need images, only the URLs.
        "profile.managed_default_content_settings.images": 2,
        }
    options.add_experimental_option("prefs", preferences)


    browser = webdriver.Chrome(
        executable_path=executable_path,
        chrome_options=options,
        )
    browser.wait = WebDriverWait(browser, WAIT_TIME)   
    
    #Opening the browser and getting the url

    url = "https://www.instagram.com/"
    browser.get(url)
    
    #wait 5 seconds to load
    time.sleep(WAIT_TIME_2)
    
    # Accept cookies
    cookies = WebDriverWait(browser, WAIT_TIME_3).until(
        EC.element_to_be_clickable((By.XPATH,'//button[contains(text(), "Aceptar")]'))).click()
    
    return browser

Inicio de sesión

Una vez aceptamos las cookies nos sale la página de inicio de sesión donde deberemos introducir nuestras credenciales.

Para ello hemos implementado la siguiente función:

def instagram_login(driver):
    """
    Login to Instagram using username and password.
    """
    usr = driver.find_element_by_name("username")
    usr.send_keys(USERNAME)
    password = driver.find_element_by_name("password")
    password.send_keys(PASSWORD)
    password.send_keys(Keys.RETURN)
    time.sleep(WAIT_TIME_2)

Donde USERNAME y PASSWORD son variables genéricas definidas anteriormente. Debemos especificar nuestro usuario y contraseña en formato string. Un ejemplo:

PASSWORD = '123456'
USERNAME = '[email protected]'

Búsqueda de información

El objetivo principal es recoger toda la información asociada a un hastag, es decir, imágenes y comentarios, y almacenarlo en local.

Por tanto, para la búsqueda por hashtag deberemos introducirlo en la caja de búsqueda una vez hayamos iniciado sesión. Esta se encuentra en la parte superior:

Como se puede apreciar, aparece un texto Busca en dicha caja. Para saber como encontrarlo es necesario inspeccionar el código fuente HTML de la página. Si hacemos click derecho sobre Busca->Inspeccionar, nos aparecerá el siguiente recuadro lateral:

Se puede apreciar como la etiqueta input contiene un atributo placeholder=»Busca». Esto lo usaremos para encontrar con código dicha caja de búsqueda, implementándolo de la siguiente forma:

 # Get the search box
    searchbox = WebDriverWait(browser,WAIT_TIME).until(
        EC.element_to_be_clickable((By.XPATH, "//input[@placeholder='Busca']")))
    
    searchbox.clear()

Una vez estamos situados y hemos limpiado el texto, podemos proceder a escribir el hashtag objetivo, que lo denominamos keyword:

   # Search by tag
    searchbox.send_keys(keyword)
    time.sleep(WAIT_TIME_3)
    searchbox.send_keys(Keys.ENTER)
    time.sleep(WAIT_TIME_3)
    searchbox.send_keys(Keys.ENTER)
    time.sleep(WAIT_TIME_3)

NOTA: se ha implementado dos veces el click sobre enter ya que la primera vez posiciona el cursor en la primera lista del desplegable y el segundo ejecuta la búsqueda en sí.

Imágenes

Una vez procedemos a buscar, nos aparecen numerosas imágenes como se muestra en la siguiente figura. Además hemos pulsado click derecho->inspeccionar sobre una imagen para analizar los atributos.

Se puede apreciar como la etiqueta <img> tiene un atributo llamado src, que contiene la url de la imagen en cuestión. Por tanto con el siguiente código recuperamos todas las url de las imágenes que aparecen en pantalla.

images = browser.find_elements_by_tag_name('img')
images = [image.get_attribute('src') for image in images]

images = images[1:-2] #slicing-off first photo, IG logo and Profile picture

Número de likes

Una vez disponemos de todas las urls de las imágenes, tenemos que ir pinchando foto a foto para que aparezcan el número de likes. Si pinchamos en una imagen cualquiera se abre la siguiente ventana:

Para ejecutar cada click con código usaremos el siguiente código, donde image será cada url:

# Click and open posts 
browser.execute_script("arguments[0].click();",
                        browser.find_element_by_xpath('//img[@src="'+str(image)+'"]'))
time.sleep(WAIT_TIME_5)

Al igual que en el caso anterior, vamos a inspeccionar para ver que etiquetas podemos usar como referencia en el scrapping:

Vemos como el caso más habitual se trata de una etiqueta <div> con clase Nm9Fw con una etiqueta <span> por debajo. Por tanto usaremos dicha clase de referencia mediante llamadas con estilo CSS:

try:
        el_likes = browser.find_element_by_css_selector(".Nm9Fw > * > span").text
                      
except Exception as e:
        try:
            el_likes = browser.find_element_by_css_selector(".Nm9Fw > button").text
        
        except Exception as e2:
            try:
                el_likes = browser.find_element_by_css_selector(".vcOH2").text
            except Exception as e3:
                print(f"ERROR - Could not fetch like  {e3}")  

NOTA: hemos comprobado que en algunos casos en lugar de etiqueta span, aparece un button. También en casos muy puntuales hemos visto que los likes aparecen directamente en una etiqueta de clase vcOH2.

Una vez extraídos los likes, es posible que la imagen no tenga ningún like, o que en lugar de imagen sea un vídeo y aparezcan número de reproducciones. Por tanto, es necesario una limpieza de datos ex-post para quedarnos únicamente con el número:

# Transform the text when there are no Likes
if el_likes == "indicar que te gusta esto":
        el_likes = '0'
        
# Clean the info to only retrieve numbers instead of text    
if "Me gusta" in str(el_likes) or "reprodu" in str(el_likes):
        el_likes = el_likes[:1]
        

Comentarios

Al igual que con los likes es necesario pulsar en cada imagen usando el siguiente código:

# Click and open posts 
browser.execute_script("arguments[0].click();",
                        browser.find_element_by_xpath('//img[@src="'+str(image)+'"]'))
time.sleep(WAIT_TIME_5)

Inspeccionamos la imagen:

Vemos que sobre la clase C4VMK cuelgan diferentes etiquetas <span > con los comentarios. Por tanto usamos el siguiente código que nos devuelve una lista con todos los comentarios:

try:
        comment_elements = browser.find_elements_by_css_selector(".eo2As .gElp9 .C4VMK")
        comment = [element.find_elements_by_tag_name('span')[1].text for element in comment_elements]
      
except Exception as e:  
        print(f"ERROR - Could not fetch comment  {e}") 
        

Procesamiento de la información

En este punto ya hemos obtenido todos los likes, urls de las imágenes y comentarios. Por tanto, estamos en disposición de procesar la información para almacenarla en local.

Básicamente vamos a estructurar la información en un Pandas Dataframe, de forma que el Título del post sea el primer comentario. Además separamos los hashtags principales en otra columna. En la siguiente imagen, mostramos lo que hemos considerado como hashtags principales:

La función implementada para llevar a cabo todo este procesado es la siguiente:

def process_info(insta_info):
    """
    Function that process the info retrieved from posts
    
    """
    
    df = pd.DataFrame(insta_info)
    
    # The first comment is considered as a title
    df["Title"]    = [i[0] for i in list(df["Comments"])] 
    df["Comments"] = [i[1:] for i in list(df["Comments"])]
    
    # Get all the hashtags from the title
    df["Principal Hashtags"] = [ re.findall("#(\w+)", title)  for title in list(df["Title"])]
    
    return df

El dataframe resultado final tiene la siguiente forma:

Descarga de la información

Finalmente vamos a proceder a descargar en local toda la información obtenida. Por un lado las imágenes en formato .jpg y por otro el dataframe con comentarios likes, etc anterior en formato excel.

df["Download name"] = download_images(keyword,list(df["Image URL"]))
df.to_csv(str(keyword)+'.csv',index=False, header=True)

La función download_images es la siguiente:

def download_images(keyword,images):
    """
    Function used to download  images
    """
    fpath = os.getcwd()
    fpath = os.path.join(fpath, keyword[1:])
  
    if(not os.path.isdir(fpath)):
        os.mkdir(fpath)
    
    #download images
    counter = 0
    image_name_lst = []
    for image in images:
        persist_image(fpath,image,counter,image_name_lst)
        counter += 1
    return image_name_lst

def persist_image(folder_path:str,url:str, counter,image_name_lst):
    """
    Function used to persist physically in localhost an image from an url
    """
    try:
        image_content = requests.get(url).content

    except Exception as e:
        print(f"ERROR - Could not download {url} - {e}")

    try:
        img_name =  'jpg' + "_" + str(counter) + ".jpg"
        f = open(os.path.join(folder_path,img_name), 'wb')
        f.write(image_content)
        f.close()
        print(f"SUCCESS - saved {url} - as {folder_path}")
        image_name_lst.append(img_name)
    except Exception as e:
        print(f"ERROR - Could not save {url} - {e}")

Espero que os haya gustado! Si tenéis cualquier duda o necesitáis alguna explicación del código más en detalle, no dudéis en poner un comentario!

Reader Interactions

Comments

  1. Grevall says

    19/05/2021 at 7:15 PM

    Buen articulo. ¿Hay manera de extraer correos electrónicos de ciertas publicaciones? , de igual forma si tienes un consolidado de todos los códigos que usaste o el proyecto en tu Git hub, podrías compartirlo?

    saludos.

    Responder
    • Ignacio Ramos Garcia says

      21/05/2021 at 12:22 PM

      Muchas gracias por el comentario! El código completo lo puedes encontrar en mi github: https://github.com/iramosgarcia/instagram-scrapping/blob/main/Instagram.ipynb.

      Respecto a la pregunta de correos electrónicos no hemos trabajado con datos de usuarios. Te animo a que aplicando las técnicas de exploración explicadas en el post, investigues la etiqueta HTML donde se encuentra la información que buscas.

      Responder
  2. María Andrea Jiménez says

    31/12/2021 at 11:29 AM

    Hola! Mil gracias por compartir tu trabajo, esto es justo lo que estaba buscando. Solo debo agregar para extraer los Shares, pero el resto me sirve perfectamente. Ahora bien, me está saltando un error al final del código cuando corres la función principal de «scrapping_instagram», y al tratar de visualizar el «df» no existe porque por el error no se crea. Te comparto el error que me salta por si pudieras ayudarme:

    Could not fetch like Message: no such element: Unable to locate element: {«method»:»css selector»,»selector»:».vcOH2″}

    Luego me salen otros errores pero creo que este es el principal. Por lo demás, me funciona ingresar a chrome, abrir instagram, aceptar las cookies, ingresar, buscar el hashtag, abrir los posts, e irse moviendo en cada uno.

    Gracias nuevamente!

    Responder
  3. Miguel says

    22/01/2022 at 10:11 AM

    Buenas tardes, muy buena publicacion. ¿Sabes si hay alguna manera de saber si ya has dado a Me Gusta a esa publicacion?

    Muchas gracias

    Responder

Deja una respuesta Cancelar la respuesta

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

Primary Sidebar

Suscríbete

Síguenos en Medium.

Entradas Recientes

  • Estructuras de Datos en Python
  • Variables en Python: Todo lo que necesitas saber
  • Mejor editor de código para Python
  • Instala Anaconda para tus proyectos de Data Science
  • Qué es Python – El lenguaje de la ciencia de datos

Copyright © 2022 The Machine Learners