Блог AST-SoftPro
Сбор лидов с площадок объявлений: парсер на BeautifulSoup и Selenium
Сбор лидов с площадок объявлений
Площадки объявлений — золотая жила для поиска клиентов. На Avito, HH.ru и подобных сайтах ежедневно публикуются тысячи объявлений о поиске подрядчиков, сотрудников и поставщиков. Автоматизированный сбор этих данных позволяет первыми реагировать на актуальные запросы.
Python предлагает два основных подхода: BeautifulSoup для статических страниц и Selenium для сайтов с динамической загрузкой.
Парсинг статических страниц (BeautifulSoup)
import requests
from bs4 import BeautifulSoup
import csv
from datetime import datetime
def parse_job_board(url, page_count=5):
"""Сбор вакансий с доски объявлений."""
leads = []
for page in range(1, page_count + 1):
params = {'page': page} if page > 1 else {}
response = requests.get(url, params=params, headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
})
soup = BeautifulSoup(response.text, 'html.parser')
for item in soup.select('.job-item'):
lead = {
'title': item.select_one('.job-title').text.strip(),
'company': item.select_one('.company-name').text.strip(),
'location': item.select_one('.location').text.strip(),
'salary': item.select_one('.salary').text.strip() if item.select_one('.salary') else '',
'url': item.select_one('a')['href'],
'date': datetime.now().strftime('%Y-%m-%d %H:%M'),
}
leads.append(lead)
return leads
# Пример использования
# jobs = parse_job_board('https://example-board.com/jobs?keyword=python')
# print(f"Найдено вакансий: {len(jobs)}")
Динамические страницы (Selenium)
Многие современные площадки загружают контент через JavaScript. Для таких сайтов Selenium — обязательный инструмент:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
import time
def parse_dynamic_board(search_query, max_pages=3):
"""Парсинг площадки с динамической загрузкой."""
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(options=chrome_options)
leads = []
try:
driver.get(f'https://example-board.com/search?q={search_query}')
wait = WebDriverWait(driver, 10)
# Ожидание загрузки результатов
wait.until(EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, '.listing-item')
))
# Прокрутка для загрузки всех элементов
for _ in range(3):
driver.execute_script('window.scrollBy(0, 500)')
time.sleep(1)
items = driver.find_elements(By.CSS_SELECTOR, '.listing-item')
for item in items:
try:
title = item.find_element(By.CSS_SELECTOR, '.title').text
contact = item.find_element(By.CSS_SELECTOR, '.contact').text
leads.append({
'title': title,
'contact': contact,
'date': datetime.now().strftime('%Y-%m-%d %H:%M'),
})
except Exception:
continue
finally:
driver.quit()
return leads
Дедупликация и очистка данных
import sqlite3
import re
def clean_phone(phone_text):
"""Нормализация номера телефона."""
digits = re.findall(r'\d', phone_text)
if len(digits) == 11 and digits[0] == '7':
return f"+{'' .join(digits)}"
return phone_text
def clean_email(email_text):
"""Извлечение email из текста."""
match = re.search(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', email_text)
return match.group(0) if match else ''
def save_unique_leads(leads):
"""Сохранение лидов с дедупликацией."""
conn = sqlite3.connect('leads.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS leads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
company TEXT,
phone TEXT,
email TEXT,
source TEXT,
url TEXT UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
inserted = 0
for lead in leads:
try:
cursor.execute(
'INSERT OR IGNORE INTO leads (title, company, phone, email, source, url) VALUES (?, ?, ?, ?, ?, ?)',
(
lead.get('title', ''),
lead.get('company', ''),
clean_phone(lead.get('contact', '')),
clean_email(lead.get('contact', '')),
'job_board',
lead.get('url', ''),
)
)
if cursor.rowcount > 0:
inserted += 1
except Exception:
pass
conn.commit()
conn.close()
return inserted
Экспорт в CSV для CRM
import csv
def export_leads_to_csv(filename='leads_export.csv'):
"""Экспорт новых лидов за сегодня в CSV."""
conn = sqlite3.connect('leads.db')
cursor = conn.cursor()
cursor.execute('''
SELECT title, company, phone, email, source, url
FROM leads
WHERE date(created_at) = date('now')
''')
rows = cursor.fetchall()
conn.close()
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(['Заголовок', 'Компания', 'Телефон', 'Email', 'Источник', 'URL'])
writer.writerows(rows)
print(f"Экспортировано {len(rows)} лидов в {filename}")
return len(rows)
Сравнение площадок для сбора лидов
| Площадка | Тип данных | Сложность парсинга | Лимиты |
|---|---|---|---|
| Avito | Объявления, контакты | Средняя (CAPTCHA) | Rate limiting строгий |
| HH.ru | Вакансии, компании | Низкая (API доступен) | Официальный API |
| Юла | Локальные объявления | Средняя | Динамическая загрузка |
| FL.ru | Фриланс-заказы | Высокая (защита) | CAPTCHA, блокировки |
Заключение
Сбор лидов с площадок объявлений — эффективный способ найти потенциальных клиентов. Ключевые шаги:
-
BeautifulSoup для статических страниц, Selenium для динамических;
-
Дедупликация по URL или контактным данным;
-
Очистка телефонов и email регулярными выражениями;
-
Экспорт в CSV для загрузки в CRM.
Начните с одной площадки, отладьте парсер, затем масштабируйте на несколько источников.