Web scraping v Pythonu s httpx a BeautifulSoup – eticky
Jak stahovat data z webu pomocí httpx a BeautifulSoup, respektovat robots.txt, nezahltit server a vyhnout se právním problémům. Praktický kód a checklist.
Scraping je automatické získávání dat z webových stránek. Technicky je to pár řádků Pythonu, ale snadno se z něj stane nežádoucí zátěž pro cizí server. Tento návod ukazuje, jak scrapovat funkčně i slušně.
Než začnete: má to smysl?
Projděte si tento seznam:
- Existuje API nebo export dat? Pokud ano, použijte je. Je stabilnější a provozovatel s ním počítá.
- Co říkají podmínky užití webu? Některé weby scraping výslovně zakazují.
- Jde o osobní údaje? Jména, e-maily, telefony spadají pod GDPR i tehdy, když jsou veřejně dostupné. Jejich hromadný sběr potřebuje právní titul.
- Autorská práva a databázová práva. Převzetí podstatné části cizí databáze může být problém i bez porušení technických opatření.
Tento článek není právní rada – u komerčního využití se poraďte s právníkem.
Instalace
pip install httpx beautifulsoup4 lxml
lxml je rychlý parser; BeautifulSoup umí i vestavěný html.parser, pokud nechcete další závislost.
Respektujte robots.txt
Standardní knihovna obsahuje parser robots.txt:
from urllib.robotparser import RobotFileParser
USER_AGENT = "MujScraper/1.0 (+mailto:[email protected])"
rp = RobotFileParser("https://example.com/robots.txt")
rp.read()
if not rp.can_fetch(USER_AGENT, "https://example.com/produkty"):
raise SystemExit("robots.txt přístup nepovoluje")
delay = rp.crawl_delay(USER_AGENT) or 2
Poznámka: RobotFileParser.read() používá urllib, ne httpx. Pro jednoduché skripty to nevadí.
User-Agent s kontaktem je slušnost – provozovatel vás může kontaktovat místo toho, aby vás rovnou zablokoval.
Stažení a parsování stránky
import time
import httpx
from bs4 import BeautifulSoup
client = httpx.Client(
headers={"User-Agent": USER_AGENT},
timeout=15,
follow_redirects=True,
)
def stahni(url: str) -> BeautifulSoup:
r = client.get(url)
r.raise_for_status()
return BeautifulSoup(r.text, "lxml")
soup = stahni("https://example.com/produkty")
for karta in soup.select("div.product"):
nazev = karta.select_one("h2").get_text(strip=True)
cena = karta.select_one(".price").get_text(strip=True)
odkaz = karta.select_one("a")["href"]
print(nazev, cena, odkaz)
Pozor: httpx na rozdíl od requests ve výchozím stavu nesleduje přesměrování – proto follow_redirects=True.
CSS selektory vs. find
soup.select("table.ceny tr") # všechny shody, CSS selektor
soup.select_one("h1") # první shoda nebo None
soup.find_all("a", class_="detail") # alternativa bez CSS
select_one vrací None, pokud nic nenajde. Volání .get_text() na None skončí AttributeError – u nespolehlivých stránek kontrolujte.
Relativní odkazy a stránkování
from urllib.parse import urljoin
base = "https://example.com/produkty"
url = base
while url:
soup = stahni(url)
# ... zpracování ...
dalsi = soup.select_one("a.next")
url = urljoin(url, dalsi["href"]) if dalsi else None
time.sleep(delay)
urljoin správně složí /produkty?page=2 i ?page=2 s aktuální adresou.
Šetrnost k serveru
- Prodleva mezi požadavky – aspoň 1–2 sekundy, nebo hodnota z
Crawl-delay. - Žádný paralelní útok. Pokud už používáte async, omezte souběh na 1–2 požadavky na doménu.
- Cache. Stránky během vývoje ukládejte na disk a parsujte z nich. Ladit selektory proti živému webu stovkami požadavků je zbytečné.
- Reagujte na 429 a 503. Zpomalte, respektujte hlavičku
Retry-After, případně skončete. - Scrapujte mimo špičku a jen to, co skutečně potřebujete.
Jednoduchá cache:
from pathlib import Path
import hashlib
CACHE = Path(".cache"); CACHE.mkdir(exist_ok=True)
def stahni_html(url: str) -> str:
soubor = CACHE / hashlib.sha256(url.encode()).hexdigest()
if soubor.exists():
return soubor.read_text(encoding="utf-8")
r = client.get(url); r.raise_for_status()
soubor.write_text(r.text, encoding="utf-8")
time.sleep(delay)
return r.text
Uložení dat
import csv
with open("produkty.csv", "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=["nazev", "cena", "url"])
w.writeheader()
w.writerows(zaznamy)
Pro Excel s českými znaky použijte encoding="utf-8-sig".
Když stránka obsah generuje JavaScriptem
BeautifulSoup vidí jen HTML, které vrátil server. Pokud se data načítají JavaScriptem, otevřete v prohlížeči DevTools → Network a hledejte XHR/fetch požadavky – často najdete JSON endpoint, který je jednodušší i šetrnější než renderovat stránku. Headless prohlížeč (Playwright) je až poslední možnost.
Co nedělat
- Neobcházejte přihlášení, CAPTCHA ani jiná technická opatření.
- Nerotujte IP adresy a User-Agenty, abyste se vyhnuli blokaci – pokud vás web blokuje, dává tím jasně najevo svůj postoj.
- Nepublikujte převzatý obsah jako vlastní.
Dodržíte-li tato pravidla, je scraping užitečný nástroj pro sledování cen, výzkum nebo archivaci vlastních dat.