html.parser --- Trình phân tích cú pháp HTML và XHTML đơn giản

Source code: Lib/html/parser.py


Mô-đun này định nghĩa một lớp HTMLParser làm cơ sở để phân tích các tệp văn bản được định dạng bằng HTML (Ngôn ngữ đánh dấu siêu văn bản) và XHTML.

class html.parser.HTMLParser(*, convert_charrefs=True, scripting=False)

Tạo một phiên bản trình phân tích cú pháp có thể phân tích cú pháp đánh dấu không hợp lệ.

Nếu convert_charrefs là true (mặc định), tất cả các tham chiếu ký tự (ngoại trừ các tham chiếu ký tự trong các phần tử như scriptstyle) sẽ tự động được chuyển đổi thành các ký tự Unicode tương ứng.

Nếu scripting sai (mặc định), nội dung của phần tử noscript được phân tích cú pháp bình thường; nếu nó đúng, nó sẽ được trả về nguyên trạng mà không bị phân tích cú pháp.

Một phiên bản HTMLParser được cung cấp dữ liệu HTML và gọi các phương thức xử lý khi gặp thẻ bắt đầu, thẻ kết thúc, văn bản, nhận xét và các phần tử đánh dấu khác. Người dùng nên phân lớp HTMLParser và ghi đè các phương thức của nó để thực hiện hành vi mong muốn.

Trình phân tích cú pháp này không kiểm tra xem thẻ kết thúc có khớp với thẻ bắt đầu hay không hoặc gọi trình xử lý thẻ kết thúc cho các phần tử được đóng hoàn toàn bằng cách đóng phần tử bên ngoài.

Thay đổi trong phiên bản 3.4: Đã thêm đối số từ khóa convert_charrefs.

Thay đổi trong phiên bản 3.5: Giá trị mặc định cho đối số convert_charrefs hiện là True.

Thay đổi trong phiên bản 3.14.1: Đã thêm tham số scripting.

Ví dụ về ứng dụng trình phân tích cú pháp HTML

Như một ví dụ cơ bản, bên dưới là trình phân tích cú pháp HTML đơn giản sử dụng lớp HTMLParser để in ra thẻ bắt đầu, thẻ kết thúc và dữ liệu khi chúng gặp phải:

từ html.parser nhập HTMLParser

lớp MyHTMLParser(HTMLParser):
    def hand_starttag(self, tag, attrs):
        print("Gặp thẻ bắt đầu:", tag)

    def hand_endtag(tự, thẻ):
        print("Gặp thẻ kết thúc :", tag)

    def hand_data(tự, dữ liệu):
        print("Gặp một số dữ liệu :", data)

trình phân tích  pháp = MyHTMLParser()
Parser.feed('<html><head><title>Kiểm tra</title></head>'
            '<body><h1>Hãy phân tích cú pháp của tôi!</h1></body></html>')

Đầu ra sau đó sẽ là:

Gặp phải thẻ bắt đầu: html
Gặp phải thẻ bắt đầu: head
Gặp phải thẻ bắt đầu: tiêu đề
Gặp một số dữ liệu: Test
Gặp phải thẻ kết thúc: tiêu đề
Gặp phải thẻ kết thúc: head
Gặp phải thẻ bắt đầu: nội dung
Gặp phải thẻ bắt đầu: h1
Gặp một số dữ liệu: Phân tích cho tôi!
Gặp phải thẻ kết thúc: h1
Gặp phải thẻ kết thúc: nội dung
Gặp phải thẻ kết thúc: html

Phương pháp HTMLParser

Các phiên bản HTMLParser có các phương thức sau:

HTMLParser.feed(data)

Cung cấp một số văn bản cho trình phân tích cú pháp. Nó được xử lý trong chừng mực nó bao gồm các phần tử hoàn chỉnh; dữ liệu không đầy đủ sẽ được lưu vào bộ đệm cho đến khi có thêm dữ liệu được cung cấp hoặc close() được gọi. data phải là str.

HTMLParser.close()

Buộc xử lý tất cả dữ liệu được lưu vào bộ đệm như thể nó được theo sau bởi dấu cuối tệp. Phương thức này có thể được định nghĩa lại bởi lớp dẫn xuất để xác định quá trình xử lý bổ sung ở cuối đầu vào, nhưng phiên bản được xác định lại phải luôn gọi phương thức lớp cơ sở HTMLParserclose().

HTMLParser.reset()

Đặt lại phiên bản. Mất tất cả dữ liệu chưa được xử lý. Điều này được gọi ngầm tại thời điểm khởi tạo.

HTMLParser.getpos()

Trả về số dòng hiện tại và offset.

HTMLParser.get_starttag_text()

Trả về văn bản của thẻ bắt đầu được mở gần đây nhất. Điều này thường không cần thiết để xử lý có cấu trúc, nhưng có thể hữu ích khi xử lý HTML "như đã triển khai" hoặc để tạo lại đầu vào với những thay đổi tối thiểu (có thể giữ nguyên khoảng trắng giữa các thuộc tính, v.v.).

Các phương thức sau đây được gọi khi gặp phải các phần tử dữ liệu hoặc đánh dấu và chúng được ghi đè trong một lớp con. Việc triển khai lớp cơ sở không làm gì cả (ngoại trừ handle_startendtag()):

HTMLParser.handle_starttag(tag, attrs)

Phương thức này được gọi để xử lý thẻ bắt đầu của một phần tử (ví dụ: <div id="main">).

Đối số tag là tên của thẻ được chuyển đổi thành chữ thường. Đối số attrs là danh sách các cặp (name, value) chứa các thuộc tính được tìm thấy bên trong dấu ngoặc <> của thẻ. name sẽ được dịch sang chữ thường và các trích dẫn trong value đã bị xóa, đồng thời các tham chiếu ký tự và thực thể đã được thay thế.

Ví dụ: đối với thẻ <A HREF="https://www.cwi.nl/">, phương thức này sẽ được gọi là handle_starttag('a', [('href', 'https://www.cwi.nl/')]).

Tất cả các tham chiếu thực thể từ html.entities đều được thay thế trong các giá trị thuộc tính.

HTMLParser.handle_endtag(tag)

Phương thức này được gọi để xử lý thẻ kết thúc của một phần tử (ví dụ: </div>).

Đối số tag là tên của thẻ được chuyển đổi thành chữ thường.

HTMLParser.handle_startendtag(tag, attrs)

Tương tự như handle_starttag(), nhưng được gọi khi trình phân tích cú pháp gặp thẻ trống kiểu XHTML (<img ... />). Phương thức này có thể bị ghi đè bởi các lớp con yêu cầu thông tin từ vựng cụ thể này; việc triển khai mặc định chỉ đơn giản gọi handle_starttag()handle_endtag().

HTMLParser.handle_data(data)

Phương thức này được gọi để xử lý dữ liệu tùy ý (ví dụ: các nút văn bản và nội dung của các phần tử như scriptstyle).

HTMLParser.handle_entityref(name)

Phương thức này được gọi để xử lý tham chiếu ký tự được đặt tên có dạng &name; (ví dụ: &gt;), trong đó name là tham chiếu thực thể chung (ví dụ: 'gt'). Phương thức này chỉ được gọi nếu convert_charrefs sai.

HTMLParser.handle_charref(name)

Phương thức này được gọi để xử lý các tham chiếu ký tự số thập phân và thập lục phân có dạng &#NNN;&#xNNN;. Ví dụ: số thập phân tương đương cho &gt;&#62;, trong khi thập lục phân là &#x3E;; trong trường hợp này phương thức sẽ nhận được '62' hoặc 'x3E'. Phương thức này chỉ được gọi nếu convert_charrefs sai.

HTMLParser.handle_comment(data)

Phương thức này được gọi khi gặp một bình luận (ví dụ: <!--comment-->).

Ví dụ: nhận xét <!-- comment --> sẽ khiến phương thức này được gọi với đối số ' comment '.

Nội dung của các bình luận có điều kiện (condcom) của Internet Explorer cũng sẽ được gửi đến phương thức này, vì vậy, đối với <!--[if IE 9]>IE9-specific content<![endif]-->, phương thức này sẽ nhận được '[if IE 9]>IE9-specific content<![endif]'.

HTMLParser.handle_decl(decl)

Phương thức này được gọi để xử lý khai báo loại tài liệu HTML (ví dụ: <!DOCTYPE html>).

Tham số decl sẽ là toàn bộ nội dung của phần khai báo bên trong đánh dấu <!...> (ví dụ: 'DOCTYPE html').

HTMLParser.handle_pi(data)

Phương thức được gọi khi gặp lệnh xử lý. Tham số data sẽ chứa toàn bộ hướng dẫn xử lý. Ví dụ: đối với lệnh xử lý <?proc color='red'>, phương thức này sẽ được gọi là handle_pi("proc color='red'"). Nó được dự định sẽ bị ghi đè bởi một lớp dẫn xuất; việc triển khai lớp cơ sở không làm gì cả.

Ghi chú

Lớp HTMLParser sử dụng quy tắc cú pháp SGML để xử lý các hướng dẫn. Lệnh xử lý XHTML sử dụng '?' ở cuối sẽ khiến '?' được đưa vào data.

HTMLParser.unknown_decl(data)

Phương thức này được gọi khi trình phân tích cú pháp đọc một khai báo không được nhận dạng.

Tham số data sẽ là toàn bộ nội dung của phần khai báo bên trong mã đánh dấu <![...]>. Đôi khi việc ghi đè bởi một lớp dẫn xuất sẽ rất hữu ích. Việc triển khai lớp cơ sở không làm gì cả.

Ví dụ

Lớp sau đây triển khai một trình phân tích cú pháp sẽ được sử dụng để minh họa thêm các ví dụ:

từ html.parser nhập HTMLParser
từ html.entities nhập tên2codepoint

lớp MyHTMLParser(HTMLParser):
    def hand_starttag(self, tag, attrs):
        print("Thẻ bắt đầu:", thẻ)
        cho attr trong attrs:
            print("attr:", attr)

    def hand_endtag(tự, thẻ):
        print("Thẻ kết thúc:", thẻ)

    def hand_data(tự, dữ liệu):
        print("Dữ liệu:", dữ liệu)

    def hand_comment(tự, dữ liệu):
        print("Nhận xét:", dữ liệu)

    def hand_entityref(bản thân, tên):
        c = chr(name2codepoint[name])
        print("Tên được đặt tên:", c)

    def hand_charref(tự, tên):
        nếu tên.startswith('x'):
            c = chr(int(name[1:], 16))
        khác:
            c = chr(int(tên))
        print("Số ent:", c)

    def hand_decl(tự, dữ liệu):
        print("Decl:", dữ liệu)

trình phân tích  pháp = MyHTMLParser()

Phân tích một loại tài liệu:

>>> Parser.feed('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
...'"http://www.w3.org/TR/html4/strict.dtd">')
Tháng mười hai : DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"

Phân tích một phần tử với một vài thuộc tính và tiêu đề:

>>> Parser.feed('<img src="python-logo.png" alt="Logo Python">')
Thẻ bắt đầu: img
     attr: ('src', 'python-logo.png')
     attr: ('alt', 'Biểu tượng Python')
>>>
>>> Parser.feed('<h1>Python</h1>')
Thẻ bắt đầu: h1
Dữ liệu: Python
Thẻ kết thúc: h1

Nội dung của các phần tử như scriptstyle được trả về nguyên trạng mà không cần phân tích cú pháp thêm:

>>> Parser.feed('<style type="text/css">#python { color: green </style>')
Thẻ bắt đầu: phong cách
     attr: ('loại', 'văn bản/css')
Dữ liệu: #python {màu: xanh lá cây }
Thẻ kết thúc: phong cách

>>> Parser.feed('<script type="text/javascript">'
... 'cảnh báo("<strong>xin chào! &#9786;</strong>");</script>')
Thẻ bắt đầu: kịch bản
     attr: ('gõ', 'văn bản/javascript')
Dữ liệu: cảnh báo("<strong>xin chào! &#9786;</strong>");
Thẻ kết thúc: kịch bản

Phân tích nhận xét:

>>> Parser.feed('<!--a comment-->'
... '<!--[if IE 9]>Nội dung dành riêng cho IE<![endif]-->')
Bình luận: một bình luận
Nhận xét : [if IE 9]>Nội dung dành riêng cho IE<![endif]

Phân tích các tham chiếu ký tự có tên và số và chuyển đổi chúng thành char chính xác (lưu ý: 3 tham chiếu này đều tương đương với '>'):

>>> trình phân tích  pháp = MyHTMLParser()
>>> Parser.feed('>&#62;&#x3E;')
Dữ liệu : >>>

>>> trình phân tích  pháp = MyHTMLParser(convert_charrefs=False)
>>> Parser.feed('>&#62;&#x3E;')
Tên ent: >
Số : >
Số : >

Việc cung cấp các phần chưa hoàn chỉnh cho feed() có hiệu quả, nhưng handle_data() có thể được gọi nhiều lần nếu convert_charrefs sai:

>>> cho đoạn trong ['<sp', 'an>buff', 'ered', ' text</s', 'pan>']:
... trình phân tích  pháp.feed(chunk)
...
Thẻ bắt đầu: khoảng
Dữ liệu: tăng cường
Dữ liệu: đã xóa
Dữ liệu: văn bản
Thẻ kết thúc: khoảng

Phân tích cú pháp HTML không hợp lệ (ví dụ: thuộc tính không được trích dẫn) cũng hoạt động:

>>> Parser.feed('<p><a class=link href=#main>súp thẻ</p ></a>')
Thẻ bắt đầu: p
Thẻ bắt đầu: a
     attr: ('lớp', 'liên kết')
     attr: ('href', '#main')
Dữ liệu: thẻ súp
Thẻ kết thúc :p
Thẻ kết thúc: a