Парсинг сайтов с Python и BeautifulSoup

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

Python считается одним из лучших языков для веб-скрапинга благодаря простоте и большому количеству библиотек. Одна из самых популярных python-библиотек это BeautifulSoup. Она позволяет удобно разбирать HTML-страницы и извлекать из них нужные данные.

В этой статье мы затронем следующие аспекты:

  • как работает веб-скрапинг

  • как читать HTML-структуру сайта

  • как использовать BeautifulSoup

  • как на практике собрать вакансии с сайта hirify.me

Как работает веб-парсинг

На базовом уровне процесс выглядит так:

  1. Скрипт отправляет HTTP-запрос на сайт.

  2. Сервер возвращает HTML-страницу.

  3. Python разбирает HTML.

  4. Мы находим нужные элементы и извлекаем данные.

Важно понимать, что BeautifulSoup не запускает JavaScript. Он работает только с тем HTML, который приходит от сервера. Именно поэтому сайты с простой и стабильной разметкой, отлично подходят для обучения и практики.

Структура HTML и дерево документа

Любая веб-страница - это HTML-документ с вложенными элементами. Упрощенно HTML-дерево выглядит так:

html
├── head
│   └── title
└── body
    ├── h1
    └── div
        ├── p
        └── a

Каждый элемент может содержать другие элементы. BeautifulSoup позволяет легко перемещаться по этому дереву и находить нужные теги.

Установка необходимых библиотек

Для примеров в статье понадобятся две библиотеки:

  • requests - для выполнения HTTP-запросов

  • beautifulsoup4 - для парсинга HTML

Установка:

pip install requests beautifulsoup4

Первый запрос к сайту

Начнем с простого запроса и загрузки HTML:

import requests
from bs4 import BeautifulSoup

url = "https://hirify.me/jobs"
headers = {
    "User-Agent": "Mozilla/5.0"
}

response = requests.get(url, headers=headers)
html = response.text

soup = BeautifulSoup(html, "html.parser")

Теперь переменная soup содержит разобранное HTML-дерево страницы.

Анализ реальной структуры вакансии hirify.me

Каждая вакансия на странице оформлена как карточка. Ее корневой элемент выглядит так:

<div class="relative flex flex-col shadow-sm hover:shadow-md vacancy-card island">
    <a href="/jobs/297259-brand-creative-designer" class="vacancy-card-link">
        ...
    </a>
</div>

Из этой карточки можно извлечь:

  • ссылку на вакансию

  • название вакансии

  • компанию

  • тип занятости и формат работы

  • список навыков

  • дату публикации

Название вакансии

Название находится внутри тега h3 с классом title:

<h3 class="font-bold text-[22px] title">
    Brand Creative Designer
</h3>

Компания

Название компании:

<div class="company">
    topfoxx
</div>

Теги формата работы

Формат и уровень находятся в блоке с классом common-tags:

<div class="tag">remote</div>
<div class="tag">fulltime</div>
<div class="tag">senior,lead</div>

Навыки

Навыки находятся в блоке vacancy-tags и представлены тегами div.tag:

<div class="vacancy-tags">
    <div class="tag">photoshop</div>
    <div class="tag">marketing</div>
    <div class="tag">illustrator</div>
</div>

Поиск карточек вакансий

Сначала найдем все карточки вакансий:

vacancies = soup.find_all(
    "div",
    class_="vacancy-card"
)

Мы используем только стабильный класс vacancy-card, игнорируя utility-классы Tailwind.

Полный пример парсинга вакансий

Ниже приведен полноценный пример парсинга с пагинацией на основе реальной структуры HTML.

import requests
from bs4 import BeautifulSoup
import csv
import time

base_url = "https://hirify.me/jobs"
headers = {
    "User-Agent": "Mozilla/5.0"
}

all_jobs = []
page = 1

while True:
    if page == 1:
        url = base_url
    else:
        url = f"{base_url}?page={page}"

    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.text, "html.parser")

    vacancies = soup.find_all("div", class_="vacancy-card")

    if not vacancies:
        break

    for vacancy in vacancies:
        link_tag = vacancy.find("a", class_="vacancy-card-link")
        link = "https://hirify.me" + link_tag["href"] if link_tag else ""

        title_tag = vacancy.find("h3", class_="title")
        title = title_tag.get_text(strip=True) if title_tag else ""

        company_tag = vacancy.find("div", class_="company")
        company = company_tag.get_text(strip=True) if company_tag else ""

        meta = []
        for block in vacancy.find_all("div", class_="common-tags"):
            meta.extend([
                tag.get_text(strip=True)
                for tag in block.find_all("div", class_="tag")
            ])

        skills_block = vacancy.find("div", class_="vacancy-tags")
        skills = []
        if skills_block:
            skills = [
                tag.get_text(strip=True)
                for tag in skills_block.find_all("div", class_="tag")
                if "more-tags" not in tag.get("class", [])
            ]

        all_jobs.append({
            "title": title,
            "company": company,
            "meta": ", ".join(meta),
            "skills": ", ".join(skills),
            "link": link
        })

    page += 1
    time.sleep(1)

Сохранение результата в CSV

После завершения пагинации можно сохранить все вакансии в csv-файл:

with open("hirify_jobs.csv", "w", newline="", encoding="utf-8") as file:
    writer = csv.writer(file)
    writer.writerow([
        "Название",
        "Компания",
        "Формат",
        "Навыки",
        "Ссылка"
    ])

    for job in all_jobs:
        writer.writerow([
            job["title"],
            job["company"],
            job["meta"],
            job["skills"],
            job["link"]
        ])

BeautifulSoup отлично подходит для парсинга сайтов с простой и предсказуемой HTML-структурой и hirify.me - хороший пример такого сайта, у которого серверная отрисовка, понятные классы и логичная структура карточек.

Комментарии (0)

Войдите, чтобы оставить комментарий

Похожие статьи

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

Почему Bash-скрипты часто ломаются на macOS и в CI, и как Python решает эту проблему. Разбираем, в каких случаях Python лучше подходит для автоматизации, чем bash, и почему он делает скрипты понятнее, стабильнее и кросс-платформеннее.

54 0 1 мин

Декодирование JSON из HTTP-ответов в Laravel

Как Laravel обрабатываются JSON-ответы HTTP-клиента и какие возможности дают флаги декодирования JSON. Рассматриваются ошибки декодирования, работа с большими числами и настройка флагов по умолчанию.

34 0 1 мин