CodeGym /Kursy Java /Python SELF PL /Obsługa błędów przy web scraping

Obsługa błędów przy web scraping

Python SELF PL
Poziom 32 , Lekcja 4
Dostępny

Jeśli dotarłeś do tego wykładu, to znaczy, że dobrze rozumiesz, jak działa web scraping i jak używać świetnego BeautifulSoup do wyciągania potrzebnych danych z HTML. Jednak w świecie web scrapingu nie wszystko jest takie różowe, jak piszą w podręcznikach. Czasami nasze marzenie o zbieraniu danych zamienia się w walkę z błędami, więc porozmawiajmy o tym, jak ominąć pułapki i sprawić, by nasz scraper był jak najbardziej odporny.

1. Typowe błędy w web scrapingu

Błąd 404 i inne błędy HTTP

Klasyczny problem: próbujesz uzyskać stronę, a zamiast treści dostajesz dumne "404 Not Found". Może się to zdarzyć, ponieważ strona została usunięta lub przeniesiona. Inne powszechne błędy HTTP to 403 (Forbidden), 500 (Internal Server Error) i 503 (Service Unavailable).

Zmiana struktury HTML

Spędziłeś mnóstwo czasu na pisaniu kodu do wyciągania danych, a następnego dnia strona postanowiła trochę "zabłysnąć" i zmieniła strukturę HTML. O nie, znowu trzeba wszystko przepisywać!

Ograniczenia ilości zapytań

Niektóre strony zaczynają podejrzewać coś nie tak, gdy skrypty scraperów cały dzień je "oblizują". W najlepszym przypadku dostaniesz blokadę na jakiś czas, w najgorszym - na zawsze.

Czas oczekiwania i timeouty

Czasami strony ładują się długo, a twój skrypt może się wywalić, jeśli czekanie przekroczy standardowy czas timeoutu.

2. Metody obsługi błędów

Użycie try-except

Twoje skrypty nie powinny się wykrzaczać przez każdą niespodziankę. Dodanie bloków try-except pomaga złapać błędy i sprawić, by twój scraper działał dalej, jakby nic się nie stało.

Python

    import requests
    from bs4 import BeautifulSoup
    
    url = 'http://example.com/some-nonexistent-page'
    
    try:
        response = requests.get(url)
        response.raise_for_status()  # Wywołuje HTTPError dla złych odpowiedzi
    except requests.exceptions.HTTPError as errh:
        print("Błąd HTTP:", errh)
    except requests.exceptions.ConnectionError as errc:
        print("Błąd połączenia:", errc)
    except requests.exceptions.Timeout as errt:
        print("Błąd timeoutu:", errt)
    except requests.exceptions.RequestException as err:
        print("Ups: coś innego", err)
        

Dobry skrypt nie tylko przechwytuje wyjątek - ma odpowiedź na każdy rodzaj błędu. Zostałeś zbanowany na IP - przechodzisz do następnego proxy, strona padła - na razie parsujesz inną. Jeśli na stronie nie znaleziono jakichś elementów, które tam powinny być, można powiadomić właściciela parsera, że należy zaktualizować skrypt parsujący i wysłać mu email.

Logowanie

"Po co te logi?" — zapytasz. A po to, że logi to twoje drugie oczy. Pomogą ci zrozumieć, co poszło nie tak i naprawić błąd jak najszybciej.

Python

import logging

logging.basicConfig(filename='scraper.log', level=logging.INFO)

try:
    # Twój kod scrapowania
    pass
except Exception as e:
    logging.error("Wystąpił wyjątek", exc_info=True)
    

Użycie timeoutów i retry

Czasem wszystko, czego potrzeba, to trochę poczekać i spróbować ponownie. Do tych celów doskonale nadają się timeouty i retry.

Python

try:
    response = requests.get(url, timeout=5)
    response.raise_for_status()
except requests.exceptions.Timeout:
    print("Wystąpił timeout. Próbuję ponownie...")
    # Powtórz zapytanie lub wykonaj inne działanie
    

3. Przykłady odpornego scrapowania

Prosty scraper z obsługą błędów

Stwórzmy mały, ale niezawodny scraper, który potrafi obsługiwać niektóre typowe błędy.

Python

import requests
from bs4 import BeautifulSoup
import time
import logging

logging.basicConfig(filename='scraper.log', level=logging.INFO)

url = 'http://example.com/some-nonexistent-page'

def fetch_html(url):
    try:
        response = requests.get(url, timeout=5)
        response.raise_for_status()
        return response.text
    except requests.exceptions.HTTPError as errh:
        logging.error("Błąd HTTP: %s", errh)
    except requests.exceptions.ConnectionError as errc:
        logging.error("Błąd połączenia: %s", errc)
    except requests.exceptions.Timeout as errt:
        logging.warning("Wystąpił timeout. Próbuję ponownie...")
        time.sleep(1)  # Poczekaj chwilę i spróbuj ponownie
        return fetch_html(url)
    except requests.exceptions.RequestException as err:
        logging.error("Ups: coś innego %s", err)
    return None

html_content = fetch_html(url)
if html_content:
    soup = BeautifulSoup(html_content, 'html.parser')
    # Twój kod do wyciągania danych
else:
    print("Nie udało się pobrać zawartości strony.")
    

Zapisywanie danych w częściach

Aby nie stracić już wyciągniętych danych w przypadku awarii, zapisuj je w częściach. Na przykład, jeśli wyciągasz informacje z listy stron, zapisując wyniki w miarę postępu, minimalizujesz ryzyko utraty danych.

Python

import csv

def save_to_csv(data, filename='data.csv'):
    with open(filename, mode='a', newline='') as file:
        writer = csv.writer(file)
        writer.writerow(data)
    

Dzięki temu, nawet jeśli twój skrypt rozbije się w połowie pracy, nie stracisz wszystkich danych i będziesz mógł kontynuować od ostatniego zapisanego punktu.

Strona, którą parsujesz, może zmienić się częściowo, a większość danych nadal będzie dostępna. Byłoby źle anulować parsowanie i stracić cenne dane, jeśli brakuje tylko niewielkiego procentu z nich.

1
Опрос
Pobieranie danych,  32 уровень,  4 лекция
недоступен
Pobieranie danych
Pobieranie danych
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION