Web scraping (po polsku scrapowanie danych, często zapisywane błędnie jako „web scrapping”) to automatyczne pobieranie danych ze stron internetowych przez program, który ściąga kod HTML, wyciąga z niego potrzebne pola i zapisuje je w uporządkowanej formie, na przykład w CSV albo bazie danych. Scraper robi to, co Wy robicie ręcznie, kopiując ceny z dwudziestu zakładek, tylko szybciej, powtarzalnie i bez literówek.
W tym artykule dostajecie więcej niż definicję: działający kod w Pythonie sprawdzony na stronie przygotowanej do nauki scrapingu, test, który porównuje ceny widoczne na froncie z cenami z API Waszej aplikacji, oraz uczciwe omówienie prawa: RODO, ochrony baz danych i pliku robots.txt. Na końcu znajdziecie listę błędów, które widzimy najczęściej, kiedy zespoły zaczynają scrapować na własną rękę.
Spis treści
- Co to jest web scraping i czy scrapping to błąd?
- Jak działa scraper krok po kroku?
- Scraping, API czy ręczne kopiowanie: co wybrać?
- Jak napisać pierwszy scraper w Pythonie?
- Jak użyć web scrapingu w testach oprogramowania?
- Kiedy requests nie wystarczy i potrzebny jest Playwright?
- Czy web scraping jest legalny w Polsce?
- Jak scrapować etycznie i nie zostać zablokowanym?
- Najczęstsze błędy przy scrapowaniu danych
- Od czego zacząć naukę web scrapingu
Co to jest web scraping i czy scrapping to błąd?
Web scraping to pobieranie i przetwarzanie danych ze stron, które zostały zbudowane dla ludzi, a nie dla programów. Przeglądarka pokazuje Wam tabelę z cenami, scraper widzi ten sam dokument HTML i odczytuje z niego wartości według reguł, które mu zapiszecie, na przykład „weź tekst z każdego elementu o klasie price”.
Kilka pojęć, które krążą wokół tematu i często się mieszają:
- Scraper to program, który pobiera konkretne dane z konkretnych stron. Pytanie „scraper co to” ma więc prostą odpowiedź: narzędzie do ekstrakcji danych
- Crawler (pająk) przechodzi po linkach i odkrywa kolejne strony. Robią to wyszukiwarki. Scraper często korzysta z crawlera, żeby znaleźć podstrony z danymi
- Parsowanie stron to etap, w którym surowy HTML zamienia się w drzewo elementów, z którego można wybierać fragmenty selektorami CSS albo XPath
- Web scrapping z dwoma „p” to po prostu literówka. W języku angielskim scrapping znaczy złomowanie, więc scraper z dwoma „p” brzmiałby jak program do zezłomowania strony. Czasem trafnie, jeśli ktoś ustawi go bez limitu zapytań.
Skala, na jaką działa dziś web scraping, jest większa, niż się wydaje. Według raportu Imperva 2025 Bad Bot Report ruch automatyczny po raz pierwszy od dekady przekroczył ruch ludzi i stanowi 51 procent całego ruchu w sieci, a złośliwe boty odpowiadają za 37 procent. Część z tych botów to właśnie agresywne scrapery, dlatego serwisy coraz częściej się przed nimi bronią, a Wasz skrypt musi umieć zachować się przyzwoicie.
Boty w ruchu internetowym
51%
ruchu w sieci generują programy, nie ludzie
37%
ruchu to złośliwe boty, w tym agresywne scrapery
429
kod HTTP, którym serwer mówi: za dużo zapytań, zwolnijcie
Źródło: Imperva, 2025 Bad Bot Report; IETF RFC 6585, 2012.
Jak działa scraper krok po kroku?
Każdy scraper, od skryptu na dwadzieścia linii po system zbierający dane z tysięcy sklepów, przechodzi przez te same etapy. Różni się tylko skalą i tym, ile uwagi poświęca obsłudze błędów.
Najwięcej czasu w praktyce zabierają etapy 4 i 6, a nie samo pobranie. W naszych projektach automatyzacji widzimy to samo przy testach UI: kod, który klika, piszemy szybko, a dopiero stabilne selektory, obsługa wyjątków i czytelne raporty decydują o tym, czy zestaw przetrwa kolejne wydania.
Scraping, API czy ręczne kopiowanie: co wybrać?
Web scraping jest narzędziem ostatniego wyboru. Jeżeli dane są dostępne przez API albo eksport, prawie zawsze lepiej sięgnąć po nie, bo API ma kontrakt: nazwane pola, wersję i dokumentację. Strona HTML takiego kontraktu nie ma i może zmienić się w piątek po południu.
Dla testerów jest jeszcze czwarta opcja, najlepsza: dane z własnej aplikacji. Tam scraping frontu i odpytywanie API Waszego backendu to dwa źródła tej samej prawdy, a ich porównanie jest pełnoprawnym testem, do czego wrócimy za chwilę. O tym, jak działają same zapytania, piszemy w artykule o metodach HTTP w testach API.
Jak napisać pierwszy scraper w Pythonie?
Najprostszy zestaw do web scrapingu w Pythonie to biblioteka requests do pobierania stron i Beautiful Soup do parsowania. Poniższy kod uruchomiliśmy 10 października 2026 roku na stronie books.toscrape.com, która powstała właśnie po to, żeby ćwiczyć scraping bez szkody dla nikogo. Skrypt zebrał 40 produktów z dwóch stron katalogu w kilka sekund.
scraper.py, Python 3, requests i beautifulsoup4
import time
from decimal import Decimal
from urllib.parse import urljoin
from urllib.robotparser import RobotFileParser
import requests
from bs4 import BeautifulSoup
START = "https://books.toscrape.com/catalogue/page-1.html"
UA = "QA-scraper-demo/1.0 (kontakt: qa@example.com)"
robots = RobotFileParser("https://books.toscrape.com/robots.txt")
robots.read()
session = requests.Session()
session.headers["User-Agent"] = UA
def pobierz(url):
if not robots.can_fetch(UA, url):
raise PermissionError(f"robots.txt nie pozwala: {url}")
for proba in range(3):
r = session.get(url, timeout=15)
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", "30")))
continue
r.raise_for_status()
r.encoding = "utf-8"
return r.text
raise RuntimeError(f"429 trzy razy z rzędu: {url}")
def produkty(url, strony=2):
for _ in range(strony):
soup = BeautifulSoup(pobierz(url), "html.parser")
for karta in soup.select("article.product_pod"):
yield {
"tytul": karta.h3.a["title"],
"cena": Decimal(karta.select_one("p.price_color").text.strip().lstrip("£")),
"dostepny": "In stock" in karta.select_one("p.availability").text,
}
nastepna = soup.select_one("li.next a")
if not nastepna:
break
url = urljoin(url, nastepna["href"])
time.sleep(1)
if __name__ == "__main__":
wynik = list(produkty(START))
print(len(wynik), "produktów, najtańszy:", min(wynik, key=lambda p: p["cena"]))
Na co zwrócić uwagę w tym kodzie, bo to elementy, które odróżniają scraper przyzwoity od kłopotliwego:
- robots.txt sprawdzany przed każdym zapytaniem. Moduł
urllib.robotparserjest w bibliotece standardowej. Ta konkretna strona nie ma pliku robots.txt (odpowiada 404), więc parser traktuje wszystko jako dozwolone, ale na prawdziwym serwisie ten warunek zadziała - User-Agent z kontaktem. Administrator, który zobaczy Wasz ruch w logach, wie, do kogo napisać, zamiast od razu blokować adres IP
- Pauza między stronami i obsługa 429. Kod 429 Too Many Requests opisuje dokument IETF RFC 6585 z 2012 roku, razem z nagłówkiem
Retry-After, który mówi, ile sekund odczekać - Decimal zamiast float. Ceny w liczbach zmiennoprzecinkowych kończą się porównaniami w rodzaju 51.769999. W testach finansowych to klasyczny fałszywy alarm
Jeśli dopiero zaczynacie z Pythonem, ten przykład jest dobrym pierwszym projektem: krótki, z widocznym efektem i z problemami, które spotkacie potem w automatyzacji testów. Pełną ścieżkę od składni do testów przechodzimy na szkoleniu Programowanie w języku Python dla testerów oprogramowania.
Jak użyć web scrapingu w testach oprogramowania?
W QA web scraping nie służy do podglądania konkurencji, tylko do sprawdzania własnego produktu oczami użytkownika. Klasyczny przypadek: sklep pokazuje na liście produktów cenę z cache albo z innego serwisu niż ten, który liczy koszyk. Użytkownik widzi 1 049 zł, płaci 999 zł albo odwrotnie. Testy API są zielone, testy UI też, bo każdy z nich patrzy tylko na jedną stronę medalu.
Test poniżej zbiera produkty z listingu na środowisku testowym i dla każdego pyta API o cenę. Sprawdziliśmy go na lokalnej makiecie: przy zgodnych cenach daje 2 zaliczone testy, a po zmianie ceny w API zgłasza błąd z czytelnym komunikatem B-200: front 1049.00 zł, API 999.00 zł.
test_ceny.py, pytest; adres środowiska w zmiennej BASE_URL
import os
import re
from decimal import Decimal
import pytest
import requests
from bs4 import BeautifulSoup
BASE = os.environ.get("BASE_URL", "https://staging.example.com")
LISTING = f"{BASE}/kategoria/laptopy/"
def cena_z_frontu(tekst):
# "1 049,00 zł" -> Decimal("1049.00"): zostawiamy cyfry i przecinek,
# bo spacja bywa twarda, a waluta różnie zakodowana
return Decimal(re.sub(r"[^\d,]", "", tekst).replace(",", "."))
def produkty_z_listingu():
html = requests.get(LISTING, timeout=15).text
soup = BeautifulSoup(html, "html.parser")
return [
(el["data-sku"], cena_z_frontu(el.select_one(".price").get_text(strip=True)))
for el in soup.select("[data-testid=product]")
]
@pytest.mark.parametrize("sku,cena_front", produkty_z_listingu())
def test_cena_na_froncie_zgodna_z_api(sku, cena_front):
r = requests.get(f"{BASE}/api/products/{sku}", timeout=15)
assert r.status_code == 200, f"API nie zna produktu {sku}"
cena_api = Decimal(r.json()["price"])
assert cena_front == cena_api, f"{sku}: front {cena_front} zł, API {cena_api} zł"
Dwie lekcje z pisania tego testu. Pierwsza: pierwsza wersja funkcji cena_z_frontu wyłożyła się na kodowaniu znaków, bo serwer nie podał zestawu znaków i „zł” przyszło jako krzaki. Dlatego zostawiamy w tekście tylko cyfry i przecinek. Druga: atrybut data-testid to selektor, który ustalacie z programistami. Nie zmienia się przy przebudowie wyglądu, więc test nie pęka przy każdej zmianie CSS.
Ten sam schemat sprawdza się w kilku innych miejscach:
- Audyt treści po migracji: zbieracie tytuły, opisy i linki ze starej i nowej wersji serwisu i porównujecie listy
- Martwe linki i brakujące obrazki: crawler po własnej domenie, który zapisuje każdy kod różny od 200
- Regresja treści: zrzut kluczowych tekstów (regulaminy, ceny, komunikaty prawne) przed i po wydaniu
- Dane testowe: lista realnych kategorii i wariantów produktów jako wejście do testów parametryzowanych
Takie porównania budujemy u naszych klientów jako część zestawów regresji. Z naszego doświadczenia to jedne z najtańszych testów w utrzymaniu, które łapią błędy naprawdę kosztowne dla biznesu. W case Argos, e-commerce, uporządkowanie testów i automatyzacja dały spadek liczby błędów krytycznych na produkcji o 46 procent i wzrost konwersji o 12 procent dzięki stabilności checkoutu i płatności. Jeśli chcecie mieć takie sprawdzenia w pipeline bez pisania ich od zera, zobaczcie, jak prowadzimy automatyzację testów API i automatyzację testów UI.
Kiedy requests nie wystarczy i potrzebny jest Playwright?
Kiedy strona buduje treść w JavaScripcie po załadowaniu. Wtedy requests dostaje pusty szkielet, a dane pojawiają się dopiero w przeglądarce. Sprawdziliśmy to na stronie quotes.toscrape.com/js/: zwykłe pobranie HTML zwraca 0 elementów z cytatami, a ta sama strona otwarta w Playwright pokazuje ich 10.
pw.py, Playwright dla Pythona 1.62
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(user_agent="QA-scraper-demo/1.0")
page.goto("https://quotes.toscrape.com/js/")
page.wait_for_selector("div.quote")
cytaty = page.locator("div.quote span.text").all_inner_texts()
print(len(cytaty), cytaty[0][:60])
browser.close()
Wbrew pozorom
Ta sama strona może dać dwa różne wyniki, i oba są „poprawne”. Na quotes.toscrape.com/js/ zapytanie HTTP widzi 0 cytatów, przeglądarka 10. Dokładnie ten mechanizm sprawia, że część treści sklepów i serwisów nie trafia do botów, które nie wykonują JavaScriptu. Dla testera to podpowiedź: jeśli coś jest ważne dla SEO albo dla czytników ekranu, sprawdźcie, czy istnieje w surowym HTML, a nie tylko na ekranie.
Playwright jest cięższy: uruchamia prawdziwą przeglądarkę, więc jest wolniejszy i zużywa więcej pamięci. Dlatego zasada jest prosta: najpierw sprawdźcie w narzędziach deweloperskich, zakładka Network, czy strona nie pobiera danych z ukrytego endpointu JSON. Jeśli tak, odpytujcie go bezpośrednio. Przeglądarka jest potrzebna dopiero wtedy, gdy dane powstają po stronie klienta. Porównanie narzędzi do automatyzacji przeglądarki znajdziecie w artykule Selenium, Cypress czy Playwright, a praktykę na szkoleniu Automatyzacja testów z narzędziem Playwright (Python).
Czy web scraping jest legalny w Polsce?
Web scraping jako sam mechanizm pobierania publicznie dostępnych stron nie jest zakazany, ale o legalności decyduje to, co pobieracie i co z tym robicie. Ten fragment opisuje przepisy i nie jest poradą prawną: przy projekcie, który zbiera dane na większą skalę albo dane osobowe, decyzję podejmujcie z prawnikiem.
Najlepszą lekcją z polskiej praktyki jest sprawa, w której nikt nie scrapował stron sklepów, a i tak zapadła kara. Według komunikatu UODO z 2023 roku Naczelny Sąd Administracyjny oddalił skargę kasacyjną spółki Bisnode i utrzymał pierwszą karę nałożoną przez polski organ nadzorczy, ponad 943 tys. zł. Spółka pozyskiwała dane z publicznych rejestrów i nie poinformowała osób, których dane przetwarzała. Wniosek dla każdego, kto zbiera dane z publicznych stron: „publiczne” nie znaczy „wolne od obowiązków”.
Jak scrapować etycznie i nie zostać zablokowanym?
Dobry scraper zachowuje się jak dobrze wychowany gość: przedstawia się, nie je za dużo i wychodzi, kiedy gospodarz poprosi. W praktyce to kilka konkretnych reguł:
- Sprawdźcie, czy jest API, eksport albo kanał RSS. Jeśli jest, używajcie go
- Przeczytajcie regulamin i robots.txt. Według RFC 9309 crawler powinien przetworzyć co najmniej 500 KiB pliku robots.txt i nie używać wersji z pamięci podręcznej dłużej niż 24 godziny
- Ustawcie User-Agent z nazwą projektu i adresem kontaktowym
- Ograniczcie tempo: jedno zapytanie na sekundę to rozsądny start dla małego serwisu. Przy kodzie 429 czekajcie tyle, ile mówi
Retry-After - Pobierajcie tylko pola, których naprawdę potrzebujecie. Mniej danych to mniej ryzyka i mniej czyszczenia
- Omijajcie dane osobowe, a jeśli są niezbędne, ustalcie podstawę prawną i obowiązek informacyjny przed startem
- Zapisujcie datę pobrania i adres źródła przy każdym rekordzie. Bez tego dane szybko stają się nieweryfikowalne
Te same reguły web scrapingu warto stosować, gdy scrapujecie własny serwis na produkcji. Crawler testowy bez limitu potrafi wygenerować ruch, który monitoring zinterpretuje jako atak, a zespół bezpieczeństwa spędzi popołudnie na analizie Waszego testu. Jeśli temat ochrony przed botami dotyczy Waszej aplikacji od drugiej strony, zajrzyjcie do naszych testów bezpieczeństwa aplikacji.
Najczęstsze błędy przy scrapowaniu danych
Te błędy widzieliśmy w kodzie zespołów, które przychodzą do nas na szkolenia i konsultacje. Każdy z nich ma prostą poprawkę:
Wspólny mianownik: web scraping to kod produkcyjny, nawet jeśli scraper powstał jako skrypt na jedno popołudnie. Potrzebuje testów, logów i właściciela. Przy ponad 450 000 testów automatycznych napisanych w Quality Island (dane własne, 2026) najczęściej powtarzamy zespołom jedno: kod, który nikogo nie alarmuje, kiedy przestaje działać, przestał działać dawno temu.
Od czego zacząć naukę web scrapingu
Kolejność, która sprawdza się u osób, które szkolimy z automatyzacji:
- Podstawy HTML i selektorów CSS. Bez nich każdy scraper będzie zgadywaniem. Pomoże artykuł o selektorach i lokalizowaniu elementów.
- HTTP: metody, kody odpowiedzi, nagłówki. Na tym opiera się i scraping, i testy API.
- Python z
requestsi Beautiful Soup na stronach do ćwiczeń, takich jak books.toscrape.com. - Playwright dla stron z JavaScriptem i dla testów end to end.
- Zapis do bazy i zapytania SQL, żeby zebrane dane dało się analizować.
Jeżeli wolicie uczyć się na żywo z trenerem, terminy najbliższych szkoleń z Pythona, API i Playwright znajdziecie w kalendarzu szkoleń Quality Island. O tym, jak testerzy wykorzystują takie umiejętności w praktyce zespołów QA, piszemy też szerzej na portalu Strefa QA w tekście o antywzorcach w testowaniu.
Co zabrać z tego artykułu
01Web scraping to automatyczne pobieranie danych ze stron HTML. Poprawna pisownia to scraping, „scrapping” to literówka.
02Najpierw szukajcie API albo ukrytego endpointu JSON. Scraping wybierajcie, gdy innej drogi nie ma.
03requests z Beautiful Soup wystarczy dla stron statycznych; dla treści budowanej w JavaScripcie potrzebny jest Playwright.
04W QA najcenniejszy jest test porównujący dane z frontu z API własnej aplikacji: tani w utrzymaniu, łapie drogie błędy.
05Publiczne dane nie są wolne od obowiązków: RODO art. 14, ochrona baz danych i regulaminy obowiązują także scrapery.
Źródło: RODO 2016/679, dyrektywa 96/9/WE, RFC 9309, RFC 6585, UODO 2023, Imperva 2025, testy kodu Quality Island z 10.10.2026.
Chcecie, żeby porównania frontu z API i inne testy regresji działały w Waszym pipeline, zamiast w jednym skrypcie na laptopie? Opiszcie aplikację, a zaproponujemy zakres.
Powiązane na blogu Quality Island
- Metody HTTP: GET, POST, PUT, PATCH i DELETE w testach API
- Postman: co to jest, do czego służy i jak zacząć testować API
- Selenium, Cypress czy Playwright: porównanie narzędzi do automatyzacji testów
- Testowanie e commerce: jak testować sklep internetowy, by sprzedawał bez przerw
- Jak skrócić testy regresyjne z 5 dni do 10 godzin: case Argos
Pełna lista źródeł
- Parlament Europejski i Rada UE, Rozporządzenie (UE) 2016/679 (RODO), 2016. Art. 14 ust. 3 (termin informacji, najpóźniej miesiąc) i art. 83 ust. 5 (kary do 20 mln EUR albo 4 procent obrotu).
- Parlament Europejski i Rada, Dyrektywa 96/9/WE w sprawie ochrony prawnej baz danych, 1996. Art. 7 (prawo sui generis) i art. 10 (ochrona przez 15 lat).
- Sejm RP, Ustawa z dnia 27 lipca 2001 r. o ochronie baz danych, Dz.U. 2001 nr 128 poz. 1402.
- IETF, RFC 9309: Robots Exclusion Protocol, 2022. Reguły nie są formą autoryzacji dostępu; limit 500 KiB i 24 godziny pamięci podręcznej.
- IETF, RFC 6585: Additional HTTP Status Codes, 2012. Kod 429 Too Many Requests i nagłówek Retry-After.
- UODO, komunikat o wyroku NSA w sprawie Bisnode, 20.09.2023. Kara ponad 943 tys. zł za brak obowiązku informacyjnego przy danych z rejestrów publicznych.
- Imperva, 2025 Bad Bot Report, 2025. Ruch automatyczny 51 procent, złośliwe boty 37 procent.
- Dokumentacja Beautiful Soup 4, Requests i Playwright dla Pythona, stan na 2026.
- Strony do ćwiczeń books.toscrape.com i quotes.toscrape.com, na których uruchomiliśmy przykłady 10.10.2026.
- Dane własne Quality Island, 2026: ponad 450 000 napisanych testów automatycznych, case study Argos z liczbami potwierdzonymi przez klienta.