Как спарсить номер телефона с 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 объявления. Следите за обновлениями селекторов и соблюдайте политику использования сайта.

    Часто задаваемые вопросы