Извлечение данных о вакансиях с Indeed с использованием Python

Комментарии: 0

Для тех, кто исследует тенденции на рынке труда или собирает информацию для найма, скрапинг вакансий на Indeed является важным инструментом. В этом руководстве описывается, как использовать Playwright для веб-скрапинга и lxml для парсинга HTML, чтобы извлекать данные о вакансиях, такие как название должности, имя работодателя, местоположение, описание должности и ссылки на вакансии. Завершающим этапом является сохранение собранной информации в формате CSV.

Настройка среды для парсинга

Для успешного выполнения скрапинга потребуется установка следующих библиотек Python:

Playwright: необходим для автоматизации браузера. Установите, используя команду:


pip install playwright

lxml: используется для парсинга HTML. Установите, используя команду:


pip install lxml

pandas: облегчает сохранение данных в формате CSV. Установите, используя команду:


pip install pandas

Установка браузеров через Playwright:

После установки Playwright, запустите следующую команду для установки необходимых бинарных файлов браузера:


playwright install

Шаг 1: Настройка Playwright для веб-скрапинга

Для начала настроим Playwright на запуск браузера Chromium, переход на целевую веб-страницу и извлечение ее содержимого. Важным элементом этой настройки является использование прокси.

Почему стоит использовать прокси?

Использование прокси имеет ряд преимуществ, включая:

  • Избежание блокировки IP: распределение запросов через различные IP-адреса помогает избежать обнаружения и блокировки.
  • Обход географических ограничений: доступ к содержимому, которое может быть ограничено в определенных регионах.
  • Обеспечение анонимности: скрытие вашего реального IP-адреса и обеспечение анонимности при скрапинге.

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': '<пароль>'
            }
        )  # Запуск браузера с отображением интерфейса
        page = await browser.new_page()
        await page.goto(url)
        
        # Извлечение содержимого страницы
        content = await page.content()
        
        await browser.close()  # Закрытие браузера после завершения
        return content

В данном примере async_playwright инициирует запуск браузера с пользовательским интерфейсом, осуществляет переход по указанному URL и извлекает HTML-содержимое страницы.

Парсинг HTML-содержимого с использованием lxml

После получения HTML-содержимого страницы следует его детальный анализ для извлечения необходимых данных. В этом процессе ключевую роль играет библиотека lxml, которая предоставляет мощные инструменты для анализа HTML. Lxml поддерживает использование XPath, что позволяет точно и эффективно локализовать и извлекать элементы данных из сложных веб-страниц.


from lxml import html

def parse_job_listings(content):
    # Парсинг HTML-содержимого
    parser = html.fromstring(content)
    
    # Извлечение каждой вакансии с использованием XPath
    job_posting = parser.xpath('//ul[@class="css-zu9cdh eu4oa1w0"]/li')
    
    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()'))

            # Добавление извлеченных данных в список jobs_data
            jobs_data.append({
                'Title': title,
                'Link': f"https://www.indeed.com{link}",
                'Location': location,
                'Description': description,
                'Company': company_name
            })
    
    return jobs_data

Шаг 2: Скрапинг вакансий

  • После того как автоматизация браузера и парсинг были успешно настроены, переходим к основной задаче — скрапингу списка вакансий с Indeed.
  • Описание:
    • get_page_content(url): Получает содержимое страницы с помощью Playwright.
    • parse_job_listings(content): Анализирует содержимое с помощью lxml и извлекает данные о вакансиях.
    • main(): Организует процесс скрапинга, получая данные и сохраняя их в файл CSV.
    
    import pandas as pd
    
    async def scrape_indeed_jobs(url):
        # Шаг 1: Получение содержимого страницы с помощью Playwright
        content = await get_page_content(url)
        
        # Шаг 2: Анализ HTML и извлечение информации о вакансиях
        jobs_data = parse_job_listings(content)
        
        return jobs_data
    
    # URL для скрапинга
    url = 'https ссылка'
    
    # Скрапинг и сохранение данных
    async def main():
        # Скрапинг данных о вакансиях с указанного URL
        jobs = await scrape_indeed_jobs(url)
        
        # Шаг 3: Сохранение данных в CSV с помощью pandas
        df = pd.DataFrame(jobs)
        df.to_csv('indeed_jobs.csv', index=False)
        
        print("Data saved to indeed_jobs.csv")
    
    # Запуск главной функции
    asyncio.run(main())
    
    

    Шаг 3: Обработка пагинации

    Сайт Indeed использует пагинацию для разбиения списка вакансий на множество страниц. Это удобно, поскольку позволяет распределить информацию и снизить нагрузку на серверы, но требует дополнительной логики в скрапере для перехода по страницам. Каждая новая страница в списке вакансий доступна через изменение параметра start в URL, который увеличивается на 10 с каждой новой страницей.

    Чтобы адаптировать скрапер к этому механизму, можно создать функцию scrape_multiple_pages, которая будет модифицировать базовый URL, увеличивая параметр start для доступа к каждой следующей странице. Этот подход позволяет систематически собирать данные со всех страниц, увеличивая охват и количество собираемых вакансий.

    
    async def scrape_multiple_pages(base_url, pages=3):
        all_jobs = []
        
        for page_num in range(pages):
            # Обновление URL для пагинации
            url = f"{base_url}&start={page_num * 10}"
            print(f"Scraping page: {url}")
            
            # Скрапинг данных о вакансиях с каждой страницы
            jobs = await scrape_indeed_jobs(url)
            all_jobs.extend(jobs)
        
        # Сохранение всех вакансий в 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")
    
    # Скрапинг нескольких страниц списка вакансий
    asyncio.run(scrape_multiple_pages('https ссылка', pages=3))
    
    

    Шаг 4: Конфигурирование поисковых запросов для вакансий

    Для того чтобы скрапить информацию по конкретным должностям или ключевым словам, необходимо настроить параметр поиска query в URL сайта Indeed. Это позволяет целенаправленно извлекать данные по интересующим профессиям или секторам. Например, для поиска вакансий Python-разработчика на сайте www.indeed.com.

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

    Изменяя значение параметра query на желаемые названия должностей или ключевые слова, можно адаптировать скрапер к различным задачам по сбору данных. Это дает гибкость в выборе целевых вакансий и позволяет эффективно собирать информацию с Indeed, адаптируясь к изменяющимся требованиям рынка труда.

    Финальная версия кода

    
    import asyncio
    from playwright.async_api import async_playwright
    from lxml import html
    import pandas as pd
    
    # Шаг 1: Получение содержимого страницы с помощью Playwright
    async def get_page_content(url):
        async with async_playwright() as p:
            browser = await p.chromium.launch(
                headless=False
                proxy = {
                    'server': '',
                    'username': '<логин>',
                    'password': '<пароль>'
                }
            )  # Запуск браузера с интерфейсом
            page = await browser.new_page()
            await page.goto(url, wait_until='networkidle')
            
            # Извлечение содержимого страницы
            content = await page.content()
            await browser.close()  # Закрытие браузера после использования
            return content
    
    # Шаг 2: Разбор HTML-содержимого с помощью lxml
    def parse_job_listings(content):
        # Разбор HTML с помощью lxml
        parser = html.fromstring(content)
        
        # Выбор отдельных объявлений о работе с помощью XPath
        job_posting = parser.xpath('//ul[@class="css-zu9cdh eu4oa1w0"]/li')
        
        # Извлечение данных о работе
        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()'))
    
                # Добавление извлеченных данных в список jobs_data
                jobs_data.append({
                    'Title': title,
                    'Link': f"https://www.indeed.com{link}",
                    'Location': location,
                    'Description': description,
                    'Company': company_name
                })
        
        return jobs_data
    
    # Шаг 3: Скрапинг вакансий Indeed для одной страницы
    async def scrape_indeed_jobs(url):
        # Получение содержимого страницы с помощью Playwright
        content = await get_page_content(url)
        
        # Разбор HTML и извлечение данных о работе
        jobs_data = parse_job_listings(content)
        
        return jobs_data
    
    # Шаг 4: Управление пагинацией и скрапинг множества страниц
    async def scrape_multiple_pages(base_url, query, pages=3):
        all_jobs = []
        
        for page_num in range(pages):
            # Обновление URL для управления пагинацией и добавление поискового запроса
            url = f"{base_url}?q={query}&start={page_num * 10}"
            print(f"Scraping page: {url}")
            
            # Скрапинг вакансий для текущей страницы
            jobs = await scrape_indeed_jobs(url)
            all_jobs.extend(jobs)
        
        # Сохранение всех вакансий в 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")
    
    #  Функция для запуска скрапера с динамическим вводом запроса пользователя
    async def run_scraper():
        # Шаг 5: Запрос у пользователя ввода поискового запроса и количества страниц для скрапинга
        query = input("Введите название должности или ключевые слова для поиска (например, python+developer): ")
        pages = int(input("Введите количество страниц для скрапинга: "))
        
        # Скрапинг вакансий на нескольких страницах в соответствии с запросом
        base_url = 'https://www.indeed.com/jobs'
        await scrape_multiple_pages(base_url, query, pages)
    
    # Запуск скрапера
    asyncio.run(run_scraper())
    
    

    Чтобы обеспечить бесперебойный процесс скрапинга и уменьшить риск блокировок и появления капчи, необходимо выбрать правильный прокси-сервер. Наиболее оптимальным вариантом для скрапинга являются ISP прокси, которые обеспечивают высокую скорость и стабильность соединения, а также имеют высокий траст-фактор, благодаря чему редко блокируются платформами. Данный тип прокси является статическим, поэтому для масштабного скрапинга необходимо сформировать пул ISP прокси и настроить ротацию IP-адресов для их регулярной смены. Альтернативным вариантом станут резидентские прокси, которые являются динамическими и имеют наибольшее географическое покрытие в сравнении с другими типами прокси-серверов.

  • Комментарии:

    0 комментариев