JavaRush /Курсы /Python SELF /Обработка ошибок при веб-скрейпинге

Обработка ошибок при веб-скрейпинге

Python SELF
32 уровень , 4 лекция
Открыта

Если вы добрались до этой лекции, значит, уже хорошо представляете, как работает веб-скрейпинг и как использовать прекрасный BeautifulSoup для извлечения нужных данных из HTML. Однако в мире веб-скрейпинга не все так гладко, как пишут в учебниках. Иногда, наша мечта собрать данные превращается в борьбу с ошибками, так что давайте поговорим о том, как обойти подводные камни и сделать наш скрейпер максимально устойчивым.

1. Общие ошибки в веб-скрейпинге

Ошибка 404 и другие HTTP-ошибки

Классическая проблема: пытаетесь получить страницу, а вам вместо контента возвращают гордое "404 Not Found". Это может случиться из-за того, что страница была удалена или перемещена. Другие распространенные HTTP ошибки включают 403 (Forbidden), 500 (Internal Server Error) и 503 (Service Unavailable).

Изменение HTML-структуры

Вы потратили кучу времени на написание кода для извлечения данных, а на следующий день сайт решил немного "покрасоваться" и поменял HTML-структуру. Ой, опять нужно всё переписывать!

Ограничения на количество запросов

Некоторые сайты начинают подозревать неладное, когда их целый день облизывают веб-скрейперы. В лучшем случае, вас заблокируют на какое-то время, а в худшем - навсегда.

Время ожидания и таймауты

Иногда страницы долго грузятся, и ваш скрипт может упасть, если ожидание превышает стандартное время таймаута.

2. Методы обработки ошибок

Использование try-except

Ваши скрипты не должны падать от любой неожиданности. Добавление блоков try-except помогает поймать ошибки и сделать так, чтобы ваш веб-скрейпер не упал, а продолжил работать, как ни в чем не бывало.

Python

    import requests
    from bs4 import BeautifulSoup
    
    url = 'http://example.com/some-nonexistent-page'
    
    try:
        response = requests.get(url)
        response.raise_for_status()  # Вызывает HTTPError для плохих ответов
    except requests.exceptions.HTTPError as errh:
        print("HTTP Error:", errh)
    except requests.exceptions.ConnectionError as errc:
        print("Error Connecting:", errc)
    except requests.exceptions.Timeout as errt:
        print("Timeout Error:", errt)
    except requests.exceptions.RequestException as err:
        print("OOps: Something Else", err)
        

Хороший скрипт не просто перехватывает исключение - у него есть ответное действие на каждый тип ошибки. Вас забанили по IP - перешли к следующему proxy, упал сайт - вы пока парсите другой. Если не найдены какие-то элементы на странице, которые там должны быть, то можно уведомить владельца парсера о том, что нужно обновить скрипт парсинга и выслать ему email.

Логирование

"Зачем эти логи?" — спросите вы. А затем, что логи — это ваше второе зрение. Они помогут вам разобраться, что пошло не так, и исправить ошибку как можно быстрее.

Python

import logging

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

try:
    # Ваш код скрейпинга
    pass
except Exception as e:
    logging.error("Exception occurred", exc_info=True)
    

Использование таймаутов и ретраев

Иногда, все, что нужно — это немного подождать и попробовать снова. Для этих целей отлично подойдут таймауты и ретраи.

Python

try:
    response = requests.get(url, timeout=5)
    response.raise_for_status()
except requests.exceptions.Timeout:
    print("Timeout occurred. Retrying...")
    # Повторите запрос или выполните другое действие
    

3. Примеры устойчивого скрейпинга

Простой скрейпер с обработкой ошибок

Давайте создадим небольшой, но надежный скрейпер, который умеет обрабатывать некоторые распространенные ошибки.

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("HTTP Error: %s", errh)
    except requests.exceptions.ConnectionError as errc:
        logging.error("Error Connecting: %s", errc)
    except requests.exceptions.Timeout as errt:
        logging.warning("Timeout occurred. Retrying...")
        time.sleep(1)  # Немного подождем и попробуем снова
        return fetch_html(url)
    except requests.exceptions.RequestException as err:
        logging.error("OOps: Something Else %s", err)
    return None

html_content = fetch_html(url)
if html_content:
    soup = BeautifulSoup(html_content, 'html.parser')
    # Ваш код для извлечения данных
else:
    print("Failed to retrieve the page content.")
    

Сохранение данных частями

Чтобы не потерять уже извлеченные данные в случае сбоя, сохраняйте их частями. Например, если вы извлекаете информацию из списка страниц, сохраняя результаты по мере продвижения, вы минимизируете риск потери данных.

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)
    

Таким образом, даже если ваш скрипт упадет на середине работы, вы не потеряете все данные и сможете продолжить с последнего сохраненного состояния.

Страница, которую вы парсите, может поменяться частично, и большая часть данных по прежнему будет доступна. Было бы нехорошо отменять парсинг и терять ценные данные, если отсутствует совсем небольшой процент их.

Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 64
25 июля 2025
Впереди требуется "логирование" но дыра... кстати, может помочь "уровень 38 лекция 0" Это очень плохо 🤮