Jak scrapować Twitter/X z proxy w 2026: Przewodnik po danych publicznych

Kompletny przewodnik po scrapowaniu publicznych danych Twitter/X przy użyciu proxy rezydencjalnych. Dowiedz się, jak obsługiwać limity API, rate limiting i GraphQL payloads w Pythonie.

Jak scrapować Twitter/X z proxy w 2026: Przewodnik po danych publicznych

Ważne zastrzeżenie prawne: Scrapowanie danych publicznych może naruszać Warunki Korzystania (ToS) platformy oraz obowiązujące przepisy prawa, takie jak CFAA w USA czy RODO w UE. Ten artykuł ma charakter wyłącznie edukacyjny i dotyczy dostępu do legalnych danych publicznych. Przed podjęciem jakichkolwiek działań należy skonsultować się z prawnikiem oraz zapoznać z regulaminem każdej platformy. W wielu przypadkach oficjalne API jest właściwym i etycznym rozwiązaniem.

Dlaczego zespoły scrapują Twitter/X w 2026 roku

W 2023 roku Twitter (obecnie X) drastycznie zmienił politykę dostępu do API. Darmowy tier, który wcześniej umożliwiał tysiące zapytań dziennie, został całkowicie usunięty. Podstawowy płatny plan zaczyna się od 100 USD miesięcznie przy drastycznie ograniczonych limitach. Dla wielu startupów, badaczy i zespołów growthowych koszty te stały się nie do zaakceptowania.

W rezultacie wiele organizacji zwróciło się ku scrapowaniu stron publicznych — podejściu, które przed zmianami API było rzadziej stosowane. To jednak wprowadza nowe wyzwania techniczne i prawne, które omówimy w tym przewodniku.

Kluczowe zmiany w ekosystemie X API

  • Usunięcie darmowego tieru — wcześniej dostępne 500,000 tweetów/miesiąc za darmo przestało istnieć
  • Basic tier: 100 USD/miesiąc — zaledwie 10,000 postów miesięcznie, bez dostępu do pełnego wyszukiwania
  • Pro tier: 5,000 USD/miesiąc — dopiero tutaj otrzymujemy znaczące limity
  • Enterprise — niestandardowe ceny, wymagana umowa

Dla porównania, scrapowanie strony publicznej może teoretycznie zapewnić dostęp do znacznie większej ilości danych — ale wiąże się z istotnymi ryzykami i ograniczeniami, które omówimy szczegółowo.

Jakie dane są dostępne publicznie na X

Strona X.com to nowoczesna Single Page Application (SPA), która ładuje dane poprzez endpointy GraphQL. Zrozumienie, co jest dostępne bez logowania, jest kluczowe dla planowania projektu scrapowania.

Dostępne bez zalogowania

  • Profile użytkowników — nazwa, opis, liczba obserwujących, data dołączenia
  • Pojedyncze tweety — treść, timestamp, liczby like/retweet/reply
  • Wątki odpowiedzi — ograniczone do kilku poziomów głębokości
  • Trending topics — aktualne tematy w danej lokalizacji
  • Wyniki wyszukiwania — ale z drastycznie ograniczonymi limitami i wymaganiami CAPTCHA

Wymagające zalogowania

  • Kompletne timeliney — X ogranicza widoczność historii dla niezalogowanych
  • Zaawansowane wyszukiwanie — filtry daty, lokalizacji, typu mediów
  • Listy i Communities — całkowicie za ścianą logowania
  • Obserwujący/obserwowani — tylko fragmentaryczne dane
  • Direct Messages — nigdy nie były publiczne

Kluczowa uwaga: X stosuje agresywne rate limiting dla sesji niezalogowanych. Po kilku zapytaniach z tego samego IP pojawi się CAPTCHA lub błąd 429. To właśnie dlatego proxy rezydencjalne są niezbędne.

Dlaczego proxy rezydencjalne są niezbędne do scrapowania X

X Corporation inwestuje znaczące zasoby w wykrywanie i blokowanie scrapowania. Systemy anty-botowe analizują wiele sygnałów:

Detekcja IP i typy proxy

Typ proxy Wykrywalność Użyteczność dla X
Datacenter Bardzo wysoka — IP są w znanych zakresach Słaba — blokowane niemal natychmiast
Shared/reseller Wysoka — te same IP używane przez wielu Umiarkowana — szybkie rate limiting
Residential rotating Niska — IP wyglądają jak zwykli użytkownicy Dobra — ale wymaga rotacji
Mobile 4G/5G Bardzo niska — IP operatorów komórkowych Najlepsza — najtrudniejsze do zablokowania

Proxy rezydencjalne pochodzą z rzeczywistych adresów domowych — są przypisane do dostawców internetowych takich jak Orange, UPC czy Netia w Polsce. Dla systemów X takie połączenia wyglądają jak zwykli użytkownicy domowi, co znacznie utrudnia automatyczne blokowanie.

Mechanizmy obronne X

X stosuje wielowarstwowe podejście do ochrony przed botami:

  • IP reputation scoring — każde IP ma historię; podejrzane zachowanie obniża reputację
  • Browser fingerprinting — analiza User-Agent, rozdzielczości, czcionek, wtyczek
  • Behavioral analysis — prędkość zapytań, wzorce nawigacji, kliknięcia
  • CAPTCHA challenges — arkoselabs lub hCaptcha dla podejrzanych sesji
  • Rate limiting tiers — różne limity dla zalogowanych vs niezalogowanych

Proxy rezydencjalne rozwiązuje tylko część tego problemu — IP reputation. Reszta wymaga odpowiedniej konfiguracji przeglądarki i strategii scrapowania.

Implementacja w Pythonie z Playwright i proxy

Przyjrzyjmy się praktycznej implementacji scrapowania X z wykorzystaniem Playwright — narzędzia, które automatyzuje prawdziwą przeglądarkę, co pomaga w obejściu fingerprintingu.

Podstawowa konfiguracja

import asyncio
from playwright.async_api import async_playwright
import json

# Konfiguracja proxy rezydencjalnego ProxyHat
PROXY_URL = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"

async def scrape_tweet(tweet_id: str):
    """Scrapuje pojedynczy tweet z X.com"""
    
    async with async_playwright() as p:
        # Uruchom przeglądarkę z proxy
        browser = await p.chromium.launch(
            proxy={"server": PROXY_URL},
            headless=True
        )
        
        context = await browser.new_context(
            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            viewport={"width": 1920, "height": 1080}
        )
        
        page = await context.new_page()
        
        try:
            # Nawiguj do tweetu
            url = f"https://x.com/i/status/{tweet_id}"
            await page.goto(url, wait_until="networkidle", timeout=30000)
            
            # Czekaj na załadowanie treści
            await page.wait_for_selector('[data-testid="tweet"]', timeout=10000)
            
            # Wyciągnij dane z DOM
            tweet_data = await page.evaluate("""() => {
                const tweet = document.querySelector('[data-testid="tweet"]');
                if (!tweet) return null;
                
                return {
                    text: tweet.querySelector('[data-testid="tweetText"]')?.innerText,
                    author: tweet.querySelector('[data-testid="User-Name"]')?.innerText,
                    time: tweet.querySelector('time')?.getAttribute('datetime'),
                    likes: tweet.querySelector('[data-testid="like"]')?.innerText,
                    retweets: tweet.querySelector('[data-testid="retweet"]')?.innerText
                };
            }""")
            
            return tweet_data
            
        except Exception as e:
            print(f"Błąd scrapowania: {e}")
            return None
            
        finally:
            await browser.close()

# Przykład użycia
asyncio.run(scrape_tweet("1234567890"))

Obsługa GraphQL payloads

X używa wewnętrznych endpointów GraphQL do ładowania danych. Możemy przechwytywać te zapytania, co jest bardziej niezawodne niż parsowanie DOM:

import asyncio
from playwright.async_api import async_playwright
import json

PROXY_URL = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"

class TwitterGraphQLScraper:
    def __init__(self):
        self.collected_data = []
        
    async def capture_graphql(self, response):
        """Przechwytuje odpowiedzi GraphQL"""
        if "graphql" in response.url:
            try:
                data = await response.json()
                self.collected_data.append({
                    "url": response.url,
                    "data": data
                })
            except:
                pass
    
    async def scrape_profile(self, username: str):
        """Scrapuje profil użytkownika"""
        async with async_playwright() as p:
            browser = await p.chromium.launch(
                proxy={"server": PROXY_URL},
                headless=True
            )
            
            context = await browser.new_context(
                user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
            )
            page = await context.new_page()
            
            # Rejestruj handler dla odpowiedzi
            page.on("response", self.capture_graphql)
            
            try:
                await page.goto(
                    f"https://x.com/{username}",
                    wait_until="networkidle",
                    timeout=45000
                )
                
                # Czekaj na dane
                await asyncio.sleep(3)
                
                # Przetwórz zebrane dane GraphQL
                for item in self.collected_data:
                    if "UserBy" in item["url"]:
                        user_data = item["data"].get("data", {}).get("user", {})
                        return {
                            "id": user_data.get("id_str"),
                            "name": user_data.get("name"),
                            "screen_name": user_data.get("screen_name"),
                            "description": user_data.get("description"),
                            "followers_count": user_data.get("followers_count"),
                            "friends_count": user_data.get("friends_count"),
                            "created_at": user_data.get("created_at")
                        }
                        
            except Exception as e:
                print(f"Błąd: {e}")
                return None
                
            finally:
                await browser.close()
        
        return None

# Użycie
scraper = TwitterGraphQLScraper()
result = asyncio.run(scraper.scrape_profile("elonmusk"))
print(json.dumps(result, indent=2))

Rotacja proxy dla skali

Do scrapowania wielu stron musimy rotować IP. ProxyHat obsługuje to poprzez flagi w nazwie użytkownika:

import random
import string

PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080
PROXY_USER = "YOUR_USER"
PROXY_PASS = "YOUR_PASS"

def get_rotating_proxy_url(country=None):
    """Generuje URL z rotującym proxy rezydencjalnym"""
    session_id = ''.join(random.choices(string.ascii_lowercase, k=8))
    
    username = f"{PROXY_USER}-session-{session_id}"
    if country:
        username = f"{username}-country-{country}"
    
    return f"http://{username}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"

# Przykład: rotacja IP dla każdego zapytania
async def scrape_multiple_tweets(tweet_ids):
    results = []
    
    for tweet_id in tweet_ids:
        # Nowy IP dla każdego tweetu
        proxy_url = get_rotating_proxy_url(country="US")
        
        async with async_playwright() as p:
            browser = await p.chromium.launch(
                proxy={"server": proxy_url},
                headless=True
            )
            # ... reszta kodu
            
        await asyncio.sleep(random.uniform(2, 5))  # Random delay
    
    return results

Obsługa rate limiting i błędów 429

X stosuje sophisticated rate limiting, który działa na wielu poziomach. Zrozumienie tych mechanizmów jest kluczowe dla stabilnego scrapowania.

Typy rate limiting na X

  1. IP-level throttling — limity per IP address, reset co 15 minut
  2. Session-level limits — limity per cookie session
  3. Account-level limits — dla zalogowanych kont
  4. Sliding window detection — wykrywanie wzorców w czasie

Strategie radzenia sobie z limitami

import asyncio
import random
from datetime import datetime, timedelta

class RateLimitHandler:
    def __init__(self, max_requests_per_ip=10, cooldown_minutes=15):
        self.max_requests = max_requests_per_ip
        self.cooldown = cooldown_minutes
        self.request_history = {}
        
    def can_make_request(self, ip_identifier):
        """Sprawdza czy możemy wykonać zapytanie"""
        now = datetime.now()
        
        if ip_identifier not in self.request_history:
            self.request_history[ip_identifier] = []
            return True
            
        # Usuń stare zapytania (poza oknem czasowym)
        cutoff = now - timedelta(minutes=self.cooldown)
        self.request_history[ip_identifier] = [
            t for t in self.request_history[ip_identifier] 
            if t > cutoff
        ]
        
        return len(self.request_history[ip_identifier]) < self.max_requests
    
    def record_request(self, ip_identifier):
        """Rejestruje wykonane zapytanie"""
        if ip_identifier not in self.request_history:
            self.request_history[ip_identifier] = []
        self.request_history[ip_identifier].append(datetime.now())
    
    async def wait_if_needed(self, ip_identifier):
        """Czeka jeśli limit został osiągnięty"""
        while not self.can_make_request(ip_identifier):
            wait_time = random.uniform(60, 180)  # 1-3 minuty
            print(f"Rate limit osiągnięty, czekam {wait_time:.0f}s")
            await asyncio.sleep(wait_time)
        
        self.record_request(ip_identifier)

# Użycie
limiter = RateLimitHandler(max_requests_per_ip=8, cooldown_minutes=15)

async def safe_scrape(url, session_id):
    await limiter.wait_if_needed(session_id)
    
    proxy_url = get_rotating_proxy_url()
    # ... wykonaj scrapowanie
    
    # Dodaj losowe opóźnienie między zapytaniami
    await asyncio.sleep(random.uniform(3, 8))

Wykrywanie i obsługa CAPTCHA

X używa głównie arkoselabs (funcaptcha) i hCaptcha. Dla sesji niezalogowanych CAPTCHA pojawia się często po 5-15 zapytaniach z tego samego IP.

async def handle_captcha(page):
    """Próbuje wykryć i obsłużyć CAPTCHA"""
    
    # Sprawdź czy jest CAPTCHA
    captcha_selectors = [
        "iframe[src*='arkoselabs']",
        "iframe[src*='hcaptcha']",
        ".challenge-form"
    ]
    
    for selector in captcha_selectors:
        element = await page.query_selector(selector)
        if element:
            print("Wykryto CAPTCHA - IP wymaga rotacji")
            return True
    
    return False

async def scrape_with_captcha_handling(url, max_retries=3):
    """Scrapowanie z obsługą CAPTCHA"""
    
    for attempt in range(max_retries):
        session_id = ''.join(random.choices(string.ascii_lowercase, k=8))
        proxy_url = get_rotating_proxy_url(session=session_id)
        
        async with async_playwright() as p:
            browser = await p.chromium.launch(
                proxy={"server": proxy_url},
                headless=True
            )
            page = await browser.new_page()
            
            try:
                await page.goto(url, timeout=30000)
                
                has_captcha = await handle_captcha(page)
                
                if has_captcha:
                    print(f"Próba {attempt + 1}: CAPTCHA, rotuję IP")
                    await browser.close()
                    await asyncio.sleep(5)
                    continue
                
                # Scrapowanie bez CAPTCHA
                data = await extract_data(page)
                await browser.close()
                return data
                
            except Exception as e:
                print(f"Błąd: {e}")
                await browser.close()
    
    return None

Aspekty prawne i etyczne

Scrapowanie Twitter/X wiąże się z istotnymi kwestiami prawnymi, które należy rozważyć przed rozpoczęciem projektu.

Stan prawny w 2026 roku

X Corporation aktywnie ściga scrapery. Kilka kluczowych przypadków:

  • X v. Bright Data (2023) — X pozwał dostawcę proxy za scrapowanie. Sprawa zakończyła się ugodą.
  • X v. Center for Countering Digital Hate — X twierdził, że scrapowanie naruszało ToS. Sprawa w toku.
  • HiQ v. LinkedIn — precedens w USA: publiczne dane mogą być scrapowane, ale ToS może to zabraniać.

Wniosek: Scrapowanie danych publicznych może być legalne w niektórych jurysdykcjach, ale naruszenie ToS może prowadzić do pozwów cywilnych. Każdy projekt wymaga indywidualnej analizy prawnej.

Kiedy scrapowanie jest NIEODPOWIEDNIE

  • Komercyjne agregowanie danych — jeśli planujesz sprzedawać dane, rozważ oficjalne API
  • Monitorowanie w czasie rzeczywistym — rate limiting uniemożliwia skuteczne RT monitoring
  • Dane prywatne — DM, tweety chronione, dane za ścianą logowania
  • Targetowanie jednostek — stalkowanie, harassment, doxxing

Kiedy scrapowanie może być uzasadnione

  • Badania naukowe — analiza sentymentu, badania społeczne (z ethics approval)
  • Jednorazowe analizy — snapshot danych dla raportu
  • Weryfikacja własnych danych — sprawdzanie jak widoczna jest Twoja marka
  • Osobiste archiwum — backup własnych tweetów (API jest lepsze)

Złota zasada: Jeśli Twoje zastosowanie można zrealizować przez oficjalne API — użyj API. Scrapowanie powinno być ostatnim resortem, gdy API jest niedostępne lub nieproporcjonalnie drogie.

RODO i prywatność danych

W UE RODO ma zastosowanie nawet do danych publicznie dostępnych:

  • Przetwarzanie tweetów zawierających dane osobowe wymaga podstawy prawnej
  • Użytkownicy mają prawo do bycia zapomnianym
  • Profilowanie i analiza sentymentu to "automatyczne podejmowanie decyzji"
  • Transgraniczny transfer danych wymaga odpowiednich umów

Alternatywy do scrapowania

Przed podjęciem decyzji o scrapowaniu, rozważ te alternatywy:

Oficjalne X API

Nawet przy wysokich cenach, API oferuje:

  • Gwarantowane limity i SLA
  • Dostęp do danych w czasie rzeczywistym
  • Brak ryzyka blokady IP
  • Legalność i zgodność z ToS

Dostawcy danych

Firmy takie jak Sprinklr, Brandwatch czy Meltwater mają oficjalne partnerstwa z X i oferują:

  • Dostęp do danych przez legalne kanały
  • Wstępnie przetworzone analytics
  • Wsparcie prawne i compliance

Open-source narzędzia

Niektóre projekty oferują dane akademickie:

  • Archive.org — archiwalne snapshoty
  • Internet Archive — wybrane tweety historyczne
  • Academic datasets — uniwersyteckie zbiory danych

Kluczowe wnioski

  • API X jest drogie — darmowy tier nie istnieje, podstawowy plan to 100 USD/miesiąc przy minimalnych limitach.
  • Proxy rezydencjalne są niezbędne — datacenter IP są blokowane niemal natychmiast przez X.
  • GraphQL to klucz — X używa wewnętrznych endpointów GraphQL, które można przechwytywać.
  • Rate limiting jest agresywne — planuj maksymalnie 10-15 zapytań per IP w oknie 15-minutowym.
  • CAPTCHA wymaga rotacji — przy wykryciu CAPTCHA, zmiana IP jest jedynym niezawodnym rozwiązaniem.
  • Kwestie prawne są istotne — scrapowanie może naruszać ToS i prowadzić do pozwów cywilnych.
  • Rozważ alternatywy — oficjalne API lub dostawcy danych mogą być lepszym wyborem dla komercyjnych projektów.

Często zadawane pytania

Czy scrapowanie Twitter/X jest legalne?

Scrapowanie danych publicznie dostępnych może być legalne w niektórych jurysdykcjach (np. USA po sprawie HiQ v. LinkedIn), ale zazwyczaj narusza Warunki Korzystania (ToS) platformy. To może prowadzić do pozwów cywilnych. W UE RODO ma zastosowanie nawet do danych publicznych. Zawsze konsultuj się z prawnikiem przed rozpoczęciem projektu scrapowania.

Dlaczego proxy datacenter nie działają z X?

X utrzymuje bazy danych zakresów IP datacenter i automatycznie je blokuje. Proxy datacenter używają adresów z znanych pul AWS, Google Cloud, DigitalOcean itp., które są łatwe do zidentyfikowania. Proxy rezydencjalne używają IP przypisanych do zwykłych dostawców internetowych, co wygląda jak ruch prawdziwych użytkowników domowych.

Ile zapytań mogę wykonać z jednym IP?

Dla sesji niezalogowanych X stosuje agresywne limity — zazwyczaj 5-15 zapytań w oknie 15-minutowym przed pojawieniem się CAPTCHA lub błędu 429. Limity są niższe dla wyszukiwania niż dla profili. Zalecamy zachowawcze podejście: max 8 zapytań per IP co 15 minut z losowymi opóźnieniami.

Czy mogę scrapować X bez proxy?

Teoretycznie tak, ale praktycznie nie. Bez proxy Twoje osobiste IP zostanie szybko zablokowane po kilku zapytaniach. Rate limiting dla pojedynczego IP jest zbyt agresywny dla jakiegokolwiek znaczącego projektu scrapowania. Proxy rezydencjalne z rotacją to praktyczna konieczność.

Jakie dane mogę legalnie scrapować z X?

To zależy od jurysdykcji i zastosowania. Dane wyraźnie publiczne (publiczne tweety, profile) są łatwiejsze do uzasadnienia niż dane wymagające logowania. Nigdy nie scrapuj DM, tweetów chronionych, ani danych wykorzystywanych do stalkowania lub harassment. Dla komercyjnych zastosowań rozważ oficjalne API lub konsultację prawną.

Gotowy, aby zacząć?

Dostęp do ponad 50 mln rezydencjalnych IP w ponad 148 krajach z filtrowaniem AI.

Zobacz cenyProxy rezydencjalne
← Powrót do Bloga