Mã HTML được phân tích cú pháp như thế nào?

Điều này phức tạp hơn một chút so với những gì bạn có thể mong đợi. Trước hết, bạn không muốn xử lý các trang HTML dưới dạng văn bản. Phân tích cú pháp văn bản HTML mà không dịch HTML trước thành cấu trúc dữ liệu chắc chắn sẽ nhạy cảm với các trang được tạo theo các cách khác nhau. Tuy nhiên, HTML được tìm thấy trong tự nhiên thường không hợp lệ. Tệ hơn nữa, trên thực tế, đôi khi nó là XHTML, trong khi các tài liệu tuyên bố là XHTML thường chứa các tính năng HTML, chẳng hạn như thiếu thẻ đóng, sử dụng chữ hoa, v.v.

Trình phân tích cú pháp SGML/SML của SWI-Prolog có thể tạo ra kết quả thường đủ tốt để cạo. Bộ tùy chọn tốt nhất phụ thuộc một chút vào bản chất của tài liệu. Các trang web hoặc mã hóa khác nhau đáng chú ý sử dụng XML nghiêm ngặt và phù hợp có thể yêu cầu các tùy chọn khác nhau. Dưới đây là một số mã để bắt đầu. Nó âm thầm bỏ qua tất cả các lỗi. Đây thường là những gì bạn muốn để cạo thực sự, nhưng việc xem các thông báo có thể hữu ích nếu bạn nhận được kết quả kỳ lạ

http_load_html(URL, DOM) :-
        setup_call_cleanup(http_open(URL, In,
                           [ timeout(60)
                           ]),
                           (   dtd(html, DTD),
                               load_structure(stream(In),
                                              DOM,
                                              [ dtd(DTD),
                                                dialect(sgml),
                                                shorttag(false),
                                                max_errors(-1),
                                                syntax_errors(quiet)
                                              ])
                           ),
                           close(In)).

Lưu ý rằng việc sử dụng setup_call_cleanup/3 đảm bảo rằng kết nối đã mở cuối cùng sẽ bị đóng. Tuy nhiên, nó không ngăn được các ngoại lệ và lỗi là một phần bình thường trong quá trình xử lý tài liệu web. Thông thường, trình quét sẽ sử dụng cấu trúc điều khiển như bên dưới để xử lý URL và chỉ cần bỏ qua nó nếu không thể truy xuất dữ liệu từ URL. Lưu ý rằng chúng tôi cung cấp mã này dưới dạng mã ví dụ thay vì thư viện vì nhu cầu đối với một trình cạp thực tế có thể thay đổi rất nhiều. e. g. , việc từ bỏ có thể là không chấp nhận được và nếu có lỗi, bạn có thể đợi và thử lại hoặc quyết định của bạn có thể phụ thuộc vào lỗi được trả về, v.v.

Để trùng với việc ra mắt chức năng viết lại HTML phát trực tuyến cho Công nhân Cloudflare, chúng tôi đang mở nguồn công cụ viết lại Rust HTML (LOL HTML) được sử dụng để sao lưu API Trình ghi lại HTML của Công nhân. Chúng tôi cũng nghĩ đã đến lúc xem lại lịch sử viết lại HTML tại Cloudflare

Bài đăng trên blog đầu tiên sẽ giải thích những điều cơ bản về trình ghi lại HTML phát trực tuyến và các yêu cầu cụ thể của chúng tôi. Chúng tôi bắt đầu khoảng 8 năm trước bằng cách mô tả nhóm trình phân tích cú pháp 'đặc biệt' được tạo với chức năng cụ thể như viết lại địa chỉ e-mail hoặc thu nhỏ HTML. Đến năm 2016, máy trạng thái được xác định trong đặc tả HTML5 có thể được sử dụng để xây dựng một trình ghi lại có thể cắm HTML tuân thủ thông số kỹ thuật duy nhất, để thay thế bộ sưu tập trình phân tích cú pháp hiện có. Mã nguồn cho trình ghi lại này hiện đã được công khai và có sẵn tại đây. https. //github. com/cloudflare/lazyhtml

Bài đăng trên blog thứ hai sẽ mô tả lần lặp lại tiếp theo của trình viết lại. Với sự ra mắt của nền tảng điện toán biên Cloudflare Worker, chúng tôi nhận ra rằng các nhà phát triển muốn có khả năng viết lại HTML tương tự với API JavaScript. Bài đăng mô tả những suy nghĩ đằng sau trình ghi lại HTML phát trực tuyến có độ trễ thấp với API dựa trên bộ chọn CSS. Chúng tôi đã mở nguồn thư viện Rust vì nó cũng có thể được sử dụng như một thư viện viết lại/phân tích cú pháp HTML độc lập

Trình viết lại HTML phát trực tuyến là gì?

Trình ghi lại HTML phát trực tuyến lấy đầu vào chuỗi HTML hoặc luồng byte, phân tích cú pháp đó thành mã thông báo hoặc bất kỳ biểu diễn trung gian có cấu trúc (IR) nào khác - chẳng hạn như Cây cú pháp trừu tượng (AST). Sau đó, nó thực hiện các chuyển đổi trên mã thông báo trước khi chuyển đổi trở lại HTML. Điều này cung cấp khả năng sửa đổi, trích xuất hoặc thêm vào tài liệu HTML hiện có khi các byte đang được xử lý. So sánh điều này với trình phân tích cú pháp cây HTML tiêu chuẩn cần truy xuất toàn bộ tệp để tạo cây DOM đầy đủ. Trình ghi lại dựa trên cây sẽ mất nhiều thời gian hơn để phân phối các byte được xử lý đầu tiên và yêu cầu nhiều bộ nhớ hơn

Mã HTML được phân tích cú pháp như thế nào?
trình viết lại HTML

Ví dụ; . Bạn sẽ nhanh chóng gặp vấn đề về tài nguyên (hình ảnh, tập lệnh, video) được cung cấp qua HTTP. 'Nội dung hỗn hợp' này mở ra một lỗ hổng bảo mật và các trình duyệt sẽ cảnh báo hoặc chặn các tài nguyên này. Có thể khó hoặc thậm chí không thể cập nhật mọi liên kết trên mọi trang của trang web. Với trình ghi lại HTML phát trực tuyến, bạn có thể chọn thuộc tính URI của bất kỳ thẻ HTML nào và thay đổi bất kỳ liên kết HTTP nào thành HTTPS. Chúng tôi đã xây dựng tính năng viết lại HTTPS tự động này vào năm 2016 để giải quyết các vấn đề về nội dung hỗn hợp cho khách hàng của chúng tôi

Người đọc có thể đã tự hỏi. “Đây không phải là một vấn đề đã được giải quyết sao, không có nhiều trình duyệt nguồn mở được sử dụng rộng rãi với trình phân tích cú pháp HTML có thể được sử dụng cho mục đích này sao?”. Thực tế là việc viết mã để chạy trong hơn 190 PoP trên toàn thế giới với yêu cầu nghiêm ngặt về độ trễ thấp đã biến những vấn đề tưởng chừng như tầm thường thành những thách thức kỹ thuật phức tạp

Các bài đăng trên blog sau đây sẽ trình bày chi tiết hành trình bắt đầu bằng một ý tưởng đơn giản là tìm địa chỉ email trong trang HTML dẫn đến việc xây dựng trình phân tích cú pháp HTML gần như tuân thủ thông số kỹ thuật và sau đó là bộ chọn CSS phù hợp với Máy ảo. Chúng tôi đã học được rất nhiều trên hành trình này. Tôi hy vọng bạn tìm thấy một số điều này là thú vị như chúng tôi đã làm

Viết lại ở rìa

Khi viết lại nội dung thông qua Cloudflare, chúng tôi không muốn ảnh hưởng đến hiệu suất trang web. Sự cân bằng trong việc thiết kế trình ghi lại HTML trực tuyến là để giảm thiểu việc tạm dừng trong luồng byte phản hồi bằng cách giữ càng ít thông tin càng tốt trong khi vẫn giữ được khả năng ghi lại các mã thông báo phù hợp

Sự khác biệt về yêu cầu so với trình phân tích cú pháp HTML được sử dụng trong trình duyệt bao gồm

Độ trễ đầu ra

Đối với các trình duyệt, Mô hình đối tượng tài liệu (DOM) là sản phẩm cuối cùng của quá trình phân tích cú pháp nhưng trong trường hợp của chúng tôi, chúng tôi phải phân tích cú pháp, viết lại và tuần tự hóa trở lại HTML. Trong trường hợp proxy ngược của Cloudflare, bất kỳ quá trình xử lý nội dung nào trên máy chủ biên đều dẫn đến độ trễ giữa máy chủ và nhãn cầu. Mong muốn giảm thiểu tác động độ trễ của việc xử lý HTML, bao gồm phân tích cú pháp, viết lại và tuần tự hóa trở lại HTML. Trong tất cả các giai đoạn này, chúng tôi muốn nhanh nhất có thể để giảm thiểu độ trễ

Thông lượng trình phân tích cú pháp

Giả sử rằng thông thường các trình duyệt hiếm khi cần xử lý các trang HTML có kích thước lớn hơn 1Mb và thời gian tải trang trung bình tốt nhất là khoảng 3 giây. Phân tích cú pháp HTML không phải là nút cổ chai chính của quá trình tải trang vì trình duyệt sẽ bị chặn khi chạy tập lệnh và tải các tài nguyên quan trọng khác. Chúng tôi có thể ước tính sơ bộ rằng ~3Mb/giây là thông lượng có thể chấp nhận được đối với trình phân tích cú pháp HTML của trình duyệt. Tại Cloudflare, chúng tôi có hàng trăm megabyte lưu lượng truy cập trên mỗi CPU, vì vậy chúng tôi cần một trình phân tích cú pháp nhanh hơn theo thứ tự cường độ

Giới hạn bộ nhớ

Như hầu hết người dùng phải nhận ra, các trình duyệt có khả năng tiêu thụ bộ nhớ. Ví dụ: đánh dấu HTML đơn giản này khi được mở trong trình duyệt sẽ tiêu tốn một lượng đáng kể bộ nhớ hệ thống của bạn trước khi tạm dừng tab trình duyệt (và tất cả bộ nhớ này sẽ được trình phân tích cú pháp sử dụng)


Thật không may, bộ đệm của một số phần đầu vào là không thể tránh khỏi ngay cả khi viết lại HTML trực tuyến. Hãy xem xét 2 đoạn mã HTML này


These seemingly similar fragments of HTML will be treated completely differently when encountered at the end of an HTML page. The first fragment will be parsed as a start tag and the second one will be ignored. By just seeing a `<` character followed by a tag name, the parser can’t determine if it has found a start tag or not. It needs to traverse the input in the search of the closing `>` to make a decision, buffering all content in between, so it can later be emitted to the consumer as a start tag token.

Yêu cầu này buộc các trình duyệt phải đệm nội dung vô thời hạn trước khi từ bỏ lỗi hết bộ nhớ

Trong trường hợp của chúng tôi, chúng tôi không đủ khả năng dành hàng trăm MB bộ nhớ để phân tích cú pháp một tệp HTML (các ràng buộc thực tế thậm chí còn chặt chẽ hơn - thậm chí sử dụng hàng chục kilobyte cho mỗi yêu cầu sẽ không được chấp nhận). Chúng ta cần tinh vi hơn nhiều so với các triển khai khác về mức sử dụng bộ nhớ và xử lý khéo léo tất cả các tình huống trong đó dung lượng bộ nhớ được cung cấp không đủ để thực hiện phân tích cú pháp

v0. “Trình phân tích cú pháp đặc biệt”

Như thường lệ với các dự án lớn, tất cả bắt đầu khá ngây thơ

Tìm và xáo trộn email

Vào năm 2010, Cloudflare đã quyết định cung cấp một tính năng ngăn chặn các trình quét email phổ biến. Ý tưởng cơ bản của tính năng bảo vệ này là tìm và làm xáo trộn các email trên các trang và sau đó giải mã chúng trở lại trong trình duyệt bằng mã JavaScript được đưa vào. Nghe có vẻ dễ dàng, phải không?

Tuy nhiên, ngay cả một nhiệm vụ có vẻ đơn giản như vậy cũng đã yêu cầu giải quyết một số vấn đề. Trước hết, chúng ta cần định nghĩa email là gì và không có câu trả lời đơn giản. Trên thực tế, ngay cả biểu thức chính quy khét tiếng được cho là bao trùm toàn bộ RFC cũng đã lỗi thời và không đầy đủ vì RFC mới đã bổ sung nhiều cấu trúc email hợp lệ, bao gồm cả hỗ trợ Unicode. Bây giờ chúng ta đừng đi xuống hố thỏ đó và thay vào đó hãy tập trung vào một vấn đề cấp cao hơn. chuyển đổi nội dung phát trực tuyến

Nội dung từ mạng có dạng gói, các gói này phải được lưu vào bộ đệm và phân tích cú pháp dưới dạng HTTP bởi máy chủ của chúng tôi. Bạn không thể dự đoán nội dung sẽ được phân chia như thế nào, điều đó có nghĩa là bạn luôn cần đệm một số nội dung vì nội dung sắp được thay thế có thể xuất hiện trong nhiều khối đầu vào

Giả sử chúng ta quyết định sử dụng một biểu thức chính quy đơn giản như `[\w. ][email được bảo vệ][\w. ]+`. Nếu nội dung đi qua có chứa email “ [email được bảo vệ] ”, thì nội dung đó có thể được chia thành các phần sau.

Mã HTML được phân tích cú pháp như thế nào?

Để duy trì Thời gian đến Byte đầu tiên (TTFB) tốt và tốc độ ổn định, chúng tôi muốn đảm bảo rằng đoạn trước đó được phát ra ngay khi chúng tôi xác định rằng nó không thú vị cho mục đích thay thế

Cách dễ nhất để làm điều đó là biến biểu thức chính quy của chúng ta thành một máy trạng thái hoặc một máy tự động hữu hạn. Mặc dù bạn có thể làm điều đó bằng tay, nhưng cuối cùng bạn sẽ gặp phải mã khó bảo trì và dễ bị lỗi. Thay vào đó, Ragel được chọn để chuyển đổi các biểu thức chính quy thành mã máy trạng thái gốc hiệu quả. Ragel không cố gắng xử lý bộ đệm hoặc bất kỳ thứ gì khác ngoài việc duyệt qua máy trạng thái. Nó cung cấp một cú pháp không chỉ mô tả các mẫu mà còn có thể liên kết các hành động tùy chỉnh (mã bằng ngôn ngữ máy chủ) với bất kỳ trạng thái cụ thể nào

Trong trường hợp của chúng tôi, chúng tôi có thể chuyển qua bộ đệm cho đến khi khớp với phần đầu của email. Nếu sau đó chúng tôi phát hiện ra mẫu không phải là email, chúng tôi có thể thoát khỏi bộ đệm ngay khi mẫu ngừng khớp. Nếu không, chúng tôi có thể truy xuất email phù hợp và thay thế bằng nội dung mới

Để biến mẫu của chúng ta thành trình phân tích cú pháp phát trực tuyến, chúng ta có thể nhớ vị trí bắt đầu tiềm năng của email và, trừ khi nó đã bị loại bỏ hoặc thay thế bằng phần cuối của đầu vào hiện tại, hãy lưu trữ phần chưa được xử lý trong bộ đệm vĩnh viễn. Sau đó, khi một đoạn mới xuất hiện, chúng tôi có thể xử lý nó một cách riêng biệt, tiếp tục từ trạng thái mà Ragel tự ghi nhớ, nhưng sau đó sử dụng cả đoạn được đệm và một đoạn mới để phát ra hoặc làm xáo trộn

Bây giờ chúng ta đã giải quyết vấn đề khớp các mẫu email trong văn bản, chúng ta cần xử lý thực tế là chúng cần được làm xáo trộn trên các trang. Đây là lúc những gợi ý đầu tiên về “phân tích cú pháp” HTML được giới thiệu

Tôi đã đặt "phân tích cú pháp" trong dấu ngoặc kép bởi vì, thay vì triển khai toàn bộ trình phân tích cú pháp, bộ lọc email (như tên gọi của mô-đun) không cố gắng sao chép toàn bộ ngữ pháp HTML, mà thay vào đó đã thêm các mẫu Ragel tùy chỉnh chỉ để bỏ qua các nhận xét

Đây là một cách tiếp cận hợp lý, đặc biệt là vào năm 2010 - bốn năm trước khi có đặc tả HTML5, khi tất cả các trình duyệt đều có cách xử lý HTML riêng. Tuy nhiên, như bạn có thể tưởng tượng, cách tiếp cận này không mở rộng tốt. Nếu bạn đang cố gắng giải quyết những điều kỳ quặc trong các trình phân tích cú pháp khác, bạn sẽ bắt đầu thu được ngày càng nhiều điều kỳ quặc của riêng mình, và sau đó cũng giải quyết những điều kỳ quặc đó. Đồng thời, các tính năng mới bắt đầu được thêm vào, cũng yêu cầu sửa đổi HTML một cách nhanh chóng (như tự động chèn tập lệnh Google Analytics) và một mô-đun hiện có dường như là nơi tốt nhất cho điều đó. Nó đã phát triển để xử lý ngày càng nhiều thẻ, hoạt động và trường hợp cạnh cú pháp

Bây giờ hãy thu nhỏ

Vào năm 2011, Cloudflare cũng đã quyết định bổ sung tính năng thu nhỏ để cho phép khách hàng tăng tốc trang web của họ ngay cả khi họ không tự sử dụng tính năng thu nhỏ. Để làm được điều đó, chúng tôi đã quyết định sử dụng công cụ thu nhỏ phát trực tuyến hiện có - jitify. Nó đã có các ràng buộc NGINX, khiến nó trở thành một ứng cử viên tuyệt vời để tích hợp vào đường ống hiện có

Thật không may, giống như hầu hết các trình phân tích cú pháp khác vào thời điểm đó cũng như trình phân tích cú pháp của chúng tôi được mô tả ở trên, nó có các quy tắc xử lý riêng cho HTML, JavaScript và CSS, không chính xác mà cố gắng phân tích nội dung trên cơ sở nỗ lực nhất. Điều này dẫn đến việc chúng tôi có hai trình phân tích cú pháp phát trực tuyến độc lập không tương thích và có thể tạo ra lỗi riêng lẻ hoặc chỉ khi kết hợp

v1. "(Hầu hết) Trình phân tích cú pháp tuân thủ HTML5 Spec"

Qua nhiều năm, các kỹ sư tiếp tục bổ sung các tính năng mới cho các máy trạng thái ngày càng phát triển, đồng thời sửa các lỗi mới phát sinh từ việc triển khai cú pháp không chính xác, xung đột giữa các trình phân tích cú pháp khác nhau và các sự cố trong chính các tính năng

Đến năm 2016, đã đến lúc thoát khỏi việc kinh doanh nhiều trình phân tích cú pháp đặc biệt và thực hiện mọi thứ 'đúng cách'

(Các) phần tiếp theo sẽ mô tả cách chúng tôi xây dựng trình phân tích cú pháp tuân thủ HTML5 của mình bắt đầu từ máy trạng thái đặc tả. Chỉ sử dụng máy trạng thái này, lẽ ra phải dễ dàng xây dựng trình phân tích cú pháp. Bạn có thể biết rằng trong lịch sử, việc phân tích cú pháp HTML không hoàn toàn nghiêm ngặt, điều đó có nghĩa là để không phá vỡ các triển khai hiện có, việc xây dựng một DOM thực tế là bắt buộc để phân tích cú pháp. Điều này là không thể đối với trình ghi lại phát trực tuyến, vì vậy một trình mô phỏng phản hồi của trình phân tích cú pháp đã được phát triển. Về hiệu suất, tốt hơn là không làm điều gì đó. Sau đó, chúng tôi mô tả lý do tại sao trình viết lại có thể 'lười biếng' và không thực hiện mã hóa và giải mã văn bản tốn kém khi viết lại HTML. Vấn đề khó khăn đáng ngạc nhiên khi quyết định xem phản hồi có phải là HTML hay không sau đó được trình bày chi tiết

HTML5

Đến năm 2016, HTML5 đã xác định các quy tắc cú pháp chính xác để phân tích cú pháp và khả năng tương thích với nội dung cũ và triển khai trình duyệt tùy chỉnh. Nó đã được triển khai bởi tất cả các trình duyệt và nhiều triển khai của bên thứ 3

Đặc tả phân tích cú pháp HTML5 xác định cú pháp HTML cơ bản ở dạng máy trạng thái. Chúng tôi đã có kinh nghiệm với Ragel cho các trường hợp sử dụng tương tự, vì vậy không có câu hỏi nào về việc sử dụng cái gì cho trình phân tích cú pháp phát trực tuyến mới. Bất chấp sự phức tạp của ngữ pháp, việc dịch đặc tả sang cú pháp Ragel rất đơn giản. Mã trông đơn giản hơn so với mô tả chính thức của máy trạng thái, nhờ khả năng kết hợp cú pháp regex với các chuyển đổi rõ ràng

Mã HTML được phân tích cú pháp như thế nào?
Trực quan hóa một phần nhỏ của máy trạng thái HTML. Nguồn. https. //twitter. com/RReverser/status/715937136520916992

Phân tích cú pháp HTML5 yêu cầu 'DOM'

Tuy nhiên, HTML có một lịch sử. Để không phá vỡ các triển khai hiện có, HTML5 được chỉ định với các quy trình khôi phục đối với việc lồng thẻ, sắp xếp thứ tự, thẻ không được đóng không chính xác, thuộc tính bị thiếu và tất cả các lỗi có thể khác đã từng hoạt động trong các trình duyệt cũ hơn. Để giải quyết những vấn đề này, thông số kỹ thuật yêu cầu trình tạo cây điều khiển từ vựng, về cơ bản có nghĩa là bạn không thể mã hóa chính xác HTML (chia thành các thẻ riêng biệt) mà không có DOM

Mã HTML được phân tích cú pháp như thế nào?
Luồng phân tích cú pháp HTML như được xác định bởi thông số kỹ thuật

Vì lý do này, hầu hết các trình phân tích cú pháp thậm chí không cố gắng thực hiện phân tích cú pháp phát trực tuyến và thay vào đó lấy toàn bộ đầu vào và tạo ra một cây tài liệu làm đầu ra. Đây không phải là điều chúng tôi có thể làm để chuyển đổi phát trực tuyến mà không gây thêm độ trễ đáng kể khi tải trang

Trình phân tích cú pháp JavaScript HTML5 hiện tại - parse5 - đã triển khai phân tích cú pháp cây tuân thủ thông số kỹ thuật bằng cách sử dụng trình mã thông báo phát trực tuyến và trình ghi lại. Để tránh phải tạo một DOM đầy đủ, khái niệm “trình mô phỏng phản hồi trình phân tích cú pháp” đã được giới thiệu

Phản hồi của người xây dựng cây

Như bạn có thể đoán từ cái tên, đây là một mô-đun nhằm mục đích mô phỏng phản hồi của trình phân tích cú pháp đầy đủ cho trình mã thông báo, mà không thực sự xây dựng toàn bộ DOM, nhưng thay vào đó chỉ lưu giữ thông tin và ngữ cảnh cần thiết để điều khiển chính xác máy trạng thái

Sau khi kiểm tra nghiêm ngặt và ngược dòng một trình chạy thử nghiệm để phân tích cú pháp5, chúng tôi nhận thấy kỹ thuật này phù hợp với phần lớn các trang được viết thậm chí kém trên Internet và đã sử dụng nó trong LazyHTML

Mã HTML được phân tích cú pháp như thế nào?
Kiến trúc HTML lười biếng

Tránh giải mã - mọi thứ đều là ASCII

Giờ đây, chúng tôi đã có một trình mã thông báo phát trực tuyến đang hoạt động, chúng tôi muốn đảm bảo rằng nó đủ nhanh để người dùng không nhận thấy bất kỳ sự chậm lại nào đối với các trang của họ khi họ đi qua trình phân tích cú pháp và chuyển đổi. Nếu không, nó sẽ phá vỡ hoàn toàn mọi tối ưu hóa mà chúng tôi muốn thử ngay lập tức

Nó không chỉ gây ra ảnh hưởng đến hiệu suất do giải mã và mã hóa lại bất kỳ nội dung HTML đã sửa đổi nào, mà còn làm phức tạp đáng kể việc triển khai của chúng tôi do cần có nhiều nguồn thông tin mã hóa tiềm năng để xác định mã hóa ký tự, bao gồm cả việc đánh hơi 1 KB đầu tiên của

Đặc tả Tiêu chuẩn HTML "sống" chỉ cho phép mã hóa được xác định trong Tiêu chuẩn mã hóa. Nếu chúng tôi xem xét kỹ các mã hóa đó, cũng như nhận xét về phần Mã hóa ký tự của thông số HTML, chúng tôi thấy rằng tất cả chúng đều tương thích với ASCII, ngoại trừ UTF-16 và ISO-2022-JP

Điều này có nghĩa là bất kỳ văn bản ASCII nào sẽ được biểu diễn bằng các bảng mã như vậy chính xác như trong ASCII và mọi văn bản không phải ASCII sẽ được biểu thị bằng các byte bên ngoài phạm vi ASCII. Thuộc tính này cho phép chúng tôi mã hóa một cách an toàn, so sánh và thậm chí sửa đổi HTML gốc mà không cần giải mã hoặc thậm chí không biết nó chứa mã hóa cụ thể nào. Có thể vì tất cả các ranh giới mã thông báo trong ngữ pháp HTML được biểu thị bằng ký tự ASCII

Chúng tôi cần phát hiện UTF-16 bằng cách đánh hơi và giải mã hoặc bỏ qua các tài liệu đó mà không sửa đổi. Chúng tôi đã chọn cách thứ hai để tránh các lỗi tiềm ẩn nhạy cảm về bảo mật thường gặp với UTF-16 và vì mã hóa ký tự được nhìn thấy trong vòng chưa đầy 0. May mắn thay, 1% mã hóa ký tự đã biết

Vấn đề duy nhất còn lại với phương pháp này là ở hầu hết các vị trí, đặc tả mã thông báo HTML yêu cầu bạn thay thế các ký tự U+0000 (NUL) bằng U+FFFD (ký tự thay thế) trong quá trình phân tích cú pháp. Có lẽ, điều này đã được thêm vào như một biện pháp phòng ngừa bảo mật chống lại các lỗi trong quá trình triển khai C của các công cụ cũ có thể xử lý ký tự NUL, được mã hóa bằng ASCII/UTF-8/. dưới dạng byte 0x00, ở cuối chuỗi (yay, chuỗi kết thúc null…). Đó là vấn đề đối với chúng tôi vì U+FFFD nằm ngoài phạm vi ASCII và sẽ được biểu thị bằng các chuỗi byte khác nhau trong các bảng mã khác nhau. Chúng tôi không biết mã hóa của tài liệu, vì vậy điều này sẽ dẫn đến hỏng đầu ra

May mắn thay, chúng tôi không kinh doanh cùng ngành với các nhà cung cấp trình duyệt và đừng lo lắng nhiều về các ký tự NUL trong chuỗi - chúng tôi sử dụng biểu diễn chuỗi “con trỏ béo”, trong đó độ dài của chuỗi không được xác định bởi vị trí của

typedef struct {
   const char *data;
   size_t length;
} lhtml_string_t;

Thay vào đó, chúng ta có thể lặng lẽ bỏ qua những phần này của thông số kỹ thuật (xin lỗi. ) và giữ nguyên trạng các ký tự U+0000 và thêm chúng như vậy vào thẻ, tên thuộc tính và các chuỗi khác, sau đó phát lại vào tài liệu. Điều này là an toàn để thực hiện, vì nó không ảnh hưởng đến bất kỳ quá trình chuyển đổi máy trạng thái nào, mà chỉ bảo toàn các byte 0x00 ban đầu và ủy quyền thay thế chúng cho trình phân tích cú pháp trong trình duyệt của người dùng cuối

Loại nội dung điên rồ

Chúng tôi muốn lười biếng và giảm thiểu các thông tin sai lệch. Chúng tôi chỉ muốn dành thời gian phân tích cú pháp, giải mã và viết lại HTML thực hơn là phá vỡ hình ảnh hoặc JSON. Vì vậy, câu hỏi đặt ra là làm cách nào để bạn quyết định xem nội dung nào đó có phải là tài liệu HTML hay không. Bạn có thể sử dụng Loại nội dung chẳng hạn không?

/*
Dear future generations. I didn't like this hack either and hoped
we could do the right thing instead. Unfortunately, the Internet
was a bad and scary place at the moment of writing. If this
ever changes and websites become more standards compliant,
please do remove it just like I tried.
Many websites use PHP which sets Content-Type: text/html by
default. There is no error or warning if you don't provide own
one, so most websites don't bother to change it and serve
JSON API responses, private keys and binary data like images
with this default Content-Type, which we would happily try to
parse and transforms. This not only hurts performance, but also
easily breaks response data itself whenever some sequence inside
it happens to look like a valid HTML tag that we are interested
in. It gets even worse when JSON contains valid HTML inside of it
and we treat it as such, and append random scripts to the end
breaking APIs critical for popular web apps.
This hack attempts to mitigate the risk by ensuring that the
first significant character (ignoring whitespaces and BOM)
is actually `<` - which increases the chances that it's indeed HTML.
That way we can potentially skip some responses that otherwise
could be rendered by a browser as part of AJAX response, but this
is still better than the opposite situation.
*/

Người đọc có thể nghĩ rằng đó là một trường hợp hiếm gặp, tuy nhiên, các quan sát của chúng tôi cho thấy rằng gần 25% lưu lượng truy cập được cung cấp qua Cloudflare với loại nội dung “văn bản/html” có thể không phải là HTML

Mã HTML được phân tích cú pháp như thế nào?

Rắc rối không kết thúc ở đó. hóa ra là có một lượng đáng kể nội dung XML được cung cấp với loại nội dung “văn bản/html” không phải lúc nào cũng được xử lý chính xác khi được xử lý dưới dạng HTML

Các gói cứu trợ theo thời gian cho dữ liệu nhị phân, JSON, AMP và xác định chính xác các đoạn HTML dẫn đến logic đánh hơi nội dung có thể được mô tả bằng sơ đồ sau

Mã HTML được phân tích cú pháp như thế nào?

Đây là một ví dụ điển hình về sự khác biệt giữa thông số kỹ thuật chính thức và thực tế

Tối ưu hóa so sánh tên thẻ

Nhưng chỉ phân tích cú pháp nhanh thôi là chưa đủ - chúng tôi có chức năng tiêu thụ đầu ra của trình phân tích cú pháp, viết lại và cung cấp lại cho quá trình tuần tự hóa. Và tất cả các ràng buộc về bộ nhớ và thời gian mà chúng tôi có đối với trình phân tích cú pháp cũng có thể áp dụng cho mã này, vì nó là một phần của cùng một quy trình xử lý nội dung

Đó là một yêu cầu phổ biến để so sánh các tên thẻ HTML được phân tích cú pháp, e. g. để xác định xem thẻ hiện tại có nên được viết lại hay không. Việc triển khai ngây thơ sẽ sử dụng phép so sánh theo từng byte thông thường, có thể yêu cầu duyệt qua toàn bộ tên thẻ. Chúng tôi có thể thu hẹp thao tác này thành một hướng dẫn so sánh số nguyên duy nhất trong phần lớn các trường hợp bằng cách sử dụng thuật toán băm được thiết kế đặc biệt

The tag names of all standard HTML elements contain only alphabetical ASCII characters and digits from 1 to 6 (in numbered header tags, i.e.

-

). Comparison of tag names is case-insensitive, so we only need 26 characters to represent alphabetical characters. Using the same basic idea as arithmetic coding, we can represent each of the possible 32 characters of a tag name using just 5 bits and, thus, fit up to floor(64 / 5) = 12 characters in a 64-bit integer which is enough for all the standard tag names and any other tag names that satisfy the same requirements! The great part is that we don’t even need to additionally traverse a tag name to hash it - we can do that as we parse the tag name consuming the input byte by byte.

Tuy nhiên, có một vấn đề với thuật toán băm này và thủ phạm không quá rõ ràng. để phù hợp với tất cả 32 ký tự trong 5 bit, chúng tôi cần sử dụng tất cả các kết hợp bit có thể có bao gồm 00000. Điều này có nghĩa là nếu ký tự đầu của tên thẻ được biểu thị bằng 00000 thì chúng tôi sẽ không thể phân biệt giữa số lần lặp lại do đó khác nhau của ký tự này

Ví dụ: xem xét rằng 'a' được mã hóa thành 00000 và 'b' là 00001

Tên thẻ Biểu diễn bit Giá trị được mã hóaab00000 000011aab00000 00000 000011

May mắn thay, chúng tôi biết rằng ngữ pháp HTML không cho phép ký tự đầu tiên của tên thẻ là bất kỳ ký tự nào ngoại trừ ký tự bảng chữ cái ASCII, do đó, đặt các số từ 0 đến 5 (00000b-00101b) cho các chữ số và số từ 6 đến 31 (00110b -

LườiHTML

Sau khi xem xét mọi thứ được đề cập ở trên, LazyHTML (https. //github. thư viện com/cloudflare/lazyhtml) đã được tạo. Nó là trình phân tích cú pháp và trình tuần tự hóa HTML phát trực tuyến nhanh với C-API dựa trên mã thông báo có nguồn gốc từ trình từ vựng HTML5 được viết bằng Ragel. Nó cung cấp một đường ống chuyển đổi có thể cắm được để cho phép nhiều trình xử lý chuyển đổi được kết nối với nhau

Một ví dụ về hàm biến đổi thuộc tính `href` của các liên kết

// define static string to be used for replacements
static const lhtml_string_t REPLACEMENT = {
   .data = "[REPLACED]",
   .length = sizeof("[REPLACED]") - 1
};

static void token_handler(lhtml_token_t *token, void *extra /* this can be your state */) {
  if (token->type == LHTML_TOKEN_START_TAG) { // we're interested only in start tags
    const lhtml_token_starttag_t *tag = &token->start_tag;
    if (tag->type == LHTML_TAG_A) { // check whether tag is of type 
      const size_t n_attrs = tag->attributes.count;
      const lhtml_attribute_t *attrs = tag->attributes.items;
      for (size_t i = 0; i < n_attrs; i++) { // iterate over attributes
        const lhtml_attribute_t *attr = &attrs[i];
        if (lhtml_name_equals(attr->name, "href")) { // match the attribute name
          attr->value = REPLACEMENT; // set the attribute value
        }
      }
    }
  }
  lhtml_emit(token, extra); // pass transformed token(s) to next handler(s)
}

Vì vậy, nó là chính xác và nó nhanh như thế nào?

Nó tuân thủ HTML5 khi được thử nghiệm với các bộ thử nghiệm chính thức. Là một phần của công việc, một số đóng góp đã được gửi đến chính đặc điểm kỹ thuật để làm rõ/đơn giản hóa ngôn ngữ đặc tả

Không giống như (các) trình phân tích cú pháp trước đó, nó không cứu được bất kỳ tài liệu nào trong số 2.382.625 tài liệu từ Lưu trữ HTTP, mặc dù 0. 2% tài liệu vượt quá giới hạn bộ đệm dự kiến ​​vì trên thực tế chúng là JavaScript hoặc RSS hoặc các loại nội dung khác được phân phát không chính xác với Loại nội dung. văn bản/html và vì mọi thứ đều là HTML5 hợp lệ, trình phân tích cú pháp đã cố phân tích cú pháp e. g. a

As for the benchmarks, In September 2016 using an example which transforms the HTML spec itself (7.9 MB HTML file) by replacing every (only that property only in those tags) to a static value. It was compared against the few existing and popular HTML parsers (only tokenization mode was used for the fair comparison, so that they don't need to build AST and so on), and timings in milliseconds for 100 iterations are the following (lazy mode means that we're using raw strings whenever possible, the other one serializes each token just for comparison):

Mã HTML được phân tích cú pháp như thế nào?

Kết quả cho thấy rằng tốc độ của trình phân tích cú pháp LazyHTML nhanh hơn khoảng một bậc

Điều đó kết thúc bài đăng đầu tiên trong loạt bài của chúng tôi về trình viết lại HTML tại Cloudflare. Bài đăng tiếp theo mô tả cách chúng tôi xây dựng một trình ghi lại phát trực tuyến mới dựa trên các ý tưởng của LazyHTML. Bản cập nhật chính là cung cấp API bộ chọn CSS dễ sử dụng hơn. Nó cung cấp back-end cho API JavaScript HTMLRewriter của công nhân Cloudflare

Phân tích cú pháp HTML giải thích với ví dụ là gì?

TỔNG QUAN VỀ MÔ HÌNH PHÂN TÍCH

Mã được phân tích cú pháp như thế nào?

Phân tích cú pháp, trong khoa học máy tính, là nơi một chuỗi lệnh – thường là một chương trình – được tách thành các thành phần dễ xử lý hơn, được phân tích để tìm đúng cú pháp và sau đó được gắn vào . Sau đó, máy tính có thể xử lý từng đoạn chương trình và chuyển đổi nó thành ngôn ngữ máy. . The computer can then process each program chunk and transform it into machine language.

Chúng ta có thể phân tích cú pháp HTML không?

HTML là ngôn ngữ đánh dấu có cấu trúc đơn giản. Sẽ khá dễ dàng để xây dựng trình phân tích cú pháp cho HTML bằng trình tạo trình phân tích cú pháp . Trên thực tế, bạn thậm chí có thể không cần phải làm điều đó, nếu bạn chọn trình tạo trình phân tích cú pháp phổ biến, như ANTLR. Đó là bởi vì đã có sẵn ngữ pháp sẵn sàng để sử dụng.

Cái gì phân tích hoặc đọc HTML?

Phân tích cú pháp có nghĩa là đọc mã nguồn HTML . Nó được đọc từng dòng (từng thẻ). Hầu hết các trình duyệt xây dựng DOM và hiển thị trang (bố cục và văn bản cơ bản) trong khi phân tích cú pháp tài liệu.