Kỹ Thuật Tìm Kiếm và Duyệt DOM Nâng Cao trong BeautifulSoup 4

Thư viện BeautifulSoup (bs4) cung cấp một bộ API linh hoạt để điều hướng và truy vấn cấu trúc DOM của tài liệu HTML hoặc XML. Hai phương thức nền tảng nhất dùng để trích xuất dữ liệu là find() (trả về phần tử khớp đầu tiên) và find_all() (trả về danh sách tất cả các phần tử khớp).

Các tham số cốt lõi

Cả hai phương thức trên đều chia sẻ chung một tập hợp các bộ lọc tham số:

  • name: Lọc theo tên của thẻ HTML (ví dụ: 'div', 'span').
  • string: Lọc dựa trên nội dung văn bản nằm bên trong thẻ.
  • recursive: Tham số boolean xác định phạm vi tìm kiếm. Mặc định là True (tìm kiếm toàn bộ cây con cháu). Nếu đặt là False, thuật toán chỉ quét các nút con trực tiếp.

Sử dụng find_all() để truy vấn dữ liệu

Dưới đây là các kịch bản tìm kiếm phổ biến sử dụng find_all(), áp dụng trên một đối tượng parser giả định:

import re

# 1. Tìm kiếm theo tên thẻ
parser.find_all("header")

# 2. Lọc theo thuộc tính
# Tìm thẻ có id chính xác
parser.find_all(id="user-profile")

# Kết hợp Regular Expression để tìm id có chứa chuỗi "admin"
parser.find_all(id=re.compile("admin"))

# Áp dụng nhiều điều kiện thuộc tính cùng lúc
parser.find_all(href=re.compile("dashboard"), class_="nav-item")

# Truy vấn các thuộc tính tùy chỉnh (data-*) thông qua dictionary
parser.find_all(attrs={"data-tracking": "click-event"})

# Lưu ý: 'class' là từ khóa dành riêng của Python, do đó bs4 sử dụng 'class_'
parser.find_all(class_="alert-warning")

# 3. Lọc theo nội dung văn bản (string)
# Tìm kiếm chuỗi chính xác
parser.find_all(string="Welcome back!")

# Tìm kiếm khớp với bất kỳ chuỗi nào trong danh sách
parser.find_all(string=["Login", "Register", "Forgot Password"])

# Sử dụng Regex để khớp định dạng văn bản
parser.find_all(string=re.compile(r"Order #\d{4}"))

# Truyền vào một hàm callback tùy chỉnh để lọc
def has_exclamation(text):
    return text and "!" in text
parser.find_all(string=has_exclamation)

# 4. Giới hạn và phạm vi tìm kiếm
# Chỉ lấy tối đa 3 kết quả đầu tiên
parser.find_all("article", limit=3)

# Chỉ tìm các thẻ div ở cấp con trực tiếp của body
parser.body.find_all("div", recursive=False)

Cú pháp viết tắt

Đối tượng BeautifulSoup có thể được gọi trực tiếp như một hàm, hành vi này tương đương hoàn toàn với việc gọi find_all().

# Cú pháp rút gọn
parser("footer")
# Tương đương với: parser.find_all("footer")

parser.title(string=True)
# Tương đương với: parser.title.find_all(string=True)

Tích hợp CSS Selectors

Ngoài các bộ lọc tích hợp sẵn, bs4 còn hỗ trợ truy vấn thông qua cú pháp CSS Selector tiêu chuẩn thông qua phương thức select()select_one().

# Tìm thẻ theo tên
parser.select("main")

# Tìm phần tử con cháu (descendant) lồng nhau
parser.select("body main p")

# Tìm phần tử con trực tiếp (child)
parser.select("ul > li")
parser.select("div > #sidebar")

# Tìm phần tử anh em liền kề (adjacent sibling) đứng ngay sau h2
parser.select("h2 + p")

# Tìm tất cả phần tử anh em theo sau (general sibling)
parser.select("h2 ~ ul")

# Gom nhóm nhiều selector
parser.select("#main-content, .sidebar-widget")

# --- Truy vấn chi tiết theo thuộc tính ---
# Thuộc tính khớp chính xác
parser.select('a[rel="noopener"]')

# Thuộc tính bắt đầu bằng chuỗi chỉ định (^=)
parser.select('img[src^="https://"]')

# Thuộc tính kết thúc bằng chuỗi chỉ định ($=)
parser.select('a[href$=".zip"]')

# Thuộc tính có chứa chuỗi chỉ định (*=)
parser.select('div[class*="container"]')

# Khớp mã ngôn ngữ có chứa tiền tố (|=)
parser.select('html[lang|="en"]')

# Chỉ lấy phần tử đầu tiên khớp với selector
parser.select_one(".primary-button")

Điều hướng cây DOM (Tree Traversal)

Bên cạnh việc tìm kiếm từ gốc, bs4 cung cấp các phương thức để di chuyển tương đối từ một nút đã biết.

Duyệt lên trên (Parents)

current_node = parser("span", id="product-price")[0]

# Tìm thẻ cha gần nhất thỏa mãn điều kiện
current_node.find_parent("div")

# Tìm tất cả các thẻ tổ tiên thỏa mãn điều kiện
current_node.find_parents("section")

Duyệt ngang hàng (Siblings)

# Tìm các nút anh em nằm phía sau nút hiện tại
current_node.find_next_sibling("span")
current_node.find_next_siblings("span")

# Tìm các nút anh em nằm phía trước nút hiện tại
current_node.find_previous_sibling("label")
current_node.find_previous_siblings("label")

Duyệt tuần tự theo tài liệu (Next & Previous)

Các phương thức này không quan tâm đến cấu trúc cây cha-con hay anh em, mà chỉ đơn thuần quét các phần tử được phân tích tiếp theo hoặc trước đó trong mã nguồn HTML.

# Duyệt các phần tử xuất hiện sau nút hiện tại
current_node.find_next("p")
current_node.find_all_next("p")

# Duyệt các phần tử xuất hiện trước nút hiện tại
current_node.find_previous("h3")
current_node.find_all_previous("h3")

Thẻ: python beautifulsoup4 web-scraping html-parser css-selectors

Đăng vào ngày 5 tháng 9 lúc 00:50