Cómo raspar listados de empleo de Indeed usando Python

Comentarios: 0

Para los buscadores de empleo, los empleadores o cualquier persona que siga las tendencias del mercado laboral, el raspado de la lista de empleos disponibles de Indeed podría proporcionar información útil. En este tutorial en particular, combinaremos Playwright para el raspado web y lxml para el análisis de contenido HTML con el fin de recopilar los detalles del trabajo, incluyendo su título, el nombre de la empresa de contratación, la ubicación, la descripción del trabajo, el enlace de publicación del trabajo y, finalmente, presentaremos los hallazgos guardando la información en un archivo CSV.

Requisitos

Para realizar con éxito el scraping es necesario tener instaladas las siguientes librerías de Python.

Playwright para la automatización del navegador:


pip install playwright

lxml para analizar HTML:


pip install lxml

pandas para guardar datos en un archivo CSV:


pip install pandas

Instalar navegadores Playwright:

Después de instalar Playwright, ejecute este comando para instalar los binarios necesarios del navegador:


playwright install

Paso 1: Configurar Playwright para el web scraping

Playwright permite automatizar e interactuar con navegadores web. Comenzamos configurando Playwright para lanzar un navegador Chromium, visitar una página web y extraer su contenido. Aquí también podemos pasar proxies a través del playwright.

¿Por qué usar proxies?

Los sitios web suelen tener medidas de limitación de velocidad o anti-scraping para bloquear las peticiones repetidas desde la misma dirección IP. Los proxies te permiten:

  • Evitar el bloqueo de IPs: distribuye tus peticiones a través de diferentes IPs para evitar ser detectado.
  • Evitar la geolocalización: accede a listas de empleo que podrían estar restringidas en función de la ubicación geográfica.
  • Anonimato: oculte su IP real y permanezca en el anonimato durante el proceso de scraping.

import asyncio
from playwright.async_api import async_playwright

async def get_page_content(url):
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False,
            proxy = {
                'server': '',
                'username': '',
                'password': ''
            }
        )  # Navegador encabezado
        page = await browser.new_page()
        await page.goto(url)
        
        # Extraer el contenido de la página
        content = await page.content()
        
        await browser.close()  # Cierre el navegador cuando haya terminado
        return content


En este código, async_playwright inicia un navegador encabezado, navega hasta la URL especificada y obtiene el contenido de la página.

Parificación de contenido HTML mediante lxml

A continuación, analizaremos el contenido de la página para extraer datos significativos. lxml se utiliza para este propósito porque proporciona un soporte robusto para analizar y consultar contenido HTML utilizando XPath.


from lxml import html

def parse_job_listings(content):
    # Analizar contenido HTML
    parser = html.fromstring(content)
    
    # Extraer cada oferta de empleo mediante XPath
    job_posting = parser.xpath('//ul[@class="css-zu9cdh eu4oa1w0"]/li')
    
    jobs_data = []
    for element in job_posting[:-1]:  # Omitir el último elemento si es un anuncio o irrelevante
        title = ''.join(element.xpath('.//h2/a/span/@title'))
        if title:
            link = ''.join(element.xpath('.//h2/a/@href'))
            location = ''.join(element.xpath('.//div[@data-testid="text-location"]/text()'))
            description = ', '.join(element.xpath('.//div[@class="css-9446fg eu4oa1w0"]/ul//li/text()'))
            company_name = ''.join(element.xpath('.//span[@data-testid="company-name"]/text()'))

            # Añadir los datos extraídos a la lista jobs_data
            jobs_data.append({
                'Title': title,
                'Link': f"https://www.indeed.com{link}",
                'Location': location,
                'Description': description,
                'Company': company_name
            })
    
    return jobs_data

Paso 2: raspado de listas de empleo

Ahora que tenemos tanto la automatización del navegador como los pasos de parseo configurados, vamos a combinarlos para scrapear listados de empleo de la página de Indeed.

Explicación:

  • get_page_content(url): Obtiene el contenido de la página utilizando Playwright.
  • parse_job_listings(content): Analiza el contenido mediante lxml y extrae los datos del trabajo.
  • main(): Organiza el proceso de scraping, obteniendo datos y guardándolos en un archivo CSV.

import pandas as pd

async def scrape_indeed_jobs(url):
    # Paso 1: Obtener el contenido de la página con Playwright
    content = await get_page_content(url)
    
    # Paso 2: Parsear el HTML y extraer los detalles del trabajo
    jobs_data = parse_job_listings(content)
    
    return jobs_data

# URL que se va a raspar
url = 'https://www.indeed.com/q-usa-jobs.html'

# Extracción y almacenamiento de datos
async def main():
    # Extraer datos de trabajo de la URL especificada
    jobs = await scrape_indeed_jobs(url)
    
    # Paso 3: Guardar los datos en CSV utilizando pandas
    df = pd.DataFrame(jobs)
    df.to_csv('indeed_jobs.csv', index=False)
    
    print("Data saved to indeed_jobs.csv")

# Ejecutar la función principal
asyncio.run(main())

Paso 3: Añadir soporte de paginación

Indeed pagina sus listados de empleo, y puedes extender fácilmente el scraper para manejar múltiples páginas. La URL de la página se ajusta usando un parámetro de consulta start, que se incrementa en 10 por cada nueva página.

Para mejorar la funcionalidad de su scraper para recopilar datos de múltiples páginas, puede implementar una función llamada scrape_multiple_pages. Esta función modificará la URL base ajustando de forma incremental el parámetro de inicio, permitiendo el acceso a las páginas posteriores. Al progresar sistemáticamente a través de cada página, puede ampliar el alcance y la cantidad de datos recopilados, como las vacantes, lo que garantiza un conjunto de datos más completo.


async def scrape_multiple_pages(base_url, pages=3):
    all_jobs = []
    
    for page_num in range(pages):
        # Actualizar URL para paginación
        url = f"{base_url}&start={page_num * 10}"
        print(f"Scraping page: {url}")
        
        # Extraer datos de trabajo de cada página
        jobs = await scrape_indeed_jobs(url)
        all_jobs.extend(jobs)
    
    # Guardar todos los trabajos en CSV
    df = pd.DataFrame(all_jobs)
    df.to_csv('indeed_jobs_all_pages.csv', index=False)
    print("Data saved to indeed_jobs_all_pages.csv")

# Recoger varias páginas de ofertas de empleo
asyncio.run(scrape_multiple_pages('https://www.indeed.com/jobs?q=usa', pages=3))

Paso 4: personalizar las consultas de búsqueda de empleo

Para apuntar a títulos de empleo o palabras clave específicos en sus esfuerzos de raspado, necesitará configurar el parámetro de búsqueda de consulta en la URL utilizada por Indeed. Esta personalización permite que el scraper recopile datos específicos de determinados empleos o sectores. Por ejemplo, si estás buscando puestos de desarrollador de Python en http://www.indeed.com, ajustarías el parámetro de consulta para incluir "Python+desarrollador" o palabras clave relevantes.


query = "python+developer"
base_url = f"https://www.indeed.com/jobs?q={query}"
asyncio.run(scrape_multiple_pages(base_url, pages=3))

Modificando este parámetro en función de sus necesidades de recopilación de datos, puede centrar su raspado en empleos específicos, mejorando la flexibilidad y la eficacia de su proceso de recopilación de datos. Este enfoque es especialmente útil para adaptarse a las demandas dinámicas del mercado laboral.

Código completo


import asyncio
from playwright.async_api import async_playwright
from lxml import html
import pandas as pd

# Paso 1: Obtener el contenido de la página con Playwright
async def get_page_content(url):
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False
            proxy = {
                'server': '',
                'username': '',
                'password': ''
            }
        )  # Ejecutar el navegador en modo encabezado
        page = await browser.new_page()
        await page.goto(url, wait_until='networkidle')
        
        # Extraer el contenido de la página
        content = await page.content()
        await browser.close()  # Cerrar el navegador después de usarlo
        return content

# Paso 2: Analizar el contenido HTML mediante lxml
def parse_job_listings(content):
    # Analiza el HTML con lxml
    parser = html.fromstring(content)
    
    # Seleccionar ofertas de empleo individuales mediante XPath
    job_posting = parser.xpath('//ul[@class="css-zu9cdh eu4oa1w0"]/li')
    
    # Extraer datos de trabajo
    jobs_data = []
    for element in job_posting[:-1]:
        title = ''.join(element.xpath('.//h2/a/span/@title'))
        if title:
            link = ''.join(element.xpath('.//h2/a/@href'))
            location = ''.join(element.xpath('.//div[@data-testid="text-location"]/text()'))
            description = ', '.join(element.xpath('.//div[@class="css-9446fg eu4oa1w0"]/ul//li/text()'))
            company_name = ''.join(element.xpath('.//span[@data-testid="company-name"]/text()'))

            # Añadir los datos extraídos a la lista jobs_data
            jobs_data.append({
                'Title': title,
                'Link': f"https://www.indeed.com{link}",
                'Location': location,
                'Description': description,
                'Company': company_name
            })
    
    return jobs_data

# Paso 3: Recopilar ofertas de empleo de Indeed para una sola página
async def scrape_indeed_jobs(url):
    # Obtener el contenido de la página con Playwright
    content = await get_page_content(url)
    
    # Análisis de HTML y extracción de datos de trabajo
    jobs_data = parse_job_listings(content)
    
    return jobs_data

# Paso 4: Gestión de la paginación y raspado de varias páginas
async def scrape_multiple_pages(base_url, query, pages=3):
    all_jobs = []
    
    for page_num in range(pages):
        # Actualice la URL para gestionar la paginación y añada la consulta de búsqueda
        url = f"{base_url}?q={query}&start={page_num * 10}"
        print(f"Scraping page: {url}")
        
        # Trabajos de raspado para la página actual
        jobs = await scrape_indeed_jobs(url)
        all_jobs.extend(jobs)
    
    # Guardar todos los trabajos en un archivo CSV
    df = pd.DataFrame(all_jobs)
    df.to_csv(f'indeed_jobs_{query}.csv', index=False)
    print(f"Data saved to indeed_jobs_{query}.csv")

# Función para ejecutar el scraper con entrada de consulta dinámica
async def run_scraper():
    # Paso 5: Pedir al usuario que introduzca la consulta y el número de páginas que desea raspar.
    query = input("Enter the job title or keywords to search (e.g., python+developer): ")
    pages = int(input("Enter the number of pages to scrape: "))
    
    # Trabajos de raspado en varias páginas en función de la consulta
    base_url = 'https://www.indeed.com/jobs'
    await scrape_multiple_pages(base_url, query, pages)

# Ejecutar el rascador
asyncio.run(run_scraper())

Para garantizar un proceso de scraping sin problemas y reducir el riesgo de bloqueos y apariciones de CAPTCHA, es crucial elegir el servidor proxy adecuado. La opción más óptima para el scraping son los proxies ISP, que proporcionan alta velocidad y estabilidad de conexión, así como un alto factor de confianza, lo que hace que rara vez sean bloqueados por las plataformas. Este tipo de proxy es estático, por lo que para el scraping a gran escala, es necesario crear un grupo de proxies ISP y configurar la rotación de IP para su cambio regular. Una opción alternativa serían los proxies residenciales, que son dinámicos y tienen la cobertura geográfica más amplia en comparación con otros tipos de servidores proxy.

Comentarios:

0 Comentarios