Nếu bạn đã đến được bài giảng này, nghĩa là bạn đã có sự hiểu biết khá rõ
về cách hoạt động của web scraping và cách sử dụng
BeautifulSoup
để trích xuất dữ liệu cần thiết từ HTML. Tuy nhiên, trong thế giới web scraping, mọi thứ không phải lúc nào
cũng "xuôi chèo mát mái" như trong sách giáo khoa. Đôi khi, giấc mơ thu thập dữ liệu biến thành
cuộc đấu tranh với lỗi, vậy hãy cùng nói về cách vượt qua những chông gai và làm cho scraper của chúng ta
trở nên bền vững nhất có thể.
1. Các lỗi phổ biến trong web scraping
Lỗi 404 và các lỗi HTTP khác
Một vấn đề kinh điển: bạn cố gắng truy cập một trang, nhưng thay vì nội dung, bạn nhận được thông báo "404 Not Found". Điều này có thể xảy ra vì trang đã bị xóa hoặc di chuyển. Các lỗi HTTP phổ biến khác bao gồm 403 (Forbidden), 500 (Internal Server Error) và 503 (Service Unavailable).
Thay đổi cấu trúc HTML
Bạn đã dành rất nhiều thời gian viết mã để trích xuất dữ liệu, nhưng ngay sau đó, trang web quyết định "trưng diện" và thay đổi cấu trúc HTML. Ôi, lại phải viết lại mọi thứ!
Giới hạn số lượng yêu cầu
Một số trang web bắt đầu nghi ngờ khi có ai đó liên tục "gõ cửa" cả ngày. Ở mức tốt nhất, bạn có thể bị chặn tạm thời, còn tệ nhất thì mãi mãi!
Thời gian chờ và timeout
Đôi khi các trang tải chậm, và script của bạn có thể bị lỗi nếu thời gian chờ vượt quá giới hạn timeout mặc định.
2. Các phương pháp xử lý lỗi
Sử dụng try-except
Script của bạn không nên dừng lại trước mọi bất ngờ. Việc thêm các khối try-except
sẽ giúp bắt lỗi và làm cho scraper của bạn tiếp tục hoạt động như chưa có chuyện gì xảy ra.
import requests
from bs4 import BeautifulSoup
url = 'http://example.com/some-nonexistent-page'
try:
response = requests.get(url)
response.raise_for_status() # Gây ra HTTPError khi có phản hồi xấu
except requests.exceptions.HTTPError as errh:
print("Lỗi HTTP:", errh)
except requests.exceptions.ConnectionError as errc:
print("Lỗi kết nối:", errc)
except requests.exceptions.Timeout as errt:
print("Lỗi timeout:", errt)
except requests.exceptions.RequestException as err:
print("Ôi, có gì đó sai sai", err)
Một script tốt không chỉ bắt ngoại lệ mà còn có hành động đáp trả cho từng loại lỗi. Bị khóa IP - chuyển sang proxy khác, trang web sập - bạn chuyển sang parse trang khác. Nếu không tìm thấy các phần tử nhất định trên trang, bạn có thể thông báo cho chủ sở hữu parser rằng cần cập nhật script parsing và gửi email cho họ.
Ghi log
"Log để làm gì?" — bạn hỏi. Là để bạn có thêm "con mắt" thứ hai. Log sẽ giúp bạn tìm ra vấn đề và sửa lỗi nhanh nhất có thể.
import logging
logging.basicConfig(filename='scraper.log', level=logging.INFO)
try:
# Mã web scraping của bạn
pass
except Exception as e:
logging.error("Xảy ra ngoại lệ", exc_info=True)
Sử dụng timeout và retry
Đôi khi, tất cả những gì bạn cần là chờ đợi một chút và thử lại. Timeout và retry rất phù hợp cho mục đích này.
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
except requests.exceptions.Timeout:
print("Timeout xảy ra. Thử lại...")
# Thực hiện yêu cầu lại hoặc hành động khác
3. Ví dụ về web scraping bền vững
Scraper đơn giản với xử lý lỗi
Hãy tạo một scraper nhỏ nhưng đáng tin cậy, có thể xử lý một số lỗi phổ biến.
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("Lỗi HTTP: %s", errh)
except requests.exceptions.ConnectionError as errc:
logging.error("Lỗi kết nối: %s", errc)
except requests.exceptions.Timeout as errt:
logging.warning("Timeout xảy ra. Thử lại...")
time.sleep(1) # Chờ một chút và thử lại
return fetch_html(url)
except requests.exceptions.RequestException as err:
logging.error("Ôi, có gì đó sai sai: %s", err)
return None
html_content = fetch_html(url)
if html_content:
soup = BeautifulSoup(html_content, 'html.parser')
# Mã của bạn để trích xuất dữ liệu
else:
print("Không thể lấy nội dung trang.")
Lưu dữ liệu từng phần
Để không mất dữ liệu đã trích xuất trong trường hợp bị lỗi, hãy lưu chúng từng phần. Ví dụ, nếu bạn đang trích xuất thông tin từ danh sách các trang, lưu kết quả theo từng bước tiến sẽ giúp bạn giảm rủi ro mất mát dữ liệu.
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)
Bằng cách này, ngay cả khi script của bạn bị lỗi ở giữa, bạn sẽ không mất hết dữ liệu và có thể tiếp tục từ trạng thái đã lưu cuối cùng.
Trang mà bạn đang parse có thể thay đổi một phần, và phần lớn dữ liệu vẫn khả dụng. Sẽ không hay nếu hủy parse và mất đi các dữ liệu quý giá chỉ vì thiếu một phần nhỏ của chúng.
GO TO FULL VERSION