Как спарсить номер телефона с OLX: пошаговое руководство
Парсинг объявлений OLX - популярная задача для сбора контактов. Однако из-за динамической загрузки и защиты сайта номер телефона часто недоступен при прямом HTML-парсинге. В этой статье разберём рабочий способ извлечения телефона через API OLX с помощью Python, aiohttp и BeautifulSoup.
Почему номер телефона не парсится из HTML?
OLX загружает контактные данные асинхронно через JavaScript. При парсинге статического HTML вы получите пустой блок или заглушку. Решение - обращаться к внутреннему API сайта, который возвращает JSON с номерами телефонов.
Рабочий код для парсинга телефона OLX
Ниже представлен скрипт на Python, который читает ссылки на объявления из файла product_links.txt, извлекает ID объявления и запрашивает номер телефона через API.
Подготовка окружения
Установите зависимости:
pip install aiohttp beautifulsoup4 tqdmПолный код скрипта
import asyncio
import aiohttp
import csv
from bs4 import BeautifulSoup
from tqdm import tqdm
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
}
with open('product_links.txt', 'r') as f:
links = [line.strip() for line in f]
sem = asyncio.Semaphore(30)
async def fetch(session, url):
async with sem:
try:
async with session.get(url, headers=HEADERS, timeout=10) as response:
return await response.text()
except Exception as e:
print(f'Ошибка запроса {url}: {e}')
return None
async def get_phone_number(session, ad_id):
api_url = f'https://www.olx.ua/api/v1/offers/{ad_id}/phones/'
async with sem:
try:
async with session.get(api_url, headers=HEADERS, timeout=10) as response:
if response.status == 200:
data = await response.json()
phones = data.get('data', {}).get('phones', [])
return phones[0] if phones else 'Нет номера'
except Exception as e:
print(f'Ошибка запроса {api_url}: {e}')
return 'Ошибка запроса'
return 'Нет номера'
def get_ad_id(soup):
id_tag = soup.select_one(
'div#hydrate-root div.css-1ek5um8 div.css-118kolg div.css-1d90tha '
'div.css-n9feq4 div.css-1wws9er div.css-cgp8kk div.css-ayk4fp span.css-1i121pa'
)
return id_tag.get_text(strip=True).split(':')[1] if id_tag else None
async def process_link(session, link):
html = await fetch(session, link)
if not html:
return None
soup = BeautifulSoup(html, 'html.parser')
ad_id = get_ad_id(soup)
if not ad_id:
return None
phone = await get_phone_number(session, ad_id)
return [phone, link]
async def main():
connector = aiohttp.TCPConnector(limit=100)
async with aiohttp.ClientSession(connector=connector) as session:
tasks = [process_link(session, link) for link in links]
results = []
for future in tqdm(asyncio.as_completed(tasks), total=len(links), desc='Обработка'):
result = await future
if result:
results.append(result)
with open('ads_data.csv', 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['Номер телефона', 'Ссылка'])
writer.writerows(results)
if __name__ == '__main__':
asyncio.run(main())Частые ошибки и их решение
Ошибка 403 при запросе к API
Сервер OLX может блокировать запросы без корректного заголовка User-Agent. Убедитесь, что вы используете актуальный заголовок из браузера. Также добавьте задержки между запросами, чтобы не превысить лимиты.
ID объявления не найден
Селектор для извлечения ID может меняться при обновлении сайта. Проверьте актуальный CSS-селектор через инструменты разработчика в браузере. Обычно ID находится в блоке с классом css-1i121pa.
Пустой ответ от API
Если API возвращает пустой массив phones, значит номер скрыт или объявление удалено. В таком случае скрипт запишет 'Нет номера'.
Альтернативные методы получения телефона
Если API недоступен, можно использовать Selenium для эмуляции браузера, но это медленнее. Также существуют готовые парсеры для OLX, но они часто платные.
Заключение
Парсинг номера телефона с OLX через API - надёжный способ, если правильно извлекать ID объявления. Следите за обновлениями селекторов и соблюдайте политику использования сайта.