Wprowadzenie: co otrzymasz i komu przyda się ten przewodnik

Yandex Maps już dawno przekształciły się z nawigatora w najbardziej kompletny katalog firm w Rosji. W kartach organizacji znajduje się to, czego potrzebuje każdy marketer, arbitrażysta czy właściciel firmy: dokładne adresy, telefony, strony internetowe, godziny pracy, oceny i tysiące szczerych opinii klientów. Problem w tym, że ręczne przejście pięciuset kart stomatologii w jednym mieście jest niemożliwe, a gotowe serwisy są albo drogie, albo zwracają nieaktualne dane.

W tym poradniku wspólnie napiszemy własny parser Yandex Maps w Pythonie. Żadnych płatnych programów, żadnych cudzych serwerów. Pod koniec przewodnika będziesz mieć:

  • Działający skrypt, który zbiera karty organizacji według dowolnego zapytania i miasta.
  • Tabelę Excel z nazwami, kategoriami, adresami, telefonami, stronami internetowymi, oceną i godzinami pracy.
  • Osobną tabelę z opiniami: autor, ocena, data, treść.
  • Zrozumienie, jak pracować ostrożnie i nie otrzymywać captcha i blokad, rozkładając obciążenie przez mobilne proxy.

Ważna granica tego artykułu: omawiamy konkretnie karty organizacji w Mapach. Parsowanie zwykłych wyników wyszukiwania Yandex, praca z antydetektowymi przeglądarkami i podstawowa konfiguracja mobilnych proxy zostały opisane w powiązanych materiałach bloga – tutaj ich nie powtarzamy, a jedynie się na nich opieramy.

Dla kogo jest ten przewodnik

  • Marketerzy i właściciele firm, którzy potrzebują bazy konkurentów lub partnerów w regionie z rzeczywistymi telefonami i ocenami.
  • Arbitrażyści, zbierający nisze offline i lokalne oferty do dalszej analizy.
  • Programiści, którym zlecono zbieranie danych z Map, a zaczynanie od zera im się nie uśmiecha.
  • Analitycy, badający opinie w celu oceny jakości usług w niszy.

Co trzeba wiedzieć wcześniej

Nic skomplikowanego. Wystarczy umieć instalować programy, otwierać wiersz poleceń i kopiować tekst. Doświadczenie programistyczne nie jest obowiązkowe: cały kod jest gotowy, wystarczy podstawić swoje wartości. Jeśli choć raz uruchamiałeś skrypt w Pythonie, będzie Ci bardzo łatwo. W sekcji dla zaawansowanych poruszymy asynchroniczność i pracę z odpowiedziami sieciowymi, ale można ją pominąć.

Ile czasu będzie potrzebne

  • Przygotowanie środowiska: 30-40 minut.
  • Napisanie i debugowanie parsera krok po kroku: 1,5-2 godziny.
  • Pierwsze pełne zbieranie na 300-500 kart: 1-3 godziny czasu w tle, podczas gdy zajmujesz się innymi sprawami.

Razem w pół dnia pracy otrzymujesz narzędzie, które dalej będzie oszczędzać Ci tygodnie ręcznej pracy.

Wstępne przygotowanie: narzędzia, dostępy i wymagania

Zanim napiszemy kod, zbierzmy wszystko, co potrzebne. Pomiń tę sekcję tylko jeśli masz już zainstalowany Python i dostęp do mobilnego proxy.

Wymagania systemowe

  • Windows 10 lub 11, macOS 12 i nowszy albo dowolny nowoczesny Linux.
  • Minimum 8 GB pamięci RAM: będziemy uruchamiać prawdziwą przeglądarkę Chromium.
  • 3-4 GB wolnego miejsca na dysku na Python, przeglądarkę i wyniki.
  • Stabilny internet. Prędkość nie jest krytyczna, ważniejszy jest brak przerw.

Co trzeba zainstalować

  1. Python 3.11 lub 3.12. Pobierz instalator z oficjalnej strony python.org. Przy instalacji na Windows koniecznie zaznacz pole Add Python to PATH na dole pierwszego okna instalatora, inaczej polecenia w konsoli nie będą działać.
  2. Edytor kodu. Wystarczy Visual Studio Code, PyCharm Community albo nawet Notepad++. Będziemy się kierować na VS Code, ale dla naszych zadań nie ma różnicy.
  3. Biblioteki Python: playwright do sterowania przeglądarką, pandas i openpyxl do tabel, requests do sprawdzania proxy. Zainstalujemy je w pierwszym kroku.
  4. Przeglądarka Chromium dla Playwright. Pobiera się osobnym poleceniem, waży około 150 MB.

Dostęp do mobilnego proxy

To kluczowy element sekcji „bez banów”. Yandex Maps są wrażliwe na częstotliwość zapytań z jednego adresu. Mobilne proxy dają adresy IP rzeczywistych operatorów komórkowych, za którymi jednocześnie siedzą tysiące zwykłych użytkowników, dlatego Yandex odnosi się do nich maksymalnie przychylnie. W panelu klienta mobileproxy.space po zakupie proxy będziesz potrzebować czterech wartości:

  • Host (adres serwera) i port do połączenia HTTP.
  • Login i hasło do autoryzacji.
  • Link do zmiany IP: specjalny URL, po odwiedzeniu którego proxy otrzymuje nowy adres od operatora. Skopiuj go do osobnego pliku, przyda się w piątym kroku.

Wskazówka: Wybieraj proxy z tego samego regionu lub przynajmniej tego samego kraju, co miasto, które parsujesz. Mapy dostosowują wyniki do geolokalizacji, a proxy z innego kraju może pokazywać niepełną listę organizacji lub interfejs w innym języku.

Kopie zapasowe i folder roboczy

Utwórz na dysku folder, na przykład maps_parser. Wewnątrz niego będą leżeć skrypty i wyniki. Wszystkie dane pośrednie będziemy zapisywać do plików po każdej karcie, więc nawet jeśli skrypt padnie na trzechsetnej organizacji, pierwsze dwieście dziewięćdziesiąt dziewięć nie przepadnie. Kopię pliku z wynikami warto robić przed każdym ponownym uruchomieniem: po prostu zmień nazwę starego pliku, dodając datę.

Sprawdzenie: Otwórz wiersz poleceń (w Windows naciśnij Win+R, wpisz cmd i Enter) i wpisz polecenie python --version. Jeśli widzisz ciąg typu Python 3.12.x, przygotowanie zakończone. Jeśli zamiast tego jest błąd, przeinstaluj Python z zaznaczonym Add to PATH.

Podstawowe pojęcia: jak zbudowane są Mapy i czym jest parsowanie kart

Zanim napiszemy kod, zapoznajmy się z terminami. Są proste, ale bez nich dalej nie będzie jasne, dlaczego robimy to właśnie tak.

Kluczowe terminy prostym językiem

  • Parsowanie (scraping) — automatyczne zbieranie informacji ze stron internetowych. Program otwiera stronę, tak jak zrobiłby to człowiek, i wyciąga z niej potrzebne fragmenty.
  • Karta organizacji — osobna strona w Mapach z adresem typu yandex.ru/maps/org/nazwa/identyfikator-liczbowy/. To na niej znajdują się telefon, adres, opinie i pozostałe dane. Lista organizacji po lewej to jedynie witryna, z której bierzemy tylko linki do kart.
  • Selektor — „adres” elementu wewnątrz strony. Na przykład klasa business-contacts-view__address wskazuje blok z adresem. Po selektorach skrypt znajduje potrzebny tekst.
  • Przeglądarka headless — prawdziwa przeglądarka sterowana przez program. Może działać z oknem (widzisz, co się dzieje) lub bez niego.
  • Playwright — biblioteka do sterowania przeglądarką z Pythona. Wybraliśmy ją, ponieważ Mapy są w całości zbudowane na JavaScripcie, a zwykłe zapytanie o HTML zwróci pustą stronę bez danych.
  • Mobilne proxy — pośrednik między Twoim komputerem a stroną z adresem IP sieci komórkowej. Strona widzi nie Twój adres, a adres operatora.
  • Rotacja IP — okresowa zmiana adresu proxy, aby obciążenie nie koncentrowało się na jednym IP.
  • Captcha — strona weryfikacji „Potwierdź, że nie jesteś robotem”. Dla nas to sygnał, że za bardzo się spieszymy. Nie będziemy jej rozwiązywać zewnętrznymi serwisami, a po prostu zatrzymamy się, zmienimy IP i zwolnimy tempo.

Podstawowe zasady działania parsera Yandex Maps

Logika jest prosta i składa się z trzech faz. Najpierw otrzymujemy listę linków do kart potrzebnych organizacji. Następnie otwieramy każdą kartę po kolei i wyciągamy dane. Na końcu składujemy wszystko w tabelę. Między tymi działaniami robimy pauzy, losowe pod względem długości, i od czasu do czasu zmieniamy IP przez link zmiany adresu.

Co ważne wiedzieć o legalności i etyce

Uwaga: Zbieraj tylko ogólnodostępne dane organizacji i wykorzystuj je do analityki. Telefon i adres firmy nie są danymi osobowymi, ale imiona autorów opinii mogą być nimi uznawane na mocy 152-FZ. Nie przechowuj ich bez potrzeby i nie wykorzystuj zebranych telefonów do masowych wysyłek bez zgody, to narusza prawo o reklamie i zasady Yandex. Pamiętaj też: umowa użytkownika Yandex ogranicza automatyczne zbieranie, więc utrzymuj obciążenie minimalne, a w przypadku projektów komercyjnych o dużych wolumenach rozważ oficjalne API Yandex do wyszukiwania organizacji.

Krok 1: Konfigurujemy środowisko i podłączamy mobilne proxy

Cel etapu: zainstalować wszystkie biblioteki, pobrać przeglądarkę i upewnić się, że zapytania idą przez mobilne proxy, a nie bezpośrednio.

Instalacja bibliotek

  1. Otwórz wiersz poleceń lub terminal.
  2. Przejdź do folderu roboczego poleceniem cd i ścieżką do folderu. Na przykład: cd C:\maps_parser w Windows lub cd ~/maps_parser w macOS i Linux.
  3. Utwórz środowisko wirtualne, aby biblioteki nie przeszkadzały innym projektom: python -m venv venv
  4. Aktywuj je. Windows: venv\Scripts\activate. macOS i Linux: source venv/bin/activate. Na początku wiersza terminala pojawi się napis (venv).
  5. Zainstaluj biblioteki jednym poleceniem:
pip install playwright pandas openpyxl requests
playwright install chromium

Drugie polecenie pobierze Chromium. Zajmuje to 2-5 minut w zależności od szybkości internetu. Poczekaj, aż w terminalu ponownie pojawi się znak zachęty.

Sprawdzenie proxy

Utwórz w edytorze plik check_proxy.py i wklej kod, zastępując LOGIN, HASŁO, HOST i PORT wartościami z panelu klienta:

import requests

proxy = 'http://LOGIN:HASŁO@HOST:PORT'
proxies = {'http': proxy, 'https': proxy}

direct = requests.get('https://api.ipify.org?format=json', timeout=30).json()
via_proxy = requests.get('https://api.ipify.org?format=json', proxies=proxies, timeout=30).json()
print('Twój bezpośredni IP:', direct['ip'])
print('IP przez proxy:', via_proxy['ip'])

Uruchom plik poleceniem python check_proxy.py. Powinieneś zobaczyć dwa różne adresy. Jeśli adresy są takie same lub pojawił się błąd połączenia, proxy nie działa i dalsze działanie nie ma sensu.

Sprawdzenie zmiany IP

Otwórz link zmiany IP z panelu klienta w zwykłej przeglądarce. Zwykle zwraca krótką odpowiedź o udanej zmianie. Poczekaj 15-30 sekund i ponownie uruchom check_proxy.py. Adres przez proxy powinien się zmienić. Zapamiętaj, ile sekund faktycznie potrzeba na zmianę: tę wartość podstawimy do skryptu w piątym kroku.

Wskazówka: Zapisz wszystkie ustawienia proxy w osobnym pliku config.py ze zmiennymi PROXY_HOST, PROXY_PORT, PROXY_LOGIN, PROXY_PASSWORD i CHANGE_IP_URL. Wtedy w pozostałych skryptach wystarczy napisać from config import * i nie przepisywać haseł dziesięć razy.

Sprawdzenie: Polecenie playwright --version wyświetla numer wersji, check_proxy.py pokazuje IP operatora komórkowego, różniący się od Twojego domowego, a po odwiedzeniu linku zmiany adres się zmienia.

Możliwe problemy

  • Błąd „pip nie jest poleceniem wewnętrznym”. Python jest zainstalowany bez dodania do PATH. Przeinstaluj z zaznaczonym polem lub użyj polecenia py -m pip zamiast pip.
  • Błąd 407 Proxy Authentication Required. Błędny login lub hasło. Sprawdź, czy nie ma zbędnych spacji przy kopiowaniu.
  • Timeout przy zapytaniu przez proxy. Port jest błędny lub proxy w tym momencie zmienia IP. Poczekaj pół minuty i powtórz.

Krok 2: Zbieramy linki do kart organizacji

Cel etapu: otrzymać plik links.json z listą adresów kart wszystkich organizacji według potrzebnego zapytania w potrzebnym mieście.

Tutaj tylko raz zaglądniemy do listy wyników Map, aby pobrać z niej linki. Same dane będziemy brać wyłącznie z kart, dlatego lista jest nam potrzebna jako spis treści, nie więcej.

Jak zbudowana jest lista organizacji

Kiedy wpisujesz w Mapach zapytanie typu „stomatologia Kazan”, po lewej pojawia się przewijany panel z snippetami. Każdy snippet zawiera link do karty. Lista ładuje się porcjami w miarę przewijania, więc skrypt będzie kręcił panel kółkiem myszy i zbierał linki po każdym przewinięciu, aż nowe przestaną się pojawiać.

Piszemy skrypt zbierania linków

Utwórz plik collect_links.py:

from playwright.sync_api import sync_playwright
import time, random, json
from config import *

PROXY = {'server': f'http://{PROXY_HOST}:{PROXY_PORT}', 'username': PROXY_LOGIN, 'password': PROXY_PASSWORD}
QUERY = 'stomatologia Kazan'
MAX_SCROLLS = 40

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False, proxy=PROXY)
    context = browser.new_context(locale='ru-RU', viewport={'width': 1400, 'height': 900})
    page = context.new_page()
    page.goto('https://yandex.ru/maps/?text=' + QUERY, wait_until='domcontentloaded')
    time.sleep(random.uniform(5, 8))
    page.mouse.move(350, 500)
    links = set()
    stale = 0
    for i in range(MAX_SCROLLS):
        before = len(links)
        page.mouse.wheel(0, random.randint(1200, 2000))
        time.sleep(random.uniform(1.5, 3.5))
        for a in page.query_selector_all('a[href*="/maps/org/"]'):
            href = a.get_attribute('href') or ''
            clean = href.split('?')[0]
            if clean.startswith('/'):
                clean = 'https://yandex.ru' + clean
            links.add(clean)
        stale = stale + 1 if len(links) == before else 0
        print(f'Przewinięcie {i+1}: linków {len(links)}')
        if stale >= 4:
            break
    with open('links.json', 'w', encoding='utf-8') as f:
        json.dump(sorted(links), f, ensure_ascii=False, indent=2)
    print('Razem zebrano:', len(links))
    browser.close()

Omawiamy, co się dzieje

  1. Linia headless=False otwiera przeglądarkę z oknem. Na etapie debugowania jest to obowiązkowe: na własne oczy zobaczysz, czy mapa się załadowała i czy nie ma captcha.
  2. page.mouse.move(350, 500) ustawia kursor na lewym panelu. Bez tego kółko myszy będzie przesuwać samą mapę, a nie listę.
  3. Selektor a[href*="/maps/org/"] szuka wszystkich linków zawierających fragment /maps/org/. Jest odporniejszy niż konkretne klasy, które Yandex zmienia co kilka miesięcy.
  4. Licznik stale zatrzymuje pętlę, jeśli cztery przewinięcia z rzędu nie przyniosły nowych linków. To znaczy, że lista się skończyła.
  5. Linki są czyszczone z parametrów po znaku zapytania, aby jedna organizacja nie trafiła do pliku dwa razy.

Uruchom skrypt: python collect_links.py. Otworzy się okno przeglądarki, załaduje się mapa z wynikami, panel zacznie się przewijać. W terminalu pobiegnie licznik linków. Dla średniego miasta po jednym zapytaniu zwykle zbiera się od 100 do 400 kart w 2-4 minuty.

Wskazówka: Mapy rzadko zwracają więcej niż 500 wyników na jedno zapytanie. Jeśli potrzebujesz całej niszy w metropolii, podziel zapytanie na dzielnice: „stomatologia rejon Wachitowski Kazan”, „stomatologia rejon Sowiecki Kazan”. Linki z różnych zapytań łącz przez set, jak w kodzie powyżej, duplikaty znikną same.

Sprawdzenie: W folderze pojawił się plik links.json, wewnątrz którego jest lista adresów typu https://yandex.ru/maps/org/nazwa/1234567890/. Otwórz dwa-trzy adresy ręcznie w zwykłej przeglądarce i upewnij się, że to karty potrzebnych organizacji.

Możliwe problemy

  • Zebrano zero linków. Najprawdopodobniej strona nie zdążyła się załadować lub kursor nie był nad listą. Zwiększ pierwszą pauzę do 10 sekund i sprawdź współrzędne myszy: panel musi być pod kursorem.
  • Lista się nie przewija, przesuwa się mapa. Zmniejsz lub zwiększ współrzędną X w mouse.move, kierując się szerokością panelu w Twoim oknie.
  • Od razu otworzyła się captcha. Zmień IP przez link, poczekaj minutę i uruchom ponownie. Jeśli się powtarza, spróbuj innego kanału proxy.

Krok 3: Parsujemy kartę organizacji — nazwa, adres, telefon, strona

Cel etapu: napisać funkcję, która otwiera jedną kartę i zwraca słownik z podstawowymi danymi organizacji, i przepuścić ją przez wszystkie linki z links.json.

Jak samodzielnie znaleźć selektory

Yandex okresowo zmienia nazwy klas w kodzie strony. Dlatego ważne jest, aby umieć je znajdować samodzielnie, a nie polegać wyłącznie na gotowym kodzie. Robi się to tak:

  1. Otwórz dowolną kartę organizacji w zwykłej przeglądarce Chrome.
  2. Kliknij prawym przyciskiem na telefon organizacji i wybierz Zbadaj (lub naciśnij F12 i kliknij na element narzędziem wyboru).
  3. W otwartym panelu zobaczysz podświetlony tag z atrybutem class. Na przykład class="card-phones-view__phone-number". To jest selektor: w kodzie pisze się go z kropką na początku.
  4. Powtórz dla nazwy, adresu, strony, oceny i godzin pracy. Zapisz klasy w notatniku.

Klasa może składać się z kilku słów oddzielonych spacją. Bierz pierwsze, najbardziej „mówiące”, z podwójnym podkreśleniem w środku. W momencie pisania przewodnika aktualne są selektory z kodu poniżej, ale sprawdź je przed uruchomieniem.

Piszemy funkcję parsowania karty

Utwórz plik parse_cards.py:

from playwright.sync_api import sync_playwright
import time, random, json, csv, os
from config import *

PROXY = {'server': f'http://{PROXY_HOST}:{PROXY_PORT}', 'username': PROXY_LOGIN, 'password': PROXY_PASSWORD}
OUT = 'orgs.csv'
FIELDS = ['url', 'name', 'category', 'address', 'phones', 'site', 'rating', 'reviews_count', 'hours']

def grab(page, selector):
    el = page.query_selector(selector)
    return el.inner_text().strip() if el else ''

def parse_card(page, url):
    page.goto(url, wait_until='domcontentloaded')
    time.sleep(random.uniform(3, 6))
    more = page.query_selector('.card-phones-view__more')
    if more:
        more.click()
        time.sleep(random.uniform(1, 2))
    phones = [e.inner_text().strip() for e in page.query_selector_all('.card-phones-view__phone-number')]
    return {
        'url': url,
        'name': grab(page, 'h1.orgpage-header-view__header'),
        'category': grab(page, '.orgpage-categories-info-view'),
        'address': grab(page, '.business-contacts-view__address'),
        'phones': '; '.join(dict.fromkeys(phones)),
        'site': grab(page, '.business-urls-view__text'),
        'rating': grab(page, '.business-rating-badge-view__rating-text'),
        'reviews_count': grab(page, '.business-header-rating-view__text'),
        'hours': grab(page, '.business-working-status-view'),
    }

def load_done():
    if not os.path.exists(OUT):
        return set()
    with open(OUT, encoding='utf-8') as f:
        return {row['url'] for row in csv.DictReader(f)}

links = json.load(open('links.json', encoding='utf-8'))
done = load_done()
todo = [u for u in links if u not in done]
print(f'Razem {len(links)}, zostało {len(todo)}')

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False, proxy=PROXY)
    page = browser.new_context(locale='ru-RU').new_page()
    new_file = not os.path.exists(OUT)
    with open(OUT, 'a', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        if new_file:
            writer.writeheader()
        for n, url in enumerate(todo, 1):
            try:
                row = parse_card(page, url)
                writer.writerow(row)
                f.flush()
                print(f'{n}/{len(todo)} {row["name"]} | {row["phones"]}')
            except Exception as e:
                print('Błąd na', url, e)
            time.sleep(random.uniform(4, 9))
    browser.close()

Co tu jest ważne

  1. Przycisk „Pokaż telefon”. W części kart pełny numer jest ukryty za przyciskiem. Szukamy go po klasie card-phones-view__more i klikamy, jeśli znaleźliśmy. Dopiero potem zbieramy numery.
  2. Zapis po każdej karcie. Funkcja f.flush() wymusza zrzut danych na dysk. Jeśli skrypt padnie, wszystko zebrane pozostanie w orgs.csv.
  3. Wznowienie pracy. Funkcja load_done czyta już zebrane linki, a przy ponownym uruchomieniu skrypt kontynuuje od miejsca zatrzymania, a nie zaczyna od początku.
  4. Pauzy 4-9 sekund między kartami. To tempo uważnego człowieka, który czyta informacje. Nie zmniejszaj go przy pierwszych uruchomieniach.

Uruchom python parse_cards.py i poobserwuj pierwsze pięć-dziesięć kart w oknie przeglądarki. Powinieneś widzieć, jak strona się otwiera, w razie potrzeby rozwija się telefon, a w terminalu pojawia się nazwa i numer.

Uwaga: Jeśli pięć kart z rzędu zwróciło puste nazwy, natychmiast zatrzymaj skrypt kombinacją Ctrl+C. Prawie na pewno Yandex zmienił układ strony i selektory się zdezaktualizowały. Znajdź nowe według instrukcji powyżej i zaktualizuj kod. Kontynuowanie zbierania pustych wierszy nie ma sensu i tylko zwiększa obciążenie proxy.

Wskazówka: Oprócz tekstu warto zapisać sam identyfikator organizacji: to ostatnia liczbowa część URL. Po nim łatwo porównywać bazy między zbiorami i śledzić zamknięte firmy. Dodaj pole 'org_id': url.rstrip('/').split('/')[-1] do słownika.

Sprawdzenie: Plik orgs.csv otwiera się w Excelu, w nim wypełnione kolumny name, address, phones u co najmniej 90 procent wierszy. Telefony wyświetlają się w formacie +7 (843) 000-00-00. Ocena wygląda jak liczba z przecinkiem, na przykład 4,7.

Możliwe problemy

  • Telefony puste, choć na stronie są. Przycisk „Pokaż telefon” ma inną klasę. Znajdź ją przez F12 i zamień w kodzie.
  • Cyrylica w CSV wyświetla się jako krzaczki. Excel nie rozpoznał kodowania. Otwórz plik przez menu Dane, Z tekstu/CSV i wybierz UTF-8, albo poczekaj do kroku 6, gdzie konwertujemy dane do xlsx.
  • Skrypt zawiesza się na jednej karcie. Dodaj parametr timeout=45000 w page.goto, aby po 45 sekundach pojawiał się wyjątek i pętla szła dalej.

Krok 4: Zbieramy opinie z kart

Cel etapu: dla każdej organizacji otrzymać listę opinii z oceną, datą i tekstem i zapisać je w osobnym pliku reviews.csv.

Gdzie mieszkają opinie

Każda karta ma zakładkę „Opinie” z adresem typu https://yandex.ru/maps/org/nazwa/identyfikator/reviews/. Opinie tam ładują się porcjami przy przewijaniu, podobnie jak lista organizacji w drugim kroku. Domyślnie są posortowane według aktualności, a świeże można uzyskać, przełączając sortowanie na „Według nowości”.

Piszemy funkcję zbierania opinii

Utwórz plik parse_reviews.py. Podstawa ta sama, co w poprzednim kroku, więc podamy tylko funkcję i pętlę:

def parse_reviews(page, url, max_scrolls=15):
    page.goto(url.rstrip('/') + '/reviews/', wait_until='domcontentloaded')
    time.sleep(random.uniform(3, 6))
    page.mouse.move(350, 600)
    seen = 0
    for _ in range(max_scrolls):
        page.mouse.wheel(0, random.randint(1500, 2500))
        time.sleep(random.uniform(1.5, 3))
        cards = page.query_selector_all('.business-review-view')
        if len(cards) == seen:
            break
        seen = len(cards)
    result = []
    for c in page.query_selector_all('.business-review-view'):
        def sub(sel):
            el = c.query_selector(sel)
            return el.inner_text().strip() if el else ''
        stars = c.query_selector_all('.business-rating-badge-view__star._full')
        result.append({
            'org_url': url,
            'author': sub('.business-review-view__author-name'),
            'date': sub('.business-review-view__date'),
            'stars': len(stars),
            'text': sub('.business-review-view__body-text'),
        })
    return result

W pętli po linkach wywołuj parse_reviews zamiast parse_card i zapisuj każdy element listy osobnym wierszem do reviews.csv z polami org_url, author, date, stars, text. Logika wznowienia i zapis po każdej organizacji pozostają takie same.

Omawiamy szczegóły

  1. Ograniczenie max_scrolls=15. U popularnych miejsc bywa dwa-trzy tysiące opinii. Zbieranie ich wszystkich rzadko jest potrzebne, a czasu i zapytań to zjada dużo. Piętnaście przewinięć zwykle wystarcza na 100-150 ostatnich opinii.
  2. Ocena przez gwiazdki. Liczba punktów w opinii nie jest napisana tekstem, a narysowana gwiazdkami. Liczymy elementy z modyfikatorem _full, czyli zamalowane gwiazdki.
  3. Długie opinie. Część tekstów jest zwinięta i wymaga kliknięcia „Więcej”. Jeśli potrzebujesz pełnego tekstu, przed zbieraniem kliknij wszystkie przyciski z klasą business-review-view__expand wewnątrz karty.

Uwaga: Imiona autorów opinii to dane użytkowników, a nie organizacji. Jeśli Twoim zadaniem jest analiza sentymentu lub szukanie typowych skarg, pole author nie jest Ci potrzebne. Nie zbieraj go bez celu, tak zdejmujesz z siebie zbędne pytania dotyczące 152-FZ. Jeśli pole jednak jest potrzebne, przechowuj plik lokalnie i nie przekazuj osobom trzecim.

Wskazówka: Uruchamiaj zbieranie opinii nie po wszystkich organizacjach, a po przefiltrowanej liście: na przykład tylko z oceną poniżej 4,0 lub tylko u bezpośrednich konkurentów. Tak znacząco zmniejszysz wolumen zapytań, a wartość danych nie ucierpi.

Sprawdzenie: W reviews.csv u każdej organizacji od 20 do 150 wierszy, kolumna stars zawiera liczby od 1 do 5, w text jest sensowny rosyjski tekst. Data wygląda jak „15 stycznia” lub „3 marca 2026”.

Możliwe problemy

  • Znalazło się zero opinii. Sprawdź, że URL kończy się na /reviews/, a panel opinii znajduje się pod kursorem przy przewijaniu.
  • U wszystkich opinii stars wynosi 0. Klasa zamalowanej gwiazdki się zmieniła. Znajdź ją przez F12, klikając na gwiazdkę.
  • Opinie się duplikują. Panel przewinął się nie do końca i jeden blok został policzony dwa razy. Usuń duplikaty w szóstym kroku po parze author plus text.

Krok 5: Konfigurujemy rotację IP i ochronę przed blokadami

Cel etapu: nauczyć parser zachowywać się jak akuratny użytkownik: zmieniać IP według harmonogramu, rozpoznawać captcha i automatycznie zwalniać tempo, zamiast wbijać się w blokadę.

Dlaczego w ogóle pojawiają się bany

Yandex nie zabrania przeglądania kart, ale śledzi anomalie: setki stron na minutę z jednego adresu, identyczne odstępy między zapytaniami, brak ruchów myszy, puste ciasteczka. Kiedy podejrzenie się kumuluje, pojawia się strona SmartCaptcha, a przy uporze — czasowe ograniczenie dla IP. Nasza strategia nie polega na „przebijaniu” zabezpieczeń, a na tym, aby nie dawać powodów do ich włączenia.

Trzy zasady spokojnego parsera Yandex Maps

  1. Tempo człowieka. Nie więcej niż 8-12 kart na minutę na jedno IP. Losowe pauzy, a nie stałe.
  2. Regularna zmiana IP. Co 25-40 kart lub co 10-15 minut odwołujemy się do linku zmiany adresu. Mobilne proxy w kilka sekund otrzymuje nowy IP operatora, a historia zapytań „zeruje się” z punktu widzenia strony.
  3. Natychmiastowy odwrót przy captcha. Zobaczyłeś weryfikację — nie próbujemy jej przejść, a robimy pauzę na 2-3 minuty, zmieniamy IP i kontynuujemy od tej samej karty w nowym kontekście przeglądarki.

Dodajemy rotację do kodu

Wklej do parse_cards.py i parse_reviews.py następujące funkcje i wywołuj je w głównej pętli:

import requests

def change_ip():
    try:
        r = requests.get(CHANGE_IP_URL, timeout=30)
        print('Zmiana IP:', r.status_code)
    except Exception as e:
        print('Nie udało się zmienić IP:', e)
    time.sleep(IP_CHANGE_WAIT)

def is_captcha(page):
    if 'showcaptcha' in page.url or 'checkcaptcha' in page.url:
        return True
    return page.query_selector('.CheckboxCaptcha') is not None

def new_page(browser):
    ctx = browser.new_context(locale='ru-RU', viewport={'width': random.choice([1366, 1440, 1536]), 'height': 900})
    return ctx.new_page()

A główna pętla przyjmuje taki wygląd:

page = new_page(browser)
since_change = 0
for n, url in enumerate(todo, 1):
    if since_change >= random.randint(25, 40):
        page.context.close()
        change_ip()
        page = new_page(browser)
        since_change = 0
    row = parse_card(page, url)
    if is_captcha(page):
        print('Captcha! Pauza i zmiana IP')
        page.context.close()
        time.sleep(random.uniform(120, 180))
        change_ip()
        page = new_page(browser)
        row = parse_card(page, url)
    writer.writerow(row)
    f.flush()
    since_change += 1
    time.sleep(random.uniform(4, 9))

Co ważne zrozumieć

  • Nowy kontekst razem z nowym IP. Zamykając kontekst, resetujemy ciasteczka i pamięć lokalną. Zmiana adresu bez resetu ciasteczek jest bezsensowna: strona nadal będzie Cię rozpoznawać po sesji.
  • Zmienna IP_CHANGE_WAIT w config.py — to czas zmierzony w pierwszym kroku, zwykle 15-30 sekund. Nie można ustawić mniej: przeglądarka otworzy stronę przez stary adres.
  • Losowy rozmiar okna dodaje różnorodności odciskowi palca przeglądarki. To miękki środek, ale jest darmowy.
  • Mobilne proxy z rotacją po linku są tu wygodniejsze niż jakiekolwiek inne: nie przełączasz się między dziesiątkami adresów ręcznie, a po prostu szarpiesz jeden URL.

Wskazówka: Załóż prosty log: zapisuj do pliku czas, numer karty i zdarzenie (sukces, captcha, zmiana IP). Po tygodniu po logu dokładnie zobaczysz, przy jakim tempie captcha nie pojawia się wcale, i dostroisz pauzy pod swój kanał proxy.

Sprawdzenie: W ciągu godziny ciągłej pracy skrypt zebrał 400-600 kart, w terminalu nie więcej niż jedno-dwa komunikaty o captcha, po każdym z nich zbieranie kontynuowało się automatycznie. IP przez proxy zmieniło się minimum dziesięć razy (widać to po wierszach „Zmiana IP: 200”).

Możliwe problemy

  • Captcha pojawia się co 10 kart. Tempo zbyt wysokie dla Twojego kanału. Zwiększ pauzy do 8-15 sekund i zmieniaj IP co 15 kart.
  • Po zmianie IP strony się nie ładują. Zwiększ IP_CHANGE_WAIT: operator jeszcze nie wydał nowego adresu.
  • Link zmiany IP zwraca błąd o zbyt częstych odwołaniach. U większości taryf jest minimalny odstęp między zmianami, zwykle od jednej do dwóch minut. Nie zmieniaj IP częściej.

Krok 6: Oczyszczamy dane i zapisujemy do Excela

Cel etapu: zmienić surowe CSV w schludne tabele Excel bez duplikatów, z znormalizowanymi telefonami i liczbową oceną.

Piszemy skrypt czyszczenia

Utwórz plik clean_export.py:

import pandas as pd

def norm_phone(value):
    out = []
    for raw in str(value).split(';'):
        digits = ''.join(ch for ch in raw if ch.isdigit())
        if len(digits) == 11 and digits[0] in '78':
            out.append('+7' + digits[1:])
        elif len(digits) == 10:
            out.append('+7' + digits)
    return '; '.join(dict.fromkeys(out))

orgs = pd.read_csv('orgs.csv', encoding='utf-8')
orgs = orgs.drop_duplicates(subset='url')
orgs['phones'] = orgs['phones'].fillna('').apply(norm_phone)
orgs['rating'] = pd.to_numeric(orgs['rating'].astype(str).str.replace(',', '.'), errors='coerce')
orgs['reviews_count'] = pd.to_numeric(orgs['reviews_count'].astype(str).str.extract('(\d+)')[0], errors='coerce')
orgs = orgs.sort_values('rating', ascending=False)

reviews = pd.read_csv('reviews.csv', encoding='utf-8')
reviews = reviews.drop_duplicates(subset=['org_url', 'author', 'text'])

with pd.ExcelWriter('yandex_maps_result.xlsx', engine='openpyxl') as w:
    orgs.to_excel(w, sheet_name='Organizacje', index=False)
    reviews.to_excel(w, sheet_name='Opinie', index=False)
print('Organizacji:', len(orgs), 'Opinii:', len(reviews))

W wierszu z wyodrębnianiem liczby opinii używa się wyrażenia regularnego z jednego odwrotnego ukośnika i litery d w nawiasach: wyciąga ono pierwszą liczbę z tekstu typu „312 opinii”. Skopiuj je uważnie.

Co robi skrypt

  1. Usuwa powtórzenia organizacji po URL.
  2. Doprowadza wszystkie telefony do formatu +7XXXXXXXXXX, usuwając nawiasy, spacje i myślniki. Taki format jest wygodny dla CRM i porównywania baz.
  3. Zamienia przecinek na kropkę w ocenie, aby Excel postrzegał ją jako liczbę i pozwalał sortować.
  4. Wyciąga liczbę opinii z ciągu tekstowego.
  5. Usuwa duplikaty opinii i zapisuje dwa arkusze w jednym pliku xlsx.

Uruchom python clean_export.py i otwórz yandex_maps_result.xlsx. Na pierwszym arkuszu organizacje są posortowane według oceny, telefony jednolite, na drugim arkuszu — opinie z powiązaniem z organizacją po kolumnie org_url.

Wskazówka: Dodaj do skryptu kolumnę „data zbierania” z bieżącą datą. Po miesiącu powtórz zbieranie i porównaj tabele funkcją merge w pandas: zobaczysz nowe organizacje, zamknięte punkty i zmiany ocen konkurentów. To już pełnoprawny monitoring rynku.

Sprawdzenie: Plik xlsx otwiera się bez ostrzeżeń, cyrylica się czyta, kolumna rating sortuje się jako liczba, w kolumnie phones nie ma nawiasów i spacji, liczba wierszy na arkuszu „Organizacje” zgadza się z liczbą unikalnych linków w links.json minus błędy.

Sprawdzenie wyniku: lista kontrolna gotowego parsera

Przejdź przez listę. Jeśli na każdy punkt odpowiedziałeś „tak”, parser Yandex Maps jest gotowy do regularnej pracy.

Lista kontrolna

  • check_proxy.py pokazuje IP operatora komórkowego, różny od domowego.
  • collect_links.py zbiera ponad 50 linków po średnim zapytaniu i zatrzymuje się sam.
  • parse_cards.py wypełnia nazwę, adres i telefon u co najmniej 90 procent kart.
  • Przycisk „Pokaż telefon” rozwija się automatycznie.
  • parse_reviews.py zwraca opinie z oceną od 1 do 5.
  • Przy captcha skrypt robi pauzę, zmienia IP i kontynuuje bez Twojego udziału.
  • Po awaryjnym zatrzymaniu ponowne uruchomienie kontynuuje od miejsca przerwania.
  • clean_export.py tworzy xlsx z dwoma arkuszami i znormalizowanymi telefonami.

Jak przetestować całość

  1. Weź niewielkie zapytanie, po którym w mieście jest 30-60 organizacji, na przykład „wulkanizacja” w mieście powiatowym.
  2. Przepuść wszystkie skrypty po kolei i zmierz czas. Na 50 kart z opiniami powinno pójść 15-25 minut.
  3. Wybierz pięć losowych organizacji z tabeli i sprawdź telefony i adresy z kartami ręcznie.
  4. Zatrzymaj parse_cards.py w połowie kombinacją Ctrl+C i uruchom ponownie. Upewnij się, że licznik „zostało” się zmniejszył, a nie zresetował.

Wskaźniki udanego wykonania

  • Dokładność danych przy ręcznym porównaniu: 100 procent zgodności po telefonach i adresach.
  • Udział pustych nazw: mniej niż 3 procent.
  • Częstotliwość captcha: nie więcej niż jedna na 200-300 kart.
  • Prędkość: 400-600 kart na godzinę na jeden kanał proxy przy bezpiecznym tempie.

Typowe błędy i ich rozwiązania

Zebraliśmy problemy, z którymi spotyka się prawie każdy, kto po raz pierwszy pisze parser Yandex Maps, i sposoby ich usunięcia.

Puste pola u większości kart

Przyczyna: Yandex zaktualizował układ strony, klasy elementów się zmieniły. Rozwiązanie: otwórz kartę w Chrome, naciśnij F12, znajdź nowe klasy i zamień je w funkcji parse_card. Trzymaj selektory w jednym słowniku na początku pliku, aby poprawiać jedno miejsce.

Captcha pojawia się już od pierwszej strony

Przyczyna: IP proxy jest już „zmęczony” poprzednią aktywnością albo przeglądarka uruchamia się z podejrzanymi parametrami. Rozwiązanie: zmień IP przed startem, upewnij się, że locale='ru-RU' jest ustawione, i nie używaj trybu headless przy pierwszych uruchomieniach: daje więcej sygnałów automatyzacji.

Przewijanie listy przesuwa mapę, a nie panel

Przyczyna: kursor myszy znajduje się poza lewym panelem. Rozwiązanie: dobierz współrzędne page.mouse.move pod swój rozmiar okna. Przy szerokości 1400 pikseli panel zajmuje około pierwszych 450 pikseli w poziomie.

Zbierają się wciąż te same 20 organizacji

Przyczyna: panel nie przewija się do końca, licznik stale zadziała za wcześnie. Rozwiązanie: zwiększ pauzę po przewinięciu do 3-4 sekund i próg stale do 6, Mapy czasem ładują następną porcję powoli.

Telefony widoczne w przeglądarce, ale w tabeli pusto

Przyczyna: numer pojawia się dopiero po kliknięciu, a skrypt zbiera dane przed jego zakończeniem, albo przycisk ma inną klasę. Rozwiązanie: zwiększ pauzę po kliknięciu do 2-3 sekund i sprawdź klasę przycisku.

Błąd Target closed lub Browser has been closed

Przyczyna: zamknąłeś kontekst przy zmianie IP, ale nadal używasz starego obiektu page. Rozwiązanie: upewnij się, że po każdym page.context.close() zmienna page jest przypisywana na nowo przez new_page(browser), jak w przykładzie kroku 5.

Po zmianie IP strony ładują się wiecznie

Przyczyna: operator jeszcze nie przydzielił nowego adresu, proxy jest w stanie przejściowym. Rozwiązanie: zwiększ IP_CHANGE_WAIT do 30-40 sekund i dodaj timeout w page.goto, aby zawieszenie nie blokowało pętli.

Excel otwiera CSV z krzaczkami

Przyczyna: Excel nie rozpoznaje UTF-8 bez znacznika BOM. Rozwiązanie: używaj clean_export.py i pracuj z xlsx albo przy zapisie CSV podaj encoding='utf-8-sig'.

Dodatkowe możliwości dla zaawansowanych

Podstawowy parser już rozwiązuje 90 procent zadań. Jeśli chcesz więcej szybkości i danych, oto kierunki rozwoju.

Przechwytywanie odpowiedzi sieciowych zamiast rozbierania kodu strony

Mapy ładują dane kart w formacie JSON osobnymi zapytaniami. Playwright umie subskrybować je przez page.on('response', handler). Wewnątrz handlera sprawdzaj, czy response.url zawiera fragment /maps/api/, i zapisuj response.json(). Plus metody: dane są ustrukturyzowane i nie zależą od klas układu strony. Minus: wewnętrzne adresy zmieniają się bez ostrzeżenia, a rozbieranie zagnieżdżonego JSON jest trudniejsze niż czytanie tekstu ze strony. Podejście dobrze łączyć z podstawowym: jeśli odpowiedź JSON przyszła — bierzemy ją, inaczej spadamy na rozbiór HTML.

Równoległa praca z kilkoma kanałami proxy

Jedno mobilne proxy przy bezpiecznym tempie daje 400-600 kart na godzinę. Jeśli potrzeba szybciej, kup dwa-trzy kanały i uruchom po osobnym procesie na każdy, dzieląc links.json na równe części. Nie uruchamiaj kilku przeglądarek przez jeden kanał: łączne tempo na IP wzrośnie i captcha wróci. Do orkiestracji wystarczy prosty skrypt-uruchamiacz na subprocess albo biblioteka asyncio z async_playwright, gdzie każdy worker dostaje swój kontekst i swoje proxy w parametrze proxy u new_context.

Monitoring zmian

Zapisuj wyniki każdego zbierania do osobnego pliku z datą i porównuj je po org_id. Pojawione identyfikatory — nowi gracze na rynku, zniknięte — zamknięte, zmiana rating i reviews_count — dynamika reputacji. Ustaw uruchamianie raz na dwa tygodnie przez Harmonogram zadań Windows lub cron, i będziesz mieć żywą mapę niszy.

Rozszerzanie pól

W kartach są też inne przydatne bloki: lista usług z cenami, linki do social mediów, oznaczenie „Zweryfikowana organizacja”, liczba zdjęć, najbliższe metro. Każde pole dodaje się według tego samego schematu: znaleźć klasę przez F12, dodać grab do słownika. Szczególnie cenny jest blok cen dla arbitrażystów oceniających średni czek niszy.

Analiza opinii

Zebrane teksty świetnie układają się w prostą analitykę: policz częstotliwość słów wśród opinii z jedną-dwiema gwiazdkami i otrzymasz listę głównych pretensji klientów do konkurentów. Do tego wystarczy pandas i Counter ze standardowej biblioteki. Zaawansowany wariant — przepuścić teksty przez model językowy do klasyfikacji tematycznej: cena, jakość, obsługa, oczekiwanie.

Oficjalne API jako alternatywa

Dla dużych projektów komercyjnych rozważ API Yandex do wyszukiwania organizacji. Zwraca nazwę, adres, telefon i kategorie w ustrukturyzowanej formie i nie tworzy ryzyka blokad wcale. Opinii w nim nie ma, limity są płatne, ale do zbierania bazy kontaktów to najczystsza droga. Parser kart w takim przypadku pozostaje narzędziem do opinii i pól, których w API nie ma.

FAQ: częste pytania o parsowanie Yandex Maps

Czy zbieranie telefonów organizacji z Yandex Maps jest legalne?

Dane kontaktowe firm są publikowane publicznie przez same organizacje i nie są danymi osobowymi. Zbieranie do własnej analizy jest dopuszczalne. Ale używanie tych numerów do masowych telefonów i wysyłek bez zgody narusza prawo o reklamie. Pamiętaj też o ograniczeniach umowy użytkownika Yandex na automatyczne zbieranie i utrzymuj obciążenie minimalne.

Dlaczego nie można obejść się zwykłymi zapytaniami requests bez przeglądarki?

Mapy są w całości rysowane kodem JavaScript. Serwer zwraca prawie pusty HTML, a dane ładują się później. requests otrzyma szkielet bez telefonów i adresów. Dlatego potrzebny jest Playwright z prawdziwym Chromium.

Czy trzeba koniecznie używać mobilnych proxy, czy można parsować z domowego IP?

Technicznie pierwsze 50-100 kart zbierze się i tak. Ale potem pojawi się captcha, a domowe IP na kilka godzin trafi pod ograniczenia i nie będziesz mógł normalnie korzystać z Yandex. Mobilne proxy rozwiązują problem na dwa sposoby: adres operatora komórkowego od początku wzbudza więcej zaufania, a rotacja po linku pozwala rozkładać obciążenie między adresami bez zatrzymywania zbierania.

Ile kart można zebrać dziennie z jednego proxy?

Przy bezpiecznym tempie 8-12 kart na minutę z pauzami i zmianą IP co 30 kart — około 5-8 tysięcy na dobę ciągłej pracy. Wraz z opiniami wolumen zmniejszy się dwa-trzy razy, bo każda strona opinii wymaga przewijania.

Co robić, jeśli Yandex zmienił układ strony i parser się zepsuł?

To standardowa sytuacja, zdarza się kilka razy w roku. Otwórz kartę, naciśnij F12, znajdź nowe klasy potrzebnych elementów i zamień je w kodzie. Zajmuje 10-15 minut. Aby uprościć proces, trzymaj wszystkie selektory w jednym słowniku na początku pliku.

Jak zebrać organizacje z całego regionu, a nie z jednego miasta?

Zrób listę miejscowości i uruchamiaj collect_links.py w pętli, podstawiając nazwę w QUERY. Łącz linki w jeden set. Dla dużych miast dodatkowo dziel na dzielnice, bo jedno zapytanie rzadko zwraca więcej niż 500 wyników.

Czy można uruchamiać parser w tle bez okna przeglądarki?

Tak, ustaw headless=True. Ale rób to dopiero po tym, jak zdebugowałeś selektory i upewniłeś się, że captcha się nie pojawia. W trybie bez okna trudniej zauważyć problem, a niektóre oznaki automatyzacji ujawniają się silniej. Kompromis: headless=True plus zrzut ekranu strony przy każdym błędzie przez page.screenshot(path='error.png').

Jak zrozumieć, że skrypt dostał captcha, jeśli nie patrzę na ekran?

Funkcja is_captcha z kroku 5 sprawdza adres strony i obecność bloku weryfikacji. Dodaj wysyłanie powiadomienia do siebie, na przykład zapis do pliku logu lub wiadomość w komunikatorze przez bota, i dowiesz się o problemie od razu.

Opinie zbierają się nie wszystkie, tylko ostatnie sto. Jak zdobyć więcej?

Zwiększ max_scrolls w parse_reviews do 50-100. Weź pod uwagę, że każde przewinięcie to dodatkowe zapytanie do serwera, więc dla organizacji z tysiącami opinii zbieranie zajmie kilka minut i będzie wymagało częstszej zmiany IP.

Czym ten sposób jest lepszy od gotowych serwisów parsujących?

Masz pełną kontrolę nad polami, tempem i świeżością danych, nie płacisz za każdy wiersz i nie zależysz od cudzego harmonogramu aktualizacji. Gotowe serwisy są wygodne do jednorazowego zadania na paręset kart, a własny parser Yandex Maps zwraca się już przy drugim zbieraniu.

Zakończenie

Podsumujmy. Zainstalowałeś Python i Playwright, podłączyłeś mobilne proxy i sprawdziłeś zmianę IP. Zebrałeś linki do kart organizacji według zapytania i miasta. Napisałeś funkcję, która otwiera każdą kartę, rozwija ukryty telefon i pobiera nazwę, adres, stronę, ocenę i godziny pracy. Dodałeś zbieranie opinii z ocenami. Nauczyłeś parser zmieniać IP według harmonogramu i poprawnie reagować na captcha. W końcu oczyściłeś dane i otrzymałeś schludny Excel z dwoma arkuszami.

Najważniejsze, co warto zapamiętać: stabilność parsera Yandex Maps opiera się nie na sztuczkach, a na trzech rzeczach — ludzkim tempie, regularnej rotacji adresów przez mobilne proxy i gotowości do zatrzymania się przy pierwszym sygnale. Skrypt, który szanuje zasób, działa miesiącami bez interwencji.

Co robić dalej

  • Uruchom pierwsze pełne zbieranie po swojej niszy i sprawdź pięć-dziesięć kart ręcznie.
  • Ustaw zbieranie na harmonogram raz na dwa tygodnie i zacznij gromadzić historię zmian.
  • Spróbuj przechwytywania odpowiedzi JSON z bloku dla zaawansowanych, aby mniej zależeć od układu strony.
  • Jeśli wolumeny rosną, dodaj drugi kanał proxy i zrównoleglij pracę.

Kierunki rozwoju

Następny logiczny krok — automatyczna analityka zebranych opinii i połączenie tabeli z Twoim CRM lub panelem reklamowym. A jeśli pracujesz z kilkoma platformami, to samo podejście z Playwright i mobilnymi proxy przenosi się na dowolne strony z dynamicznym ładowaniem. Zasady są takie same, zmieniają się tylko selektory. Udanych zbiorów i czystych danych!