Cómo extraer datos de Indeed con Python

Aprenda a extraer ofertas de empleo de Indeed utilizando Python, a gestionar los retos anti-bot y a optimizar la recopilación de datos de forma eficiente.
16 min de lectura
How to Scrape Indeed blog image

En este artículo descubrirás:

  • Qué es un Scraper de Indeed y cómo funciona
  • Los tipos de datos que puede extraer automáticamente de Indeed
  • Cómo crear un script de scraping de Indeed utilizando Python
  • Cuándo y por qué puede necesitar una solución más avanzada

¡Empecemos!

¿Qué es un Scraper de Indeed?

Un Scraper de Indeed extrae automáticamente ofertas de empleo y datos relacionados del sitio web de Indeed. Funciona imitando las interacciones humanas para navegar por las páginas de búsqueda de empleo. A continuación, identifica elementos específicos como puestos de trabajo, empresas, ubicaciones y descripciones. Por último, elbot de scrapingextrae los datos y los exporta para su análisis.

Datos que puedes encontrar en Indeed

Indeed es un tesoro de datos relacionados con el empleo, que pueden ser muy valiosos para el análisis de mercado, la contratación o la investigación. A continuación se muestra una lista de los puntos de datos clave que puede extraer de él:

  • Puestos de trabajo: la función o el puesto anunciado en la oferta.
  • Nombres de empresas: detalles del empleador, incluidos los perfiles de las empresas.
  • Ubicaciones: la ciudad, el estado o el país donde se encuentra el puesto de trabajo.
  • Descripciones de los puestos: información detallada sobre la función, las responsabilidades y los requisitos.
  • Rangos salariales: escalas salariales anunciadas (si están disponibles).
  • Tipos de empleo: a tiempo completo, a tiempo parcial, por contrato, prácticas, etc.
  • Fechas de publicación: fecha en la que se publicó la oferta de empleo.
  • Etiquetas y atributos: Palabras clave como «Contratación urgente» o «Remoto».
  • Valoraciones y opiniones: valoraciones de los empleadores y comentarios de los empleados.
  • Opciones de solicitud: Indicadores como la disponibilidad de «Solicitud fácil».

Si te centras en los puestos de trabajo, sigue nuestra guía sobre cómo extraer anuncios de empleo.

Cómo extraer datos de Indeed: guía paso a paso

En esta sección del tutorial, verás cómo crear un Scraper de Indeed. Se te guiará a través del proceso de creación de un script de Python para extraer la página de ofertas de empleo de «científico de datos» de Indeed:

The data scientist job posting page on Indeed

¡Siga las instrucciones y aprenda a extraer datos de Indeed!

Paso n.º 1: Configuración del proyecto

Antes de empezar, asegúrate de tener Python 3 instalado en tu ordenador. Si no es así, descárgalo e instálalo.

Ahora, ejecuta el siguiente comando en la terminal para crear un directorio para tu proyecto:

mkdir indeed_scraper

indeed_scraper contendrá tu Scraper de Python para Indeed.

Introdúzcalo en la terminal e inicialice un entorno virtual dentro de él:

cd indeed_scraper
python -m venv env

A continuación, carga la carpeta del proyecto en tu IDE de Python favorito. Visual Studio Code con la extensión Python y PyCharm Community Edition son buenas opciones.

Crea un archivo scraper.py en el directorio del proyecto, que ahora debería contener esta estructura de archivos:

the new scraper.py file in the project directory

scraper.py pronto contendrá la lógica de scraping deseada.

Es hora de activar el entorno virtual en la terminal del IDE. En Linux o macOS, hazlo con este comando:

./env/bin/activate

De forma equivalente, en Windows, ejecuta:

env/Scripts/activate

¡Genial! Ya tienes un entorno Python para el Scraping web de Indeed.

Paso n.º 2: elegir la biblioteca de scraping adecuada

El siguiente paso es determinar si Indeed se basa en páginas dinámicas o estáticas. Para ello, abra la página de destino de Indeed en modo incógnito con su navegador y empiece a jugar con ella. Como puede ver fácilmente, la mayoría de los datos de la página se cargan dinámicamente:

An example showing that most of the data is loading dynamically

Eso es suficiente para decir que necesitas una herramienta de automatización del navegador como Selenium para extraer datos de Indeed de forma eficaz. Para obtener más información sobre este proceso, lee nuestra guía sobre el Scraping web con Selenium.

Selenium le permite controlar mediante programación un navegador web para simular las interacciones del usuario y extraer el contenido renderizado por JavaScript. ¡Es hora de instalarlo y empezar a utilizarlo!

Paso n.º 3: Instalar y configurar Selenium

En un entorno virtual activado, ejecuta el siguiente comando para instalar Selenium:

pip install -U selenium

Importe Selenium en scraper.py y configure un objeto WebDriver:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

# Configure una instancia de Chrome controlable
driver = webdriver.Chrome(service=Service())

El código anterior inicializa lo que necesita para controlar una instancia de Chrome.

Nota: Indeed ha implementado medidas antiscraping para impedir que los navegadores sin interfaz gráfica accedan a sus páginas. Por lo tanto, configurar el indicador --headless haría que su script fallara. Como alternativa, eche un vistazo a Playwright Stealth.

Como última línea de tu script, no olvides cerrar el controlador web:

driver.quit()

¡Genial! Ya tienes todo configurado para extraer datos de Indeed.

Paso n.º 4: Visita la página de destino

Con el método get() de Selenium, indica al navegador controlado que visite la página de destino:

driver.get("https://www.indeed.com/jobs?q=data+scientist&l=New+York%2C+NY&from=searchOnHP%2Cwhatautocomplete&vjk=45d1ba700870fbef")

scraper.py ahora contendrá las siguientes líneas de código:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

# Configurar una instancia controlable de Chrome
driver = webdriver.Chrome(service=Service())

# Abrir la página de destino en el navegador
driver.get("https://www.indeed.com/jobs?q=data+scientist&l=New+York%2C+NY&from=searchOnHP%2Cwhatautocomplete&vjk=45d1ba700870fbef")

# Scraping logc...

# Cerrar el controlador web
driver.quit()

Añade un punto de interrupción de depuración en la última línea. Ejecuta el script con el depurador y esto es lo que deberías ver:

What you should see after running the script with the debugger

Nota: La notificación «Chrome está siendo controlado por un software de pruebas automatizado» le indica que Selenium está controlando Chrome como se esperaba.

¡Bien hecho!

Paso n.º 5: Seleccionar los elementos de la oferta de empleo

La página de búsqueda de empleo de Indeed muestra numerosas ofertas de trabajo. Dado que nuestro objetivo es extraerlas todas, comience por inicializar una matriz para almacenar los datos extraídos:

jobs = []

A continuación, inspeccione los elementos HTML de las ofertas de empleo de la página para comprender cómo seleccionarlos:

The HTML elements of the job openings

Aquí, cada elemento de empleo es un nodo slider_item dentro del contenedor #mosaic-provider-jobcards.

Normalmente, se utilizarían clases CSS para seleccionar elementos de la página. Sin embargo, estas clases parecen generarse aleatoriamente, probablemente en el momento de la compilación. Para garantizar la estabilidad, es mejor seleccionar los atributos id y data-testid, que son menos propensos a cambiar con frecuencia.

Confíe en Selenium para seleccionar los elementos de empleo:

jobs_container_element = driver.find_element(By.CSS_SELECTOR, "#mosaic-provider-jobcards")
job_elements = jobs_container_element.find_elements(By.CSS_SELECTOR, "[data-testid="slider_item"]")

El método find_elements() aplica la estrategia de selección especificada para recuperar todos los elementos coincidentes de la página. En este caso, la estrategia de selección es un selector CSS.

Asegúrate de importar By para que esto funcione:

from selenium.webdriver.common.by import By

Ahora, itere sobre los elementos seleccionados y prepárese para extraer datos de cada uno de ellos:

for job_element in job_elements:
    # extraer datos de cada oferta de trabajo

¡Fantástico! Ya estás listo para empezar a extraer puestos de trabajo de Indeed.

Paso n.º 6: extraiga la información principal del puesto de trabajo

Inspecciona un elemento de tarjeta, centrándote en la información de la sección superior de la tarjeta:

The HTML of the job title element

Aquí puedes ver que puedes extraer:

  • El título del puesto de trabajo de <h2>
  • La URL de la página del puesto de trabajo del elemento <a> dentro del título <h2>
  • El nombre de la empresa del nodo [data-testid="company-name"]
  • La ubicación de la empresa del elemento [data-testid="text-location"]

Transforma la información anterior en lógica de extracción de la siguiente manera:

title_element = job_element.find_element(By.CSS_SELECTOR, "h2.jobTitle")
title = title_element.text

url_element = title_element.find_element(By.CSS_SELECTOR, "a")
url = url_element.get_attribute("href")

company_element =job_element.find_element(By.CSS_SELECTOR, "[data-testid="company-name"]")
company = company_element.text

location_element = job_element.find_element(By.CSS_SELECTOR, "[data-testid="text-location"]")
location = location_element.text

find_element() selecciona el primer elemento que coincide con el selector dado. Dado un nodo, puede acceder a su contenido de texto con el atributo text. Para obtener el valor de un atributo HTML del nodo, debe utilizar el método get_attribute().

¡Genial! Ha sentado las bases para su lógica de scraping de Indeed, pero aún quedan datos útiles por extraer.

Paso n.º 7: extraer los detalles del puesto

Céntrate en la sección de detalles de la ficha del puesto de trabajo:

Inspecting the HTML of the job details

Esta vez, la información que hay que extraer es:

  • Las etiquetas del puesto de trabajo en uno o más elementos [data-testid="attribute_snippet_testid"] dentro de un .jobMetaDataGroup <div>
  • Si existe la opción de solicitar el puesto fácilmente a través de Indeed
  • Los elementos de descripción en uno o más elementos ul li dentro de un [role="presentation"] <div>

Empecemos por seleccionar las etiquetas. Puedes extraerlas todas con:

tags = []
tags_container_element = job_element.find_element(By.CSS_SELECTOR, ".jobMetaDataGroup")
tag_elements = tags_container_element.find_elements(By.CSS_SELECTOR, "[data-testid="attribute_snippet_testid"]")
for tag_element in tag_elements:
    tag = tag_element.text
    tags.append(tag)

En primer lugar, hay que inicializar una matriz donde almacenar todas las etiquetas recuperadas. Esto es necesario, ya que una sola ficha de oferta de empleo puede contener varias etiquetas. Después de seleccionarlas, hay que iterar sobre ellas, extraer el texto y añadir las etiquetas a la matriz.

Extraer la información de «Solicitar fácilmente» también es complicado. El problema es que el elemento HTML que indica esa posibilidad no está presente en todos los puestos de trabajo. Evidentemente, solo está presente donde se admite la opción «Solicitar fácilmente».

Cuando se intenta seleccionar un elemento que no está en la página, Selenium genera una excepción NoSuchElementException. Por lo tanto, se puede utilizar eso para extraer la casilla «Solicitar fácilmente» de forma eficaz:

try:
    job_element.find_element(By.CSS_SELECTOR, "[data-testid="indeedApply"]")
    easily_apply = True
except NoSuchElementException:
    easily_apply = False

Si el nodo [data-testid="indeedApply"] no está en la página, Selenium generará una excepción NoSuchElementException. Esa excepción será interceptada y easily_apply se establecerá en False.

En cuanto a los elementos de descripción, puedes extraerlos todos como hiciste con las etiquetas:

description = []
description_container_element = job_element.find_element(By.CSS_SELECTOR, "[role="presentation"]")
description_elements = description_container_element.find_elements(By.CSS_SELECTOR, "ul li")
for description_element in description_elements:
    description_item_text = description_element.text
    # Ignorar cadenas de descripción vacías
    if (description_item_text != ""):
        description.append(description_item_text)

¡Vaya! El Scraper de Indeed está casi completo.

Paso n.º 8: recopilar los datos extraídos

Con los datos extraídos de cada puesto de trabajo, rellena un diccionario de puestos:

job = {
    "title": title,
    "url": url,
    "company": company,
    "location": location,
    "tags": tags,
    "easily_apply": easily_apply,
    "description": description
}

A continuación, añádalo a la matriz de puestos de trabajo:

jobs.append(job)

Al final del bucle «for», los productos deberían contener algo como:

[{'title': 'Científico de datos', 'url': 'https://www.indeed.com/rc/clk?jk=efc7b7f4a8be2882&bb=NM368jsOPyYGAfEtQk2NNae8tSeBHdJ8Y9tImVa1Q9GAipGe0zzddcUozFEL0Na_pYCR4W6ljgljsBxWTUrluVuL8Gom7x7UZlgMzs0spo3NRgisrZ7meuaPfaEcjWoe&xkcb=SoD767M34WNyEaSTwx0FbzkdCdPP&fccid=8678bc4e64c24580&vjs=3', 'company': 'GQR', 'location': 'Nueva York, NY', 'tags': [], 'easily_apply': False, 'description': ['Manténgase al día de las tendencias del sector y las tecnologías emergentes para garantizar su ventaja competitiva.', 'Aplique técnicas estadísticas y de aprendizaje automático para mejorar la inversión...']},
# omitido por brevedad...
{'title': 'Científico de datos, delitos financieros - USDS', 'url': 'https://www.indeed.com/rc/clk?jk=aaa16dfd1cc6ef01&bb=NM368jsOPyYGAfEtQk2NNdxizAZQnHpzRrlr6WgbV1RtxmXz4vto1qiiqGiIj9CJFQQCV6cW59nE4hGw1yeNdokPfu8Fgl3EALBx5zdWjPm4COEu78DCFh4KTUMIFWkh&xkcb=SoAT67M34WNyEaSTwx0pbzkdCdPP&fccid=caed318a9335aac0&vjs=3', 'company': 'TikTok', 'location': 'Trabajo híbrido en Nueva York, NY', 'tags': [], 'easily_apply': False, 'description': ['Como científico de datos especializado en delitos financieros, desempeñarás un papel crucial en el aprovechamiento del aprendizaje automático, el análisis y las técnicas de visualización para mejorar nuestro...']}]

¡Maravilloso! Solo tienes que convertir estos datos a un formato mejor.

Paso n.º 9: Exportar los datos recopilados a CSV

Para que los datos recopilados sean accesibles y se puedan compartir, es buena idea exportarlos a un formato legible para los humanos. Por ejemplo, escríbelos en un archivo CSV. Para ello, utiliza estas líneas de código:

csv_file = "scraped_jobs.csv"
csv_headers = ["title", "url", "company", "location", "tags", "easily_apply", "description"]

with open(csv_file, mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=csv_headers)
    writer.writeheader()
    for job in jobs:
        writer.writerow({
            "title": job["title"],
            "url": job["url"],
            "company": job["company"],
            "location": job["location"],
            "tags": ";".join(job["tags"]),
            "easily_apply": "Yes" if job["easily_apply"] else "No",
            "description": ";".join(job["description"])
        })

La función open() crea el archivo CSV de salida, que luego se rellena con csv.DictWriter. Dado que los campos de etiquetas y descripción son matrices, se utiliza join() para aplanarlos en una sola cadena con elementos separados por ;.

No olvides importar csv desde la biblioteca estándar de Python:

import csv

¡Ya está! El Scraper de Indeed está completo.

Paso n.º 10: Ponlo todo junto

Tu archivo scraper.py final ahora contendrá:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.common import NoSuchElementException
import csv

# Configurar una instancia de Chrome controlable
driver = webdriver.Chrome(service=Service())

# Abrir la página de destino en el navegador
driver.get("https://www.indeed.com/jobs?q=data+scientist&l=New+York%2C+NY&from=searchOnDesktopSerp")

# Una estructura de datos donde almacenar las ofertas de empleo extraídas
jobs = []

# Seleccionar los elementos de ofertas de empleo en la página
jobs_container_element = driver.find_element(By.CSS_SELECTOR, "#mosaic-provider-jobcards")
job_elements = jobs_container_element.find_elements(By.CSS_SELECTOR, "[data-testid="slider_item"]")

# Recopilar cada oferta de empleo de la página
for job_element in job_elements:
    title_element = job_element.find_element(By.CSS_SELECTOR, "h2.jobTitle")
    title = title_element.text

    url_element = title_element.find_element(By.CSS_SELECTOR, "a")
    url = url_element.get_attribute("href")

    company_element =job_element.find_element(By.CSS_SELECTOR, "[data-testid="company-name"]")
    company = company_element.text

    location_element = job_element.find_element(By.CSS_SELECTOR, "[data-testid="text-location"]")
    location = location_element.text

    tags = []
    tags_container_element = job_element.find_element(By.CSS_SELECTOR, ".jobMetaDataGroup")
    tag_elements = tags_container_element.find_elements(By.CSS_SELECTOR, "[data-testid="attribute_snippet_testid"]")
    for tag_element in tag_elements:
        tag = tag_element.text
        tags.append(tag)

    # Comprueba si el elemento «Easy Apply» está en la página.
    try:
        job_element.find_element(By.CSS_SELECTOR, "[data-testid="indeedApply"]")
        easily_apply = True
    except NoSuchElementException:
        easily_apply = False

    description = []
    description_container_element = job_element.find_element(By.CSS_SELECTOR, "[role="presentation"]")
    description_elements = description_container_element.find_elements(By.CSS_SELECTOR, "ul li")
    for description_element in description_elements:
        description_item_text = description_element.text
        # Ignorar cadenas de descripción vacías
        if (description_item_text != ""):
            description.append(description_item_text)

    # Almacenar los datos extraídos
    job = {
        "title": title,
        "url": url,
        "company": company,
        "location": location,
        "tags": tags,
        "easily_apply": easily_apply,
        "description": description
    }
    jobs.append(job)

# Exportar los datos extraídos a un archivo CSV de salida
csv_file = "jobs.csv"
csv_headers = ["title", "url", "company", "location", "tags", "easily_apply", "description"]

with open(csv_file, mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=csv_headers)
    writer.writeheader()
    for job in jobs:
        writer.writerow({
            "title": job["title"],
            "url": job["url"],
            "company": job["company"],
            "location": job["location"],
            "tags": ";".join(job["tags"]),
            "easily_apply": "Yes" if job["easily_apply"] else "No",
            "description": ";".join(job["description"])
        })

# Cerrar el controlador web
driver.quit()

¡En menos de 100 líneas de código, acabas de crear un Scraper de Indeed en Python!

Inicia el Scraper con el siguiente comando:

python3 script.py

O, en Windows:

python script.py

Aparecerá un archivo jobs.csv en la carpeta de tu proyecto. Ábrelo y verás:

The final jobs.csv file with all the scraped results

¡Et voilà! Misión completada.

Desbloquea los datos de Indeed con facilidad

Indeed es muy consciente del valor de sus datos y emplea medidas robustas para protegerlos. Por eso, al interactuar con sus páginas utilizando una herramienta de automatización de navegadores como Selenium, es probable que te encuentres con un CAPTCHA:

Cloudflare CAPTCHA on Indeed

Como primer paso, considere seguir nuestra guía sobre cómo evitar los CAPTCHA en Python. No obstante, tenga en cuenta que el sitio podría seguir bloqueando sus intentos con medidas antibots adicionales. Descúbralas todas en nuestro seminario web sobre técnicas antibots.

Estos retos ponen de relieve cómo el scraping de Indeed sin las herramientas adecuadas puede convertirse rápidamente en una tarea frustrante e ineficaz. Además, la imposibilidad de utilizar navegadores sin interfaz gráfica hace que su script de scraping sea más lento y consuma más recursos.

¿La solución? La API Indeed Scraper de Bright Data, una herramienta que le permite recuperar datos de Indeed sin problemas mediante simples llamadas a la API, sin CAPTCHAs, sin bloqueos y sin complicaciones.

Conclusión

En esta guía paso a paso, ha aprendido qué es un Scraper de Indeed, los tipos de datos que puede recuperar y cómo crear uno en Python. Con solo unas 100 líneas de código, ha creado un script que recopila automáticamente datos de Indeed.

Aun así, el scraping de Indeed tiene sus retos. La plataforma aplica estrictas medidas anti-bot, incluyendo CAPTCHAs. Estos son difíciles de eludir y pueden ralentizar tu proceso de scraping, haciéndolo menos eficiente. Olvídate de todos esos retos con nuestra API Indeed Scraper.

Si el Scraping web no es lo tuyo, pero sigues interesado en los datos de ofertas de empleo, ¡explora nuestros Conjuntos de datos de Indeed listos para usar!

Crea hoy mismo una cuenta gratuita en Bright Data para probar nuestras API de Scraper o explorar nuestros Conjuntos de datos.