Sử dụng các biểu thức chính quy để phân tích HTML: tại sao không?


207

Có vẻ như mọi câu hỏi trên stackoverflow nơi người hỏi đang sử dụng regex để lấy một số thông tin từ HTML chắc chắn sẽ có một "câu trả lời" nói rằng không sử dụng regex để phân tích HTML.

Tại sao không? Tôi biết rằng có những trình phân tích cú pháp HTML "thực sự" không có trích dẫn ngoài đó như Beautiful Soup , và tôi chắc chắn rằng chúng mạnh mẽ và hữu ích, nhưng nếu bạn chỉ làm một việc đơn giản, nhanh chóng hoặc bẩn thỉu thì tại sao bận tâm sử dụng một cái gì đó quá phức tạp khi một vài câu lệnh regex sẽ hoạt động tốt?

Hơn nữa, có điều gì cơ bản mà tôi không hiểu về regex khiến chúng trở thành một lựa chọn tồi cho việc phân tích cú pháp nói chung?


3
tôi nghĩ rằng đây là bản sao của stackoverflow.com/questions/133601
jcrossley3

23
Bởi vì chỉ Chuck Norris mới có thể phân tích HTML bằng regex (như đã giải thích trong điều Zalgo nổi tiếng này: stackoverflow.com/questions/1732348/ Lỗi ).
takeshin

1
Câu hỏi này khiến tôi phải hỏi một câu hỏi khác có liên quan. Trong trường hợp bạn quan tâm: Tại sao không thể sử dụng regex để phân tích HTML / XML: một lời giải thích chính thức theo thuật ngữ của giáo dân
mac


Câu hỏi này đã được thêm vào Câu hỏi thường gặp về Biểu thức thường xuyên chồng chéo , trong phần "Nhiệm vụ xác thực chung".
aliteralmind

Câu trả lời:


212

Toàn bộ phân tích HTML không thể thực hiện được với các biểu thức thông thường, vì nó phụ thuộc vào việc khớp thẻ mở và thẻ đóng không thể thực hiện được với biểu thức chính quy.

Các biểu thức thông thường chỉ có thể phù hợp với các ngôn ngữ thông thường nhưng HTML là ngôn ngữ không ngữ cảnhkhông phải là ngôn ngữ thông thường (Như @StefanPochmann đã chỉ ra, các ngôn ngữ thông thường cũng không có ngữ cảnh, vì vậy không có ngữ cảnh không nhất thiết không phải là thông thường). Điều duy nhất bạn có thể làm với regexps trên HTML là heuristic nhưng điều đó sẽ không hoạt động trên mọi điều kiện. Có thể trình bày một tệp HTML sẽ bị khớp sai bởi bất kỳ biểu thức chính quy nào.


26
Câu trả lời tốt nhất cho đến nay. Nếu nó chỉ có thể phù hợp với các ngữ pháp thông thường thì chúng ta sẽ cần một biểu thức chính quy lớn vô hạn để phân tích một ngữ pháp không ngữ cảnh như HTML. Tôi thích khi những điều này có câu trả lời lý thuyết rõ ràng.
vào

2
Tôi giả sử chúng ta đang thảo luận về các biểu thức kiểu Perl nơi chúng không thực sự là biểu thức chính quy.
Hank Gay

5
Trên thực tế, các biểu thức chính quy .Net có thể khớp với mở bằng các thẻ đóng, ở một mức độ nào đó, sử dụng các nhóm cân bằng và biểu thức được tạo cẩn thận. Chứa tất cả những thứ đó trong một bản regex vẫn còn điên rồ, tất nhiên, nó sẽ trông giống như mã Chtulhu tuyệt vời và có lẽ cũng sẽ triệu tập mã thật. Và cuối cùng nó vẫn không hoạt động cho tất cả các trường hợp. Họ nói rằng nếu bạn viết một biểu thức chính quy có thể phân tích chính xác bất kỳ HTML nào, vũ trụ sẽ tự sụp đổ.
Alex Paven

5
Một số lib regex có thể thực hiện các biểu thức chính quy đệ quy (thực sự biến chúng thành các biểu thức không chính quy :)
Ondra ižka

43
-1 Câu trả lời này rút ra kết luận đúng ("Thật là một ý tưởng tồi khi phân tích HTML bằng Regex") từ các đối số sai ("Vì HTML không phải là ngôn ngữ thông thường"). Điều mà hầu hết mọi người hiện nay có nghĩa là khi họ nói "regex" (PCRE) có khả năng tốt không chỉ phân tích ngữ pháp không ngữ cảnh (thực sự tầm thường), mà còn về ngữ pháp nhạy cảm ngữ cảnh (xem stackoverflow.com/questions/7434272/ Cẩu ).
NikiC

35

Đối với regex nhanh chóng, ba mươi regrec sẽ làm tốt. Nhưng điều cơ bản cần biết là không thể xây dựng một biểu thức chính quy sẽ phân tích chính xác HTML.

Lý do là regexps không thể xử lý các biểu thức lồng nhau một cách độc đoán. Xem Các biểu thức chính quy có thể được sử dụng để khớp với các mẫu lồng nhau không?


1
Một số lib regex có thể thực hiện các biểu thức chính quy đệ quy (thực sự biến chúng thành các biểu thức không chính quy :)
Ondra ižka

23

(Từ http://htmlparsing.com/regexes )

Giả sử bạn đã có tệp HTML nơi bạn đang cố trích xuất URL từ thẻ <img>.

<img src="http://example.com/whatever.jpg">

Vì vậy, bạn viết một regex như thế này trong Perl:

if ( $html =~ /<img src="(.+)"/ ) {
    $url = $1;
}

Trong trường hợp này, $urlthực sự sẽ chứa http://example.com/whatever.jpg. Nhưng điều gì xảy ra khi bạn bắt đầu nhận HTML như thế này:

<img src='http://example.com/whatever.jpg'>

hoặc là

<img src=http://example.com/whatever.jpg>

hoặc là

<img border=0 src="http://example.com/whatever.jpg">

hoặc là

<img
    src="http://example.com/whatever.jpg">

hoặc bạn bắt đầu nhận được dương tính giả từ

<!-- // commented out
<img src="http://example.com/outdated.png">
-->

Nó trông rất đơn giản và có thể đơn giản đối với một tệp duy nhất, không thay đổi, nhưng đối với bất kỳ điều gì bạn sẽ làm trên dữ liệu HTML tùy ý, các biểu thức chính chỉ là một công thức cho sự đau lòng trong tương lai.


4
Đây có vẻ là câu trả lời thực sự - trong khi có thể phân tích HTML tùy ý bằng regex vì các biểu thức ngày nay không chỉ là một automata hữu hạn, để phân tích html tùy ý và không chỉ là một trang cụ thể mà bạn phải thực hiện lại trình phân tích cú pháp HTML trong regrec và regexes chắc chắn trở thành 1000 lần không thể đọc được.
Smit Johnth

1
Này Andy, tôi đã dành thời gian để đưa ra một biểu thức hỗ trợ cho các trường hợp được đề cập của bạn. stackoverflow.com/a/40095824/1204332 Hãy cho tôi biết bạn nghĩ gì! :)
Ivan Chaer

2
Lý do trong câu trả lời này là cách lỗi thời, và áp dụng thậm chí ít hơn ngày hôm nay hơn nó đã làm ban đầu (mà tôi nghĩ rằng nó không). (Trích dẫn OP: "nếu bạn chỉ làm một việc gì đó đơn giản, nhanh chóng hoặc bẩn thỉu ...".)
Sz.

16

Hai lý do nhanh chóng:

  • viết một regex có thể chống lại đầu vào độc hại là khó khăn; khó hơn so với sử dụng một công cụ dựng sẵn
  • viết một regex có thể làm việc với đánh dấu lố bịch mà bạn chắc chắn sẽ bị mắc kẹt là khó khăn; khó hơn so với sử dụng một công cụ dựng sẵn

Về sự phù hợp của regexes để phân tích cú pháp nói chung: chúng không phù hợp. Bạn đã bao giờ thấy các loại regex bạn sẽ cần phân tích hầu hết các ngôn ngữ chưa?


2
Ồ Một downvote sau hơn 2 năm? Trong trường hợp bất cứ ai thắc mắc, tôi đã không nói "Bởi vì về mặt lý thuyết là không thể" bởi vì câu hỏi rõ ràng hỏi về "nhanh và bẩn", không "chính xác". OP rõ ràng đã đọc câu trả lời bao trùm lãnh thổ về mặt lý thuyết và vẫn không hài lòng.
Hank Gay

1
Có một upvote sau hơn 5 năm. :) Về lý do tại sao bạn có thể đã nhận được downvote, tôi không đủ điều kiện để nói, nhưng cá nhân tôi, tôi muốn xem một số ví dụ hoặc giải thích hơn là câu hỏi tu từ kết thúc.
Adam Jensen

3
Về cơ bản, tất cả các phân tích cú pháp html nhanh và bẩn được thực hiện trong các sản phẩm vận chuyển hoặc các công cụ nội bộ cuối cùng đều là một lỗ hổng bảo mật, hoặc một lỗi đang chờ xảy ra. Nó phải được khuyến khích với sự thích thú. Nếu người ta có thể sử dụng regex, người ta có thể sử dụng trình phân tích cú pháp html thích hợp.
Phục hồi lại

16

Theo như phân tích cú pháp, các biểu thức thông thường có thể hữu ích trong giai đoạn "phân tích từ vựng" (lexer), trong đó đầu vào được chia thành các mã thông báo. Nó ít hữu ích hơn trong giai đoạn "xây dựng một cây phân tích" thực tế.

Đối với trình phân tích cú pháp HTML, tôi hy vọng nó chỉ chấp nhận HTML được định dạng tốt và yêu cầu các khả năng bên ngoài những gì một biểu thức thông thường có thể làm (chúng không thể "đếm" và đảm bảo rằng một số phần tử mở nhất định được cân bằng với cùng một số của các yếu tố đóng).


8

Bởi vì có nhiều cách để "làm hỏng" HTML mà các trình duyệt sẽ xử lý theo cách khá tự do nhưng sẽ mất khá nhiều nỗ lực để tái tạo hành vi tự do của trình duyệt để bao quát tất cả các trường hợp bằng các biểu thức thông thường, do đó, regex của bạn chắc chắn sẽ thất bại trong một số trường hợp đặc biệt trường hợp và điều đó có thể sẽ giới thiệu những lỗ hổng bảo mật nghiêm trọng trong hệ thống của bạn.


1
Rất đúng, phần lớn HTML ngoài kia dường như là khủng khiếp. Tôi không hiểu làm thế nào một biểu thức chính quy thất bại có thể giới thiệu các lỗ hổng bảo mật nghiêm trọng. Bạn có thể đưa ra một ví dụ không?
vào

4
ntownsend: Chẳng hạn, bạn nghĩ rằng bạn đã loại bỏ tất cả các thẻ script khỏi HTML nhưng regex của bạn không bao gồm một trường hợp đặc biệt (giả sử, chỉ hoạt động trên IE6): boom, bạn có một lỗi XSS!
Tamas Czinege

1
Đây là một ví dụ giả thuyết nghiêm ngặt vì hầu hết các ví dụ trong thế giới thực quá phức tạp để phù hợp với những bình luận này nhưng bạn có thể tìm thấy một vài bằng cách nhanh chóng tìm hiểu về chủ đề này.
Tamas Czinege

3
+1 để đề cập đến góc độ bảo mật. Khi bạn giao tiếp với toàn bộ internet, bạn không thể đủ khả năng để viết mã "hoạt động hầu hết thời gian".
j_random_hacker

7

Vấn đề là hầu hết người dùng hỏi một câu hỏi liên quan đến HTML và regex đều làm điều này bởi vì họ không thể tìm thấy một regex riêng nào hoạt động. Sau đó, người ta phải suy nghĩ liệu mọi thứ sẽ dễ dàng hơn khi sử dụng trình phân tích cú pháp DOM hoặc SAX hoặc một cái gì đó tương tự. Chúng được tối ưu hóa và được xây dựng cho mục đích làm việc với các cấu trúc tài liệu giống như XML.

Chắc chắn, có những vấn đề có thể được giải quyết dễ dàng với các biểu thức thông thường. Nhưng sự nhấn mạnh nằm ở dễ dàng .

Nếu bạn chỉ muốn tìm tất cả các URL trông giống như http://.../bạn ổn với biểu thức chính quy. Nhưng nếu bạn muốn tìm tất cả các URL nằm trong một Phần tử có lớp 'mylink' thì có lẽ bạn nên sử dụng một trình phân tích cú pháp thích hợp.


6

Các biểu thức thông thường không được thiết kế để xử lý cấu trúc thẻ lồng nhau và điều phức tạp nhất (tệ nhất là không thể) để xử lý tất cả các trường hợp cạnh có thể bạn có với HTML thực.


6

Tôi tin rằng câu trả lời nằm trong lý thuyết tính toán. Để một ngôn ngữ được phân tích cú pháp bằng regex, nó phải theo định nghĩa "thông thường" ( liên kết ). HTML không phải là ngôn ngữ thông thường vì nó không đáp ứng một số tiêu chí cho ngôn ngữ thông thường (nhiều việc phải làm với nhiều cấp độ lồng nhau vốn có trong mã html). Nếu bạn quan tâm đến lý thuyết tính toán, tôi muốn giới thiệu cuốn sách này .


1
Tôi thực sự đã đọc cuốn sách đó. Nó đã không xảy ra với tôi rằng HTML là một ngôn ngữ không ngữ cảnh.
ntownsend

4

Biểu thức này lấy các thuộc tính từ các phần tử HTML. Nó hỗ trợ:

  • thuộc tính không trích dẫn / trích dẫn,
  • báo giá đơn / đôi,
  • thoát dấu ngoặc kép bên trong thuộc tính,
  • không gian xung quanh bằng dấu hiệu,
  • bất kỳ số lượng thuộc tính,
  • chỉ kiểm tra các thuộc tính bên trong thẻ,
  • thoát bình luận, và
  • quản lý các trích dẫn khác nhau trong một giá trị thuộc tính.

(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)

Kiểm tra nó ra . Nó hoạt động tốt hơn với các cờ "gisx", như trong bản demo.


1
Thật là thú vị. Không đọc được, có lẽ khó gỡ lỗi nhưng vẫn: Công việc ấn tượng!
Eric Duminil

Điều này vẫn còn mơ hồ cho rằng HTML được hình thành tốt ,. Nếu không khớp ngữ cảnh, điều này sẽ khớp với các URL rõ ràng trong các ngữ cảnh mà bạn thường không muốn khớp chúng, như trong một đoạn mã JavaScript bên trong <script>thẻ.
tripleee

4

HTML / XML được chia thành đánh dấu và nội dung. Regex chỉ hữu ích khi thực hiện phân tích thẻ từ vựng. Tôi đoán bạn có thể suy luận nội dung. Nó sẽ là một lựa chọn tốt cho trình phân tích cú pháp SAX. Các thẻ và nội dung có thể được gửi đến một chức năng do người dùng xác định trong đó việc lồng / đóng các phần tử có thể được theo dõi.

Theo như phân tích các thẻ, nó có thể được thực hiện với regex và được sử dụng để tách các thẻ từ một tài liệu.

Qua nhiều năm thử nghiệm, tôi đã tìm thấy bí mật về cách trình duyệt phân tích các thẻ, cả hình thành tốt và xấu.

Các yếu tố bình thường được phân tích cú pháp với hình thức này:

Cốt lõi của các thẻ này sử dụng regex này

 (?:
      " [\S\s]*? " 
   |  ' [\S\s]*? ' 
   |  [^>]? 
 )+

Bạn sẽ nhận thấy đây [^>]?là một trong những lựa chọn thay thế. Điều này sẽ phù hợp với trích dẫn không cân bằng từ các thẻ không định hình.

Nó cũng là, gốc rễ duy nhất của tất cả các biểu hiện xấu xa thông thường. Cách nó được sử dụng sẽ kích hoạt một cú va chạm để thỏa mãn thùng chứa định lượng phải phù hợp, tham lam của nó.

Nếu được sử dụng một cách thụ động, sẽ không bao giờ có vấn đề Nhưng, nếu bạn buộc một thứ gì đó khớp với nhau bằng cách xen kẽ nó với một cặp thuộc tính / giá trị mong muốn và không cung cấp sự bảo vệ đầy đủ khỏi việc quay lui, đó là một cơn ác mộng ngoài tầm kiểm soát.

Đây là hình thức chung cho các thẻ cũ đơn giản. Chú ý tên [\w:]đại diện? Trong thực tế, các ký tự pháp lý đại diện cho tên thẻ là một danh sách đáng kinh ngạc của các ký tự Unicode.

 <     
 (?:
      [\w:]+ 
      \s+ 
      (?:
           " [\S\s]*? " 
        |  ' [\S\s]*? ' 
        |  [^>]? 
      )+
      \s* /?
 )
 >

Tiếp tục, chúng tôi cũng thấy rằng bạn không thể tìm kiếm một thẻ cụ thể mà không phân tích TẤT CẢ các thẻ. Ý tôi là bạn có thể, nhưng nó sẽ phải sử dụng kết hợp các động từ như (* SKIP) (* FAIL) nhưng tất cả các thẻ phải được phân tích cú pháp.

Lý do là cú pháp thẻ có thể được ẩn bên trong các thẻ khác, v.v.

Vì vậy, để phân tích thụ động tất cả các thẻ, một regex là cần thiết như bên dưới. Điều này đặc biệt phù hợp với nội dung vô hình là tốt.

Khi HTML hoặc xml mới hoặc bất kỳ cấu trúc mới nào khác phát triển, chỉ cần thêm nó dưới dạng một trong các lựa chọn thay thế.


Lưu ý trang web - Tôi chưa bao giờ thấy một trang web (hoặc xhtml / xml) mà điều này
gặp sự cố. Nếu bạn tìm thấy một, cho tôi biết.

Ghi chú hiệu suất - Thật nhanh chóng. Đây là trình phân tích cú pháp thẻ nhanh nhất tôi từng thấy
(có thể nhanh hơn, ai biết được).
Tôi có một vài phiên bản cụ thể. Nó cũng là tuyệt vời như cạp
(nếu bạn là loại thực hành).


Hoàn thành regex thô

<(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\1\s*(?=>))|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>

Định dạng

 <
 (?:
      (?:
           (?:
                # Invisible content; end tag req'd
                (                             # (1 start)
                     script
                  |  style
                  |  object
                  |  embed
                  |  applet
                  |  noframes
                  |  noscript
                  |  noembed 
                )                             # (1 end)
                (?:
                     \s+ 
                     (?>
                          " [\S\s]*? "
                       |  ' [\S\s]*? '
                       |  (?:
                               (?! /> )
                               [^>] 
                          )?
                     )+
                )?
                \s* >
           )

           [\S\s]*? </ \1 \s* 
           (?= > )
      )

   |  (?: /? [\w:]+ \s* /? )
   |  (?:
           [\w:]+ 
           \s+ 
           (?:
                " [\S\s]*? " 
             |  ' [\S\s]*? ' 
             |  [^>]? 
           )+
           \s* /?
      )
   |  \? [\S\s]*? \?
   |  (?:
           !
           (?:
                (?: DOCTYPE [\S\s]*? )
             |  (?: \[CDATA\[ [\S\s]*? \]\] )
             |  (?: -- [\S\s]*? -- )
             |  (?: ATTLIST [\S\s]*? )
             |  (?: ENTITY [\S\s]*? )
             |  (?: ELEMENT [\S\s]*? )
           )
      )
 )
 >

3

"Nó phụ thuộc" mặc dù. Đúng là regexes không và không thể phân tích HTML với độ chính xác thực sự, vì tất cả các lý do được đưa ra ở đây. Tuy nhiên, nếu hậu quả của việc làm sai (chẳng hạn như không xử lý các thẻ lồng nhau) là không đáng kể và nếu regexes siêu tiện lợi trong môi trường của bạn (chẳng hạn như khi bạn hack Perl), hãy tiếp tục.

Giả sử bạn, ồ, có thể phân tích các trang web liên kết đến trang web của bạn - có lẽ bạn đã tìm thấy chúng bằng tìm kiếm liên kết của Google - và bạn muốn có một cách nhanh chóng để có ý tưởng chung về bối cảnh bao quanh liên kết của bạn. Bạn đang cố chạy một báo cáo nhỏ có thể cảnh báo bạn liên kết thư rác, đại loại như thế.

Trong trường hợp đó, việc đánh giá sai một số tài liệu sẽ không phải là vấn đề lớn. Không ai ngoài bạn sẽ nhìn thấy những sai lầm, và nếu bạn rất may mắn, sẽ có một vài điều đủ để bạn có thể theo dõi riêng lẻ.

Tôi đoán tôi đang nói đó là một sự đánh đổi. Đôi khi, việc triển khai hoặc sử dụng một trình phân tích cú pháp chính xác - dễ như có thể - có thể không đáng để gặp rắc rối nếu độ chính xác không quan trọng.

Chỉ cần cẩn thận với các giả định của bạn. Tôi có thể nghĩ ra một vài cách mà phím tắt regrec có thể phản tác dụng nếu bạn đang cố phân tích một cái gì đó sẽ được hiển thị trước công chúng, chẳng hạn.


3

Chắc chắn có những trường hợp sử dụng biểu thức chính quy để phân tích một số thông tin từ HTML là cách chính xác - nó phụ thuộc rất nhiều vào tình huống cụ thể.

Sự đồng thuận ở trên là nói chung đó là một ý tưởng tồi. Tuy nhiên, nếu cấu trúc HTML được biết đến (và không có khả năng thay đổi) thì đó vẫn là một cách tiếp cận hợp lệ.


3

Hãy nhớ rằng trong khi bản thân HTML không thường xuyên, các phần của trang bạn đang xem có thể là thường xuyên.

Ví dụ, đó là một lỗi cho <form>các thẻ được lồng nhau; nếu trang web hoạt động chính xác, thì sử dụng biểu thức chính quy để lấy một cái <form>sẽ hoàn toàn hợp lý.

Gần đây tôi đã thực hiện một số thao tác quét web chỉ sử dụng Selenium và các biểu thức thông thường. Tôi đã đi với nó vì dữ liệu tôi muốn được đặt trong một <form>, và đặt trong một định dạng bảng đơn giản (vì vậy tôi thậm chí có thể đếm trên <table>, <tr><td>là phi lồng nhau - mà thực sự là rất không bình thường). Ở một mức độ nào đó, các biểu thức chính quy thậm chí gần như cần thiết, bởi vì một số cấu trúc tôi cần truy cập đã được phân định bởi các bình luận. (Soup đẹp có thể cho bạn ý kiến, nhưng sẽ rất khó để lấy <!-- BEGIN --><!-- END -->chặn khi sử dụng Beautiful Soup.)

Tuy nhiên, nếu tôi phải lo lắng về các bảng lồng nhau, cách tiếp cận của tôi đơn giản là không hiệu quả! Tôi đã phải quay trở lại với Soup đẹp. Tuy nhiên, ngay cả khi đó, đôi khi bạn có thể sử dụng một biểu thức thông thường để lấy đoạn bạn cần, và sau đó đi sâu vào đó.


2

Trên thực tế, phân tích cú pháp HTML bằng regex là hoàn toàn có thể có trong PHP. Bạn chỉ cần phân tích lại toàn bộ chuỗi ngược bằng cách sử dụng strrposđể tìm <và lặp lại biểu thức chính từ đó bằng cách sử dụng các công cụ xác định vô duyên mỗi lần để vượt qua các thẻ lồng nhau. Không ưa thích và chậm kinh khủng với những thứ lớn, nhưng tôi đã sử dụng nó cho trình soạn thảo mẫu cá nhân của riêng tôi cho trang web của tôi. Tôi thực sự không phân tích cú pháp HTML, nhưng một vài thẻ tùy chỉnh tôi đã tạo để truy vấn các mục cơ sở dữ liệu để hiển thị các bảng dữ liệu ( <#if()>thẻ của tôi có thể làm nổi bật các mục đặc biệt theo cách này). Tôi đã không chuẩn bị để tìm một trình phân tích cú pháp XML chỉ bằng một vài thẻ tự tạo (với dữ liệu không phải là XML trong đó) ở đây và ở đó.

Vì vậy, mặc dù câu hỏi này đã chết đáng kể, nó vẫn xuất hiện trong một tìm kiếm của Google. Tôi đọc nó và nghĩ rằng "thách thức được chấp nhận" và hoàn tất việc sửa mã đơn giản của mình mà không phải thay thế mọi thứ. Quyết định đưa ra một ý kiến ​​khác nhau cho bất cứ ai tìm kiếm một lý do tương tự. Ngoài ra câu trả lời cuối cùng đã được đăng 4 giờ trước vì vậy đây vẫn là một chủ đề nóng.


2
-1 để đề xuất ý tưởng TERRIBLE. Bạn đã xem xét khoảng trắng giữa thẻ và khung góc đóng chưa? (Ví dụ, <tag >) Bạn có xem xét các thẻ đóng bình luận không? (Ví dụ, <tag> <!-- </tag> -->) Bạn đã xem xét CDATA? Bạn đã xem xét các thẻ trường hợp không nhất quán? (Ví dụ, <Tag> </tAG>) Bạn có xem xét điều này là tốt?
rmunn

1
Trong trường hợp cụ thể của một vài thẻ tùy chỉnh của bạn, vâng, biểu thức chính quy hoạt động tốt. Vì vậy, không phải việc bạn sử dụng chúng là một sai lầm trong trường hợp cụ thể của bạn . Tuy nhiên, đó không phải là HTML và nói rằng "phân tích cú pháp HTML bằng regex là hoàn toàn có thể có trong PHP" hoàn toàn sai và là một ý tưởng TERRIBLE. Sự không nhất quán của HTML thực (và có nhiều cách hơn so với số ít tôi liệt kê) là lý do tại sao bạn không bao giờ nên phân tích HTML thực bằng các biểu thức thông thường. Xem, tốt, tất cả các câu trả lời khác cho câu hỏi này, cũng như câu trả lời tôi liên kết trong bình luận khác của tôi ở trên.
rmunn

2
PHP là một ngôn ngữ hoàn chỉnh, vì vậy nó hoàn toàn không sai. Mọi thứ có thể tính toán đều có thể, kể cả phân tích HTML. Không gian trong thẻ KHÔNG BAO GIỜ là một vấn đề và từ đó tôi đã điều chỉnh nó để liệt kê các thành phần thẻ theo thứ tự. Việc sử dụng của tôi tự động sửa các thẻ với vỏ không nhất quán, loại bỏ các công cụ nhận xét ở giai đoạn đầu tiên và sau một số bổ sung sau đó, tất cả các loại thẻ có thể dễ dàng được thêm vào (mặc dù tùy theo trường hợp, theo lựa chọn của riêng tôi). Và tôi khá chắc chắn rằng CDATA thực sự là một yếu tố XML, không phải là một HTML.
Deji

2
Phương pháp cũ của tôi (mà tôi đã mô tả ở đây) khá kém hiệu quả và gần đây tôi đã bắt đầu viết lại rất nhiều trình soạn thảo nội dung. Khi nói đến việc làm những điều này, khả năng không phải là vấn đề; cách tốt nhất luôn là mối quan tâm chính. Câu trả lời thực sự là "không có cách DỄ DÀNG để làm điều đó trong PHP". KHÔNG ai nói rằng không có cách nào để làm điều đó trong PHP hoặc đó là một ý tưởng tồi tệ, nhưng điều đó là không thể với regex, điều mà tôi chưa bao giờ thử, nhưng một lỗ hổng lớn trong câu trả lời của tôi là tôi cho rằng câu hỏi là về regex trong bối cảnh của PHP, điều này không nhất thiết phải như vậy.
Deji

2

Tôi cũng đã thử với một regex cho việc này. Nó hầu như hữu ích cho việc tìm các khối nội dung được ghép nối với thẻ HTML tiếp theo và nó không tìm kiếm các thẻ đóng phù hợp , nhưng nó sẽ chọn các thẻ đóng. Cuộn một chồng trong ngôn ngữ của bạn để kiểm tra những người.

Sử dụng với tùy chọn 'sx'. 'G' cũng vậy nếu bạn cảm thấy may mắn:

(?P<content>.*?)                # Content up to next tag
(?P<markup>                     # Entire tag
  <!\[CDATA\[(?P<cdata>.+?)]]>| # <![CDATA[ ... ]]>
  <!--(?P<comment>.+?)-->|      # <!-- Comment -->
  </\s*(?P<close_tag>\w+)\s*>|  # </tag>
  <(?P<tag>\w+)                 # <tag ...
    (?P<attributes>
      (?P<attribute>\s+
# <snip>: Use this part to get the attributes out of 'attributes' group.
        (?P<attribute_name>\w+)
        (?:\s*=\s*
          (?P<attribute_value>
            [\w:/.\-]+|         # Unquoted
            (?=(?P<_v>          # Quoted
              (?P<_q>['\"]).*?(?<!\\)(?P=_q)))
            (?P=_v)
          ))?
# </snip>
      )*
    )\s*
  (?P<is_self_closing>/?)   # Self-closing indicator
  >)                        # End of tag

Công cụ này được thiết kế cho Python (nó có thể hoạt động với các ngôn ngữ khác, chưa thử nó, nó sử dụng các giao diện tích cực, các giao diện tiêu cực và các phản hồi có tên). Hỗ trợ:

  • Mở thẻ - <div ...>
  • Đóng thẻ - </div>
  • Bình luận - <!-- ... -->
  • CDATA - <![CDATA[ ... ]]>
  • Thẻ tự đóng - <div .../>
  • Giá trị thuộc tính tùy chọn - <input checked>
  • Các giá trị thuộc tính không được trích dẫn / trích dẫn - <div style='...'>
  • Báo giá đơn / đôi - <div style="...">
  • Báo giá đã thoát - <a title='John\'s Story'>
    (đây không phải là HTML hợp lệ, nhưng tôi là một người tốt)
  • Khoảng cách xung quanh dấu bằng - <a href = '...'>
  • Tên được đặt cho bit thú vị

Nó cũng khá tốt về việc không kích hoạt các thẻ không đúng định dạng, như khi bạn quên một <hoặc >.

Nếu hương vị regex của bạn hỗ trợ các lần chụp có tên lặp đi lặp lại thì bạn là vàng, nhưng Python rethì không (tôi biết regex có, nhưng tôi cần sử dụng vanilla Python). Đây là những gì bạn nhận được:

  • content- Tất cả các nội dung cho đến thẻ tiếp theo. Bạn có thể bỏ nó ra.
  • markup - Toàn bộ thẻ với tất cả mọi thứ trong đó.
  • comment - Nếu đó là một bình luận, nội dung bình luận.
  • cdata- Nếu đó là một <![CDATA[...]]>, nội dung CDATA.
  • close_tag- Nếu đó là thẻ đóng ( </div>), tên thẻ.
  • tag- Nếu đó là thẻ mở ( <div>), tên thẻ.
  • attributes- Tất cả các thuộc tính bên trong thẻ. Sử dụng điều này để có được tất cả các thuộc tính nếu bạn không nhận được các nhóm lặp lại.
  • attribute - Lặp đi lặp lại, từng thuộc tính.
  • attribute_name - Lặp đi lặp lại, mỗi tên thuộc tính.
  • attribute_value- Lặp đi lặp lại, từng giá trị thuộc tính. Điều này bao gồm các trích dẫn nếu nó được trích dẫn.
  • is_self_closing- Đây là /nếu đó là một thẻ tự đóng, nếu không thì không có gì.
  • _q_v- Bỏ qua những điều này; chúng được sử dụng nội bộ cho các phản hồi.

Nếu công cụ regex của bạn không hỗ trợ các lần chụp có tên lặp đi lặp lại, có một phần được gọi ra mà bạn có thể sử dụng để lấy từng thuộc tính. Chỉ cần chạy regex đó trên attributesnhóm để lấy từng cái attribute, attribute_nameattribute_valuethoát khỏi nó.

Demo tại đây: https://regex101.com/r/mH8jSu/11


1

Các biểu thức thông thường không đủ mạnh cho một ngôn ngữ như HTML. Chắc chắn, có một số ví dụ mà bạn có thể sử dụng các biểu thức thông thường. Nhưng nói chung nó không thích hợp để phân tích cú pháp.


0

Bạn biết đấy ... có rất nhiều tâm lý của bạn KHÔNG THỂ làm được và tôi nghĩ rằng tất cả mọi người ở hai bên hàng rào đều đúng và sai. Bạn CÓ THỂ làm điều đó, nhưng cần một chút xử lý hơn là chỉ chạy một regex chống lại nó. Lấy điều này (tôi đã viết điều này trong vòng một giờ) làm ví dụ. Nó giả sử HTML là hoàn toàn hợp lệ, nhưng tùy thuộc vào ngôn ngữ bạn đang sử dụng để áp dụng regex đã nói ở trên, bạn có thể thực hiện một số sửa lỗi HTML để đảm bảo rằng nó sẽ thành công. Ví dụ: xóa các thẻ đóng không được phép ở đó: </img>ví dụ. Sau đó, thêm dấu gạch chéo chuyển tiếp HTML đơn vào các phần tử thiếu chúng, v.v.

[x].getElementsByTagName()Ví dụ, tôi sử dụng điều này trong bối cảnh viết thư viện cho phép tôi thực hiện truy xuất phần tử HTML gần giống với JavaScript . Tôi chỉ cần ghép các chức năng mà tôi đã viết trong phần DEFINE của regex và sử dụng nó để bước vào bên trong một cây các phần tử, từng phần một.

Vì vậy, đây sẽ là câu trả lời 100% cuối cùng để xác thực HTML? Không. Nhưng đó là một khởi đầu và với một chút công việc, nó có thể được thực hiện. Tuy nhiên, cố gắng thực hiện nó trong một lần thực thi regex là không thực tế, cũng không hiệu quả.

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.