Scraper w Pythonie to sposób na zbieranie danych z internetu bez płacenia za drogie API – w kilka godzin zbudujesz narzędzie, które pobierze dane cenowe od konkurencji, SERP results, content z branżowych portali lub product catalog. Ten tutorial to kompletny przewodnik od zera: setup środowiska, pierwszy scraper, obsługa JavaScript, storage danych i anti-bot techniques.
Zakładamy podstawową znajomość Pythona – jeśli nie kodowałeś wcześniej, ten tutorial przeprowadzi cię przez wszystko, ale zrozumienie zmiennych, funkcji i if-ów jest wymagane. Kod w tutorialu działa na Pythonie 3.10+ i biblioteki aktualne na 2026 rok.
W skrócie
- Czas zbudowania działającego scrapera: 2–4 godziny dla początkującego, 30 min dla doświadczonego.
- Stack: Python 3.10+, requests, BeautifulSoup, Playwright (dla JS), pandas (dla storage).
- Koszt: 0 zł – wszystkie biblioteki open source, lokalny serwer wystarczy.
- Legalne scrapowanie wymaga: respektowania robots.txt, rozsądnego rate limit, nie pobierania danych osobowych.
- Anti-bot techniques rosną – w 2026 Cloudflare Turnstile, Akamai Bot Manager, PerimeterX są standardem dla e-commerce.
Setup środowiska Python
Krok 1: Instalacja Pythona
Python 3.10+ z oficjalnej strony python.org (Windows, Mac) lub brew install python3 (Mac). Linux zwykle ma Pythona 3, sprawdź python3 --version.
Krok 2: Virtualenv i pip
python3 -m venv scraper-env
source scraper-env/bin/activate # Linux/Mac
scraper-envScriptsactivate # Windows
pip install requests beautifulsoup4 lxml pandas playwright
playwright install chromiumKrok 3: Edytor
VS Code z rozszerzeniem Python lub PyCharm Community (darmowy). Konfiguracja formatowania – Black auto-formatter, pip install black. Powiązane zagadnienia opisujemy w narzędzia SEO.
Krok 4: Struktura projektu
scraper/
├── main.py
├── scrapers/
│ ├── __init__.py
│ └── example_scraper.py
├── storage/
│ └── data.csv
├── requirements.txt
└── README.mdPierwszy scraper – statyczna strona HTML
Najprostszy scraper do statycznej strony (bez JavaScript). Przykład: scrape listy artykułów z bloga. Praktyczne wskazówki zawiera przewodniku po stacku marketingowym 2026.
import requests
from bs4 import BeautifulSoup
import pandas as pd
URL = "https://example.com/blog"
HEADERS = {
"User-Agent": "Mozilla/5.0 (compatible; MyBot/1.0)"
}
def scrape_blog():
response = requests.get(URL, headers=HEADERS, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "lxml")
articles = []
for article in soup.select("article.post"):
title = article.select_one("h2").get_text(strip=True)
link = article.select_one("a")["href"]
date = article.select_one(".date").get_text(strip=True)
articles.append({
"title": title,
"link": link,
"date": date
})
return articles
if __name__ == "__main__":
data = scrape_blog()
df = pd.DataFrame(data)
df.to_csv("storage/blog.csv", index=False)
print(f"Scraped {len(data)} articles")Co się dzieje
requests.getpobiera HTML z URL.BeautifulSoupparsuje HTML do struktury, którą można przeszukiwać.- CSS selectory (
article.post,h2) znajdują elementy. pandaszapisuje do CSV.
Jak znaleźć CSS selectory
- Otwórz stronę w Chrome.
- F12 – DevTools.
- Prawy klik na element, który chcesz – Inspect.
- Prawy klik na HTML w DevTools – Copy → Copy selector.
- Uprość selector (usuwając zbędne ID/klasy z dziwnymi nazwami).
Obsługa paginacji
Większość stron ma więcej niż jedną stronę wyników. Scraper musi iterować.
def scrape_all_pages():
all_articles = []
page = 1
while True:
url = f"{URL}?page={page}"
response = requests.get(url, headers=HEADERS)
soup = BeautifulSoup(response.text, "lxml")
articles = soup.select("article.post")
if not articles:
break
for article in articles:
title = article.select_one("h2").get_text(strip=True)
all_articles.append({"title": title})
page += 1
time.sleep(2) # be polite
if page > 100: # safety limit
break
return all_articlesDobre praktyki paginacji
time.sleep(2)– 2 sekundy między requestami. Nie zabijaj serwera.- Safety limit – nieskończone pętle to problem.
- Logging progresji –
print(f"Page {page}: {len(articles)} items"). - Save partial results – co 10 stron zapisuj do CSV, żeby nie stracić wszystkiego przy crash.
Scraping stron z JavaScript – Playwright
Nowoczesne strony (React, Vue, Angular) renderują content przez JavaScript. requests zwraca pustą stronę. Rozwiązanie: Playwright, który odpala pełną przeglądarkę.
from playwright.sync_api import sync_playwright
def scrape_spa():
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://spa-example.com")
page.wait_for_selector("article.post")
articles = page.query_selector_all("article.post")
data = []
for article in articles:
title = article.query_selector("h2").inner_text()
data.append({"title": title})
browser.close()
return dataKiedy Playwright vs requests
- requests – statyczne HTML, starsze strony, szybko.
- Playwright – SPA, wolniej (2–5x), ale obsługuje JavaScript.
- Test:
curl https://url.com– jeśli content widzisz w HTML, requests wystarczą. Jeśli nie, Playwright.
Playwright headless vs headed
headless=True– bez UI, szybciej, dla produkcji.headless=False– widzisz przeglądarkę, debug.slow_mo=1000– spowalnia dla obserwacji.
Storage danych – CSV, SQLite, PostgreSQL
CSV (prosty start)
import pandas as pd
df = pd.DataFrame(data)
df.to_csv("output.csv", index=False, encoding="utf-8")Plusy: prosty, Excel-friendly. Minusy: nie skaluje się > 100k rows, brak struktury, trudny query.
SQLite (dobry middle ground)
import sqlite3
conn = sqlite3.connect("scraper.db")
df.to_sql("articles", conn, if_exists="append", index=False)
conn.close()Plusy: file-based, jeden plik, SQL queries. Minusy: lokalny, nie networked.
PostgreSQL (scale)
from sqlalchemy import create_engine
engine = create_engine("postgresql://user:pass@localhost/db")
df.to_sql("articles", engine, if_exists="append", index=False)Plusy: production-ready, concurrent access, indexing. Minusy: setup wymaga serwera.
Error handling i resiliencja
Produkcyjny scraper musi obsługiwać awarie: timeouts, 503, network errors, zmiany w HTML.
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=4, max=10)
)
def safe_scrape(url):
response = requests.get(url, headers=HEADERS, timeout=15)
response.raise_for_status()
return response.text
try:
html = safe_scrape(url)
except Exception as e:
print(f"Failed after 3 retries: {e}")
log_to_file(url, str(e))Co catchować
requests.Timeout– serwer wolny, retry.requests.HTTPError– 4xx/5xx, retry lub skip.requests.ConnectionError– sieć, retry.AttributeError– BeautifulSoup selector nie znalazł elementu, może HTML się zmienił – log i skip.
Rate limiting i grzeczne scrapowanie
Agresywne scrapowanie = ban IP. Zasady grzeczności.
Respect robots.txt
from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.set_url("https://example.com/robots.txt")
rp.read()
if rp.can_fetch("MyBot", "https://example.com/page"):
# OK to scrape
passRate limit
- 1–2 sekundy między requestami dla większości stron.
- 5–10 sekund dla wrażliwych stron (banki, gov).
- Randomizuj –
time.sleep(random.uniform(1, 3))zamiast stałego 2s. - Concurrent requests: max 2–5 naraz, nie 100.
User-Agent rotation
import random
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0) Chrome/120.0",
"Mozilla/5.0 (Macintosh) Safari/604.1",
"Mozilla/5.0 (X11; Linux) Firefox/120.0"
]
headers = {"User-Agent": random.choice(USER_AGENTS)}Anti-bot detection i jak sobie radzić
W 2026 Cloudflare, Akamai, PerimeterX blokują scrapery. Etyczne techniki bypass.
Cloudflare challenge
Cloudflare Turnstile pokazuje „Checking your browser” page. Rozwiązania:
- Playwright stealth –
pip install playwright-stealth, ukrywa sygnały automatyzacji. - undetected-chromedriver – Selenium-based, ale bardziej skuteczny niż vanilla.
- Commercial APIs – ScraperAPI, Bright Data solve Cloudflare automatycznie. Zobacz porównanie scraping API.
Rate limit detection
- 429 Too Many Requests – wait 60 sekund, retry.
- IP rotation – proxy list (ScraperAPI, Bright Data).
- Session cookies – często strona memoryzuje „ta sesja już pobrała X” – twórz nowe sesje.
CAPTCHA
Jeśli pojawia się CAPTCHA – scraping tej strony jest sygnalizowany jako problematyczny. Etyczne opcje:
- Użyj oficjalnego API (jeśli istnieje).
- Kontakt z właścicielem strony – często zgadzają się na dostęp w zamian za atrybucję.
- Commercial scraping API, które mają umowy na CAPTCHA solving.
- Jeśli strona jawnie zabrania scrapingu w ToS – nie scrapuj.
Aspekty prawne scrapowania
Krótka wersja: scrapowanie jest legalne w większości przypadków, ale są granice. Pełne informacje w artykule o legalnym scrapowaniu.
Co jest OK
- Publicznie dostępne dane (bez logowania).
- Dane niekomercyjne (dla analiz własnych).
- Respektowanie robots.txt i rate limit.
- Dane bez osobowych informacji (RODO).
Co nie jest OK
- Omijanie zabezpieczeń technicznych – CFAA w USA, art. 267 KK w Polsce.
- Scrapowanie treści chronionych copyrightem do dalszej publikacji.
- Zbieranie danych osobowych bez podstawy RODO.
- DDoS-like scrapowanie (1000 requestów/sekundę).
Trzy gotowe przykłady scraperów
Przykład 1: SERP scraper (dla SEO research)
Pobiera top 10 wyników Google dla listy fraz. Używaj z proxy i rate limit – Google aktywnie blokuje scraperów.
import requests
from bs4 import BeautifulSoup
def scrape_google(query):
url = f"https://www.google.com/search?q={query}"
response = requests.get(url, headers={
"User-Agent": "Mozilla/5.0..."
})
soup = BeautifulSoup(response.text, "lxml")
results = []
for result in soup.select("div.g"):
title = result.select_one("h3")
link = result.select_one("a")
if title and link:
results.append({
"title": title.text,
"url": link["href"]
})
return resultsPrzykład 2: Price monitoring konkurencji
Pobiera ceny produktów z konkurencyjnych sklepów. Daily cron, storage w SQLite, alerty przy zmianie > 10%.
Przykład 3: Content aggregator
Pobiera tytuły i wstępy z 20 branżowych blogów. Używany do content research – co publikuje konkurencja, jakie tematy rosną.
Deploy scrapera – od local do cloud
Local cron
crontab -e, dodaj: 0 6 * * * /path/to/python /path/to/scraper.py. Scrapuje codziennie o 6:00. Minusy: komputer musi być włączony.
VPS – Hetzner, DigitalOcean
20–50 zł/mies VPS, deploy scrapera, cron. Zalety: always-on, tani. Minusy: setup systemu, utrzymanie.
Serverless – AWS Lambda, Cloudflare Workers
Płacisz za run, nie za utrzymanie. Lambda: max 15 min per run, ~2 USD za 1 mln runs. Dobry dla lekkich scraperów.
Docker + Kubernetes (enterprise)
Dla wielu scraperów, load balancing, scaling. Overkill dla pojedynczego projektu.
Scrapy framework dla większych projektów
Scrapy to pełen framework scrapingu – strukturyzuje kod, zarządza concurrent requests, ma built-in storage i middleware. Warto gdy robisz > 3 scrapery lub scrapujesz > 10k stron dziennie.
Instalacja i setup
pip install scrapy
scrapy startproject myscraper
cd myscraper
scrapy genspider example example.comAnatomia Scrapy spidera
import scrapy
class ExampleSpider(scrapy.Spider):
name = "example"
start_urls = ["https://example.com/blog"]
custom_settings = {
"DOWNLOAD_DELAY": 2,
"CONCURRENT_REQUESTS": 4,
"USER_AGENT": "MyBot/1.0",
}
def parse(self, response):
for article in response.css("article.post"):
yield {
"title": article.css("h2::text").get(),
"link": article.css("a::attr(href)").get(),
}
next_page = response.css("a.next::attr(href)").get()
if next_page:
yield response.follow(next_page, self.parse)Zalety Scrapy
- Auto-handling concurrent requests, throttling.
- Built-in storage – JSON, CSV, SQLite, custom pipelines.
- Middleware system – proxy rotation, user-agent rotation, retry logic.
- Extensions – monitoring, statistics, memory debugger.
- Scrapyd – deployment platform.
Wady Scrapy
- Learning curve – więcej konceptów do opanowania.
- Overkill dla pojedynczego skryptu.
- Async model może być mylący dla początkujących.
Proxy – kiedy i jak używać
Proxy to serwer pośredni – twoje requesty wychodzą z proxy IP, nie twojego. Przydatne dla: rotacji IP (unikanie bana), dostępu do stron geo-blokowanych, anonimowości.
Typy proxy
- Datacenter proxy – tanie (2–5 USD/mies per IP), szybkie, ale łatwo wykrywalne. Dobre dla statycznych stron.
- Residential proxy – droższe (5–15 USD/GB ruchu), IP od realnych użytkowników. Trudno wykrywalne. Dla e-commerce, Google.
- Mobile proxy – najdroższe (10–50 USD/GB), IP od urządzeń mobilnych. Najlepsze unmask, ale overkill dla większości zadań.
Dostawcy proxy
- Bright Data – największy dostawca, residential i mobile, od $500/mies.
- Oxylabs – konkurent Bright Data, premium quality.
- Smartproxy – tańszy, od $75/mies.
- ScrapingBee – proxy + headless browser w jednym, od $49/mies.
- ProxyMesh – tanie datacenter, od $10/mies.
Implementacja proxy w requests
proxies = {
"http": "http://user:pass@proxy.example.com:8080",
"https": "http://user:pass@proxy.example.com:8080"
}
response = requests.get(url, proxies=proxies)Rotacja proxy
import random
PROXY_LIST = [
"http://proxy1.com:8080",
"http://proxy2.com:8080",
"http://proxy3.com:8080",
]
def get_random_proxy():
return {"https": random.choice(PROXY_LIST)}
response = requests.get(url, proxies=get_random_proxy())Monitoring działającego scrapera
Scraper uruchomiony na VPS może failować bez twojej wiedzy. Monitoring obowiązkowy.
Health checks
- Każdy udany run zapisuje timestamp w pliku lub DB.
- Cron job (lub external monitor) sprawdza, czy timestamp jest recent.
- Jeśli > 2x expected interval – alert (email, Slack).
Metryki do śledzenia
- Liczba pobranych URL per run.
- Liczba errors (4xx, 5xx, timeouts).
- Czas wykonania.
- % udanych parsings (items zwracane vs planned).
Alerty
- Run failed completely – natychmiast.
- > 20% errors w runie – alert.
- < 50% expected items – możliwa zmiana HTML, alert.
- Run trwa > 2x average – możliwa blockada, alert.
Najczęstsze błędy
Błąd 1: Nie handlować błędów HTML
Strona zmieniła strukturę – selector zwraca None, AttributeError, scraper wywala się. Zawsze sprawdzaj, czy element istnieje: if elem is None: continue.
Błąd 2: Za agresywne rate limit
100 requestów/sek = ban IP w 5 minut. Domyślnie 1–2 sek między requestami, rosnące z feedbacku (429 → wait dłużej).
Błąd 3: Storage bez deduplikacji
Scrapujesz blog codziennie, każdy artykuł trafia do DB ponownie. Unique constraint na URL lub hash content.
Błąd 4: Ignorowanie robots.txt
Pierwsza rzecz, którą sprawdzają prawnicy przy dispute. Zawsze respektuj.
Błąd 5: Brak logging
Scraper failuje w nocy – nie wiesz dlaczego. Używaj logging module, zapisuj do pliku, rotate codziennie.
FAQ — najczęstsze pytania
Czy scrapowanie jest legalne w Polsce?
W większości przypadków tak, dla publicznych danych bez zabezpieczeń. Art. 267 KK karze „bezprawne uzyskanie dostępu” – kluczowe słowo „bezprawne”. Dane publiczne, respect robots.txt, rozsądne rate limit – bezpiecznie. Dane chronione hasłem, ToS zakazujące scrapingu, omijanie CAPTCHA – ryzyko. Szczegóły w artykule o legalnym scrapowaniu.
Requests czy Playwright – co wybrać?
Requests dla statycznego HTML (90% starszych stron). Playwright dla SPA (React, Vue). Test: curl url.com | grep "target-element" – jeśli element jest, requests wystarczą. Requests są 5–10x szybsze, więc preferuj je, gdy możliwe.
Ile kosztuje utrzymanie produkcyjnego scrapera?
VPS (Hetzner, DO) 20–50 zł/mies + proxy (jeśli potrzebne) 50–200 zł/mies + storage DB 0–30 zł/mies = typowo 100–250 zł/mies per scraper. Enterprise z proxy, commercial APIs – 500–2000 zł/mies. Dla pojedynczego projektu lokalny cron na laptopie wystarczy – 0 zł.
Czy warto budować własny scraper zamiast używać API?
Zależy od skali. Pojedynczy projekt, < 1000 requestów dziennie – własny scraper jest tańszy (0 zł vs 50 USD/mies API). Wiele stron, > 10k requestów dziennie, anti-bot protection – commercial API jest tańsze niż czas na building custom infrastructure. Zobacz porównanie scraping API.
Jak często aktualizować scraper po zmianach na stronie?
Statyczne strony – raz na 3–6 miesięcy, jeśli monitor nie sygnalizuje błędów. E-commerce, frequent redesigns – co miesiąc check. Monitor: alert, gdy scraper zwraca < 50% expected results. Dobrze napisany scraper z defensive coding (try/except, optional chaining) przeżyje małe zmiany, ale major redesign wymaga rewrite selectorów.
Czy mogę sprzedawać dane ze scrapingu?
Ostrożnie. Surowe dane ze strony A sprzedawane jako produkt B to prawdopodobnie copyright infringement. Przetworzone, zagregowane dane (analytics, insights, statystyki) – zwykle OK. Dane osobowe – nie bez podstawy RODO. Zawsze konsultuj z prawnikiem przed monetyzacją, zasady różnią się jurysdykcyjnie.
Czy scraping w Pythonie jest lepszy niż Node.js?
Oba działają. Python: więcej bibliotek (BeautifulSoup, Scrapy, pandas), łatwiejszy dla data science. Node.js: Puppeteer/Playwright też działają, lepsze dla JavaScript-heavy sites. Dla 90% projektów Python wystarczy. Wybór zależy od tego, co twój zespół lepiej zna.
Co dalej
Na początek sprawdź scraping API. Gdy opanujesz podstawy, przejdź do legalne scrapowanie — tam czekają zaawansowane techniki.