Cào dữ liệu web bằng Python với requests và BeautifulSoup: hướng dẫn có trách nhiệm

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Cào dữ liệu (web scraping) là kỹ thuật tự động đọc nội dung trang web và trích xuất thông tin cần thiết, ví dụ danh sách sản phẩm hay tiêu đề bài viết. Bài này hướng dẫn bạn làm từng bước với Python, requests và BeautifulSoup, đồng thời nhắc về những nguyên tắc đạo đức cần tuân thủ.

Trước khi cào: kiểm tra quyền và quy tắc

Không phải trang nào cũng cho phép cào dữ liệu. Trước khi bắt đầu, hãy làm ba việc:

  • Đọc điều khoản sử dụng của trang xem có cấm thu thập tự động không.
  • Xem file /robots.txt ở gốc tên miền để biết đường dẫn nào được phép truy cập.
  • Nếu trang có API chính thức, hãy ưu tiên dùng API thay vì cào HTML.

Khi cào, hãy gửi yêu cầu thưa thớt, đặt khoảng nghỉ giữa các lần gọi để không gây tải cho máy chủ, và không thu thập dữ liệu cá nhân hoặc nội dung có bản quyền để phát tán. Trong bài, ta thực hành trên trang luyện tập dành cho người học scraping là books.toscrape.com, được tạo ra để thử nghiệm.

Bước 1: Cài thư viện

python -m venv venv
# kích hoạt môi trường ảo rồi:
pip install requests beautifulsoup4

Bước 2: Tải trang và phân tích HTML

import requests
from bs4 import BeautifulSoup

URL = "https://books.toscrape.com/"
headers = {"User-Agent": "hoc-scraping-demo/1.0"}

response = requests.get(URL, headers=headers, timeout=10)
response.raise_for_status()
response.encoding = response.apparent_encoding

soup = BeautifulSoup(response.text, "html.parser")
print(soup.title.get_text(strip=True))

Ba chi tiết đáng nhớ: timeout tránh treo vô hạn khi mạng chậm; raise_for_status() báo lỗi nếu mã trạng thái là 4xx hoặc 5xx; và thiết lập User-Agent rõ ràng giúp chủ trang biết ai đang truy cập.

Bước 3: Tìm phần tử bằng CSS selector

Mở trang trong trình duyệt, bấm chuột phải vào một sản phẩm và chọn Inspect để xem cấu trúc HTML. Trên trang mẫu, mỗi cuốn sách nằm trong thẻ article có lớp product_pod. Ta dùng select với CSS selector để lấy:

books = []

for item in soup.select("article.product_pod"):
    title = item.h3.a["title"]
    price = item.select_one("p.price_color").get_text(strip=True)
    link = item.h3.a["href"]
    books.append({"title": title, "price": price, "link": link})

for b in books[:5]:
    print(b["title"], "-", b["price"])

Ghi nhớ các hàm chính: select trả về danh sách, select_one trả về phần tử đầu tiên hoặc None, get_text(strip=True) lấy chữ đã cắt khoảng trắng, và cú pháp tag["thuộc_tính"] lấy giá trị thuộc tính như href. Hãy kiểm tra None khi cấu trúc trang không đồng đều, nếu không bạn sẽ gặp lỗi AttributeError.

Bước 4: Đi qua nhiều trang

Trang mẫu có nút "next" dẫn sang trang kế tiếp. Ta theo liên kết đó cho đến khi hết, kèm khoảng nghỉ lịch sự:

import time
from urllib.parse import urljoin

def scrape_all(start_url, max_pages=3):
    url = start_url
    results = []
    for _ in range(max_pages):
        res = requests.get(url, headers=headers, timeout=10)
        res.raise_for_status()
        res.encoding = res.apparent_encoding
        page = BeautifulSoup(res.text, "html.parser")

        for item in page.select("article.product_pod"):
            results.append({
                "title": item.h3.a["title"],
                "price": item.select_one("p.price_color").get_text(strip=True),
            })

        next_link = page.select_one("li.next a")
        if not next_link:
            break
        url = urljoin(url, next_link["href"])
        time.sleep(1)  # nghỉ 1 giây giữa các lần gọi
    return results

Hàm urljoin ghép liên kết tương đối thành địa chỉ đầy đủ. Giới hạn max_pages giúp bạn thử nghiệm an toàn trước khi chạy rộng.

Bước 5: Lưu ra file CSV

import csv

data = scrape_all(URL)

with open("books.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.DictWriter(f, fieldnames=["title", "price"])
    writer.writeheader()
    writer.writerows(data)

print("Đã lưu", len(data), "dòng")

Dùng bảng mã utf-8-sig giúp Excel đọc đúng tiếng Việt có dấu khi mở file CSV.

Khi nào requests không đủ

  • Trang nạp dữ liệu bằng JavaScript: HTML tải về không chứa dữ liệu. Hãy kiểm tra tab Network của trình duyệt, nhiều khi có API JSON gọi trực tiếp được. Nếu không, mới cần công cụ điều khiển trình duyệt như Playwright.
  • Trang yêu cầu đăng nhập hoặc có chống bot: hãy cân nhắc đạo đức và điều khoản; đừng cố vượt qua các biện pháp bảo vệ.
  • Cấu trúc HTML thay đổi: code cào rất mong manh, cần bảo trì định kỳ.

Kết luận

Bạn đã đi từ tải trang, chọn phần tử, duyệt nhiều trang đến xuất CSV. Điều quan trọng không kém kỹ thuật là thái độ: cào dữ liệu có trách nhiệm, tôn trọng robots.txt, giới hạn tần suất và ưu tiên API chính thức.

Nếu muốn dùng Python cho phân tích dữ liệu và tự động hóa, bạn có thể tham khảo các khóa học tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment