Làm thế nào để phân tích cú pháp HTML hoạt động nếu chúng không sử dụng regexp?


96

Tôi thấy các câu hỏi mỗi ngày hỏi cách phân tích cú pháp hoặc trích xuất nội dung nào đó từ một chuỗi HTML nào đó và câu trả lời / nhận xét đầu tiên luôn là "Đừng sử dụng RegEx để phân tích cú pháp HTML, kẻo bạn cảm thấy tức giận!" (phần cuối đó đôi khi bị lược bỏ).

Điều này khá khó hiểu đối với tôi, tôi luôn nghĩ rằng nói chung, cách tốt nhất để phân tích cú pháp bất kỳ chuỗi phức tạp nào là sử dụng một biểu thức chính quy. Vậy trình phân tích cú pháp HTML hoạt động như thế nào? Nó không sử dụng biểu thức chính quy để phân tích cú pháp.

Một đối số cụ thể để sử dụng biểu thức chính quy là không phải lúc nào cũng có một giải pháp thay thế phân tích cú pháp (chẳng hạn như JavaScript, trong đó DOMDocument không phải là một tùy chọn khả dụng phổ biến). jQuery, chẳng hạn, dường như quản lý tốt bằng cách sử dụng regex để chuyển đổi một chuỗi HTML thành các nút DOM.

Không chắc có nên CW điều này hay không, đó là một câu hỏi thực sự mà tôi muốn được trả lời và không thực sự có ý định trở thành một chủ đề thảo luận.


Được gắn thẻ lại để thêm phân tích cú pháp và phân tích cú pháp html - @Andy E, tôi hy vọng điều đó ổn với bạn - tôi nghĩ nó sẽ hữu ích.
JXG

@JXG: Điều đó ổn với tôi, cảm ơn :-)
Andy E

Câu trả lời:


65

Thông thường bằng cách sử dụng tokeniser. Đặc tả HTML5 dự thảo có một thuật toán mở rộng để xử lý "HTML trong thế giới thực".


1
Tìm tốt ... để trích dẫn "Để xử lý những trường hợp này, trình phân tích cú pháp có cấp độ lồng tập lệnh, mức này ban đầu phải được đặt thành 0 và cờ tạm dừng trình phân tích cú pháp, ban đầu phải được đặt thành sai." - Nói cách khác, bạn phải lặp đó cho mình và có rất nhiều logic tùy chỉnh: P
Timothy Khouri

1
Ủng hộ. Tốt hơn là nhấn mạnh độ phức tạp của thuật toán thay vì một số công nghệ.
Arnis Lapsa

1
Tự mình lặp đi lặp lại với nhiều logic tùy chỉnh không phải là một ý tưởng tuyệt vời. Sử dụng thư viện hỗ trợ thuật toán chuẩn nếu bạn có thể. ví dụ: search.cpan.org/~tobyink/HTML-HTML5-Parser-0.03/lib/HTML/HTML5/… / code.google.com/p/html5lib
Quentin

8
Vấn đề chính với trình phân tích cú pháp HTML là khi gặp lỗi, bạn không thể nói ra "Lỗi phân tích cú pháp" và để nguyên tại đó. Bạn vào chế độ kỳ quặc và cố gắng làm tốt nhất có thể từ mớ hỗn độn bạn gặp phải, bao gồm các thẻ không khớp, [{]} kiểu xen kẽ và tất cả các loại kỳ lạ, cố gắng làm cho kết quả trông tốt nhất có thể và không thể tránh khỏi thất bại ít đau đớn nhất ... đây không phải là điều bạn có thể làm với regexes.
SF.

7
@Timothy K: 'Lưu ý: Do cách thuật toán này khiến các phần tử thay đổi cha mẹ, nó được mệnh danh là "thuật toán của cơ quan nhận con nuôi" (trái ngược với các thuật toán có thể có khác để xử lý nội dung sai lệch, bao gồm "thuật toán loạn luân", "thuật toán vụ bí mật" và "thuật toán Heisenberg"). '
JXG

133

Vậy trình phân tích cú pháp HTML hoạt động như thế nào? Nó không sử dụng biểu thức chính quy để phân tích cú pháp?

Ồ không.

Nếu bạn quay trở lại bộ não của mình về một khóa học lý thuyết về tính toán, nếu bạn đã tham gia một khóa học hoặc khóa học về trình biên dịch, hoặc thứ gì đó tương tự, bạn có thể nhớ lại rằng có nhiều loại ngôn ngữ và mô hình tính toán khác nhau. Tôi không đủ điều kiện để đi vào tất cả các chi tiết, nhưng tôi có thể cùng bạn xem xét một vài điểm chính.

Loại ngôn ngữ & tính toán đơn giản nhất (cho những mục đích này) là ngôn ngữ thông thường. Chúng có thể được tạo bằng các biểu thức chính quy và được nhận dạng bằng các dữ liệu tự động hữu hạn. Về cơ bản, điều đó có nghĩa là các chuỗi "phân tích cú pháp" trong các ngôn ngữ này sử dụng trạng thái, nhưng không sử dụng bộ nhớ phụ. HTML chắc chắn không phải là một ngôn ngữ thông thường. Nếu bạn nghĩ về nó, danh sách các thẻ có thể được lồng sâu tùy ý. Ví dụ: bảng có thể chứa các bảng và mỗi bảng có thể chứa nhiều thẻ lồng nhau. Với biểu thức chính quy, bạn có thể chọn ra một cặp thẻ, nhưng chắc chắn không phải bất cứ thứ gì được lồng vào nhau một cách tùy tiện.

Một ngôn ngữ đơn giản cổ điển không thông thường được kết hợp chính xác trong dấu ngoặc đơn. Cố gắng hết sức có thể, bạn sẽ không bao giờ có thể tạo một biểu thức chính quy (hoặc một tự động hóa hữu hạn) luôn hoạt động. Bạn cần có bộ nhớ để theo dõi độ sâu của tổ.

Máy trạng thái có ngăn xếp bộ nhớ là điểm mạnh tiếp theo của mô hình tính toán. Đây được gọi là một tự động đẩy xuống và nó nhận dạng các ngôn ngữ được tạo bởi các ngữ pháp không có ngữ cảnh. Ở đây, chúng ta có thể nhận ra các dấu ngoặc được khớp chính xác - thực sự, một ngăn xếp là mô hình bộ nhớ hoàn hảo cho nó.

Chà, điều này có đủ tốt cho HTML không? Thật đáng buồn không. Có thể đối với XML siêu duper đã được xác thực cẩn thận, trong đó tất cả các thẻ luôn xếp hàng hoàn hảo. Trong HTML thế giới thực, bạn có thể dễ dàng tìm thấy các đoạn mã như <b><i>wow!</b></i>. Điều này rõ ràng là không lồng vào nhau, vì vậy để phân tích cú pháp chính xác, một ngăn xếp không đủ mạnh.

Cấp độ tính toán tiếp theo là các ngôn ngữ được tạo ra bởi các ngữ pháp chung và được các máy Turing nhận dạng. Điều này thường được chấp nhận là mô hình tính toán mạnh nhất có hiệu quả - một máy trạng thái, với bộ nhớ phụ, bộ nhớ của nó có thể được sửa đổi ở bất cứ đâu. Đây là những gì ngôn ngữ lập trình có thể làm. Đây là mức độ phức tạp của HTML.

Để tóm tắt mọi thứ ở đây trong một câu: để phân tích cú pháp HTML chung, bạn cần một ngôn ngữ lập trình thực sự, không phải một biểu thức chính quy.

HTML được phân tích cú pháp giống như cách các ngôn ngữ khác được phân tích cú pháp: lexing và phân tích cú pháp. Bước lexing chia nhỏ luồng ký tự riêng lẻ thành các mã thông báo có ý nghĩa. Bước phân tích cú pháp tập hợp các mã thông báo, sử dụng các trạng thái và bộ nhớ, thành một tài liệu mạch lạc về mặt logic có thể được thực hiện.


22

Biểu thức chính quy chỉ là một dạng phân tích cú pháp. Một trình phân tích cú pháp HTML trung thực đến tốt đẹp sẽ phức tạp hơn đáng kể so với việc có thể được diễn đạt bằng regex, sử dụng phương pháp rút gốc đệ quy , dự đoán và một số kỹ thuật khác để giải thích văn bản một cách chính xác. Nếu bạn thực sự muốn tham gia, bạn có thể xem lex & yacc và các công cụ tương tự.

Lệnh cấm sử dụng regexes để phân tích cú pháp HTML có lẽ nên được viết đúng hơn là: "Đừng sử dụng các cụm từ thông dụng ngây thơ để phân tích cú pháp HTML ..." (kẻo bạn cảm thấy tức giận) "... và xử lý kết quả một cách thận trọng." Đối với các mục tiêu cụ thể nhất định, regex có thể hoàn toàn phù hợp, nhưng bạn cần phải hết sức cẩn thận để nhận biết các hạn chế của regex và thận trọng khi phù hợp với nguồn văn bản bạn đang phân tích cú pháp (ví dụ: nếu nó đầu vào của người dùng, thực sự phải rất cẩn thận).


+1, một câu trả lời hay. Tôi phải thừa nhận rằng, trước đây tôi đã sử dụng regexes ngay cả khi tôi không kiểm soát HTML, nhưng không phải trong bất kỳ loại ứng dụng phát hành công khai nào. Tôi cũng "cảm thấy phẫn nộ", bởi vì điều đó thật ngây thơ. Nhưng đó là một thời gian dài trước đây :-)
Andy E

6

Phân tích cú pháp HTML là việc chuyển đổi một văn bản tuyến tính thành một cấu trúc cây. Biểu thức chính quy thường không thể xử lý cấu trúc cây. Biểu thức chính quy bạn cần tại mỗi thời điểm để nhận mã thông báo tiếp theo luôn thay đổi. Bạn có thể sử dụng biểu thức chính quy trong trình phân tích cú pháp, nhưng bạn sẽ cần một mảng toàn bộ các biểu thức chính quy cho mỗi trạng thái phân tích cú pháp có thể có.


2

Nếu bạn muốn có một giải pháp 100%: Bạn cần viết mã tùy chỉnh của riêng mình lặp đi lặp lại từng ký tự HTML và bạn cần có một lượng logic lớn để xác định xem bạn có nên dừng nút hiện tại và bắt đầu kế tiếp.

Lý do là đây là HTML hợp lệ:

<ul>
<li>One
<li>Two
<li>Three
</ul>

Nhưng đây là điều:

<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>

Nếu bạn đồng ý với "giải pháp 90%": Thì việc sử dụng trình phân tích cú pháp XML để tải tài liệu là tốt. Hoặc sử dụng Regex (mặc dù xml sẽ dễ dàng hơn nếu sau đó bạn là người nắm vững nội dung).


4
Trình phân tích cú pháp XML giống giải pháp 1% hơn. Số lượng tài liệu HTML được định dạng tốt XML là rất nhỏ.
Quentin

4
Có, họ không ... theo nghĩa đen "từng ký tự", vì bạn có thể cố gắng truyền tải mọi thứ. Nhưng quan điểm của tôi là bạn phải viết trình phân tích cú pháp của riêng bạn. Lập trình viên mới niên không được sử dụng để viết rằng loại mã ... chúng tôi đang sử dụng để "HtmlDocumentUtility.Load" và các công cụ như :) rằng
Timothy Khouri

4
@Andy E: Regexes không phải là ma thuật, chúng cũng hoạt động theo từng ký tự, giống như bất kỳ loại phân tích cú pháp nào khác, hoặc heck, bất kỳ hàm chuỗi nào khác.
Bart van Heukelom

1
BTW: Ví dụ đầu tiên của bạn không chỉ là "HTML bán hợp lệ". Nó thực sự hợp lệ HTML 4.01 Nghiêm ngặt. Bạn có thể sử dụng ví dụ: trình xác thực W3C để xác minh điều này. Thẻ đóng chính thức là tùy chọn cho <li> (xem thông số kỹ thuật HTML 4).
sleske

2
@Bart: điểm tốt, đôi khi não tôi quên hết logic và nghĩ rằng mọi thứ hoạt động theo phép thuật.
Andy E
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.