Menggunakan ekspresi reguler untuk mem-parsing HTML: mengapa tidak?

207

Sepertinya setiap pertanyaan pada stackoverflow di mana penanya menggunakan regex untuk mengambil beberapa informasi dari HTML pasti akan memiliki "jawaban" yang mengatakan tidak menggunakan regex untuk mem-parsing HTML.

Kenapa tidak? Saya sadar bahwa ada parser HTML "nyata" kutipan-tanda kutip di luar sana seperti Beautiful Soup , dan saya yakin mereka kuat dan berguna, tetapi jika Anda hanya melakukan sesuatu yang sederhana, cepat, atau kotor, lalu mengapa repot menggunakan sesuatu yang begitu rumit ketika beberapa pernyataan regex akan berfungsi dengan baik?

Selain itu, apakah ada sesuatu yang mendasar yang saya tidak mengerti tentang regex yang membuat mereka menjadi pilihan yang buruk untuk penguraian secara umum?

ntownsend
sumber
3
saya pikir ini adalah korban penipuan dari stackoverflow.com/questions/133601
jcrossley3
23
Karena hanya Chuck Norris yang dapat menguraikan HTML dengan regex (seperti yang dijelaskan dalam hal Zalgo yang terkenal ini: stackoverflow.com/questions/1732348/… ).
takeshin
1
Pertanyaan ini mendorong saya untuk bertanya satu sama lain yang entah bagaimana terkait. Jika Anda tertarik: Mengapa tidak mungkin menggunakan regex untuk mem-parsing HTML / XML: penjelasan formal dalam istilah awam
mac
Waspadalah terhadap Zalgo
Kelly S. French
Pertanyaan ini telah ditambahkan ke FAQ Ekspresi Reguler Overflow Overflow , di bawah "Tugas Validasi Umum".
aliteralmind

Jawaban:

212

Seluruh parsing HTML tidak dimungkinkan dengan ekspresi reguler, karena itu tergantung pada pencocokan pembukaan dan tag penutup yang tidak mungkin dengan regexps.

Ekspresi reguler hanya dapat mencocokkan bahasa biasa tetapi HTML adalah bahasa bebas konteks dan bukan bahasa biasa (Seperti yang ditunjukkan oleh @StefanPochmann, bahasa biasa juga bebas konteks, jadi bebas konteks tidak selalu berarti tidak biasa). Satu-satunya hal yang dapat Anda lakukan dengan regexps pada HTML adalah heuristik tetapi itu tidak akan bekerja pada setiap kondisi. Seharusnya dimungkinkan untuk menyajikan file HTML yang akan dicocokkan secara salah dengan ekspresi reguler apa pun.

Johannes Weiss
sumber
26
Jawaban terbaik sejauh ini. Jika itu hanya bisa cocok dengan tata bahasa biasa maka kita akan membutuhkan regexp besar yang tak terhingga untuk mengurai tata bahasa bebas konteks seperti HTML. Saya suka ketika hal-hal ini memiliki jawaban teoretis yang jelas.
ntownsend
2
Saya berasumsi kami sedang mendiskusikan regex tipe Perl di mana mereka sebenarnya bukan ekspresi reguler.
Hank Gay
5
Sebenarnya, ekspresi reguler .Net dapat mencocokkan pembukaan dengan tag penutup, sampai batas tertentu, menggunakan grup penyeimbang dan ekspresi yang dibuat dengan cermat. Mengandung semua itu dalam regexp masih gila tentu saja, itu akan terlihat seperti kode Chtulhu yang hebat dan mungkin akan memanggil yang asli juga. Dan pada akhirnya itu masih tidak berfungsi untuk semua kasus. Mereka mengatakan bahwa jika Anda menulis ekspresi reguler yang dapat mem-parsing HTML dengan benar, alam semesta akan runtuh ke dirinya sendiri.
Alex Paven
5
Beberapa lib regex dapat melakukan ekspresi reguler rekursif (secara efektif menjadikannya ekspresi non-reguler :)
Ondra Žižka
43
-1 Jawaban ini menarik kesimpulan yang benar ("Merupakan ide yang buruk untuk mem-parsing HTML dengan Regex") dari argumen yang salah ("Karena HTML bukan bahasa biasa"). Hal yang kebanyakan orang maksud saat ini ketika mereka mengatakan "regex" (PCRE) mampu dengan baik tidak hanya mengurai tata bahasa bebas konteks (itu sebenarnya sepele), tetapi juga tata bahasa konteks-sensitif (lihat stackoverflow.com/questions/7434272/ ... )
NikiC
35

Untuk quick'n´dirty regexp akan baik-baik saja. Tetapi hal mendasar yang perlu diketahui adalah bahwa tidak mungkin untuk membangun regexp yang akan mem-parsing HTML dengan benar .

Alasannya adalah bahwa regexps tidak dapat menangani ekspresi bersarang secara arbiter. Lihat Bisakah ekspresi reguler digunakan untuk mencocokkan pola bersarang?

kmkaplan
sumber
1
Beberapa lib regex dapat melakukan ekspresi reguler rekursif (secara efektif menjadikannya ekspresi non-reguler :)
Ondra Žižka
23

(Dari http://htmlparsing.com/regexes )

Katakanlah Anda memiliki file HTML tempat Anda mencoba mengekstrak URL dari tag <img>.

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

Jadi Anda menulis regex seperti ini di Perl:

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

Dalam hal ini, $urlmemang akan mengandung http://example.com/whatever.jpg. Tetapi apa yang terjadi ketika Anda mulai mendapatkan HTML seperti ini:

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

atau

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

atau

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

atau

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

atau Anda mulai mendapatkan positif palsu dari

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

Itu terlihat sangat sederhana, dan mungkin sederhana untuk satu file yang tidak berubah, tetapi untuk apa pun yang akan Anda lakukan pada data HTML sewenang-wenang, regex hanyalah resep untuk sakit hati di masa depan.

Andy Lester
sumber
4
Ini tampaknya menjadi jawaban nyata - sementara itu mungkin untuk menguraikan HTML sewenang-wenang dengan regex karena todays regex lebih dari sekedar automata terbatas, untuk mengurai html sewenang-wenang dan bukan hanya halaman konkret Anda harus menerapkan parser HTML di regexp dan regexes pasti menjadi 1000 kali tidak dapat dibaca.
Smit Johnth
1
Hei Andy, saya meluangkan waktu untuk membuat ekspresi yang mendukung kasus yang Anda sebutkan. stackoverflow.com/a/40095824/1204332 Beritahu saya apa yang Anda pikirkan! :)
Ivan Chaer
2
Alasan dalam jawaban ini cara usang, dan berlaku bahkan kurang hari ini daripada itu awalnya (yang saya pikir tidak). (Mengutip OP: "jika Anda hanya melakukan sesuatu yang sederhana, cepat, atau kotor ...".)
Sz.
16

Dua alasan cepat:

  • menulis regex yang tahan terhadap input berbahaya sulit; jauh lebih sulit daripada menggunakan alat prebuilt
  • menulis regex yang dapat bekerja dengan markup konyol yang Anda pasti akan terjebak dengan sulit; jauh lebih sulit daripada menggunakan alat prebuilt

Mengenai kesesuaian regex untuk parsing secara umum: mereka tidak cocok. Pernahkah Anda melihat jenis regex yang Anda perlukan untuk menguraikan sebagian besar bahasa?

Hank Gay
sumber
2
Wow? A downvote setelah 2+ tahun? Jika ada yang bertanya-tanya, saya tidak mengatakan "Karena secara teori tidak mungkin" karena pertanyaannya dengan jelas bertanya tentang "cepat-dan-kotor", bukan "benar". OP jelas sudah membaca jawaban yang mencakup wilayah yang secara teori mustahil dan masih belum puas.
Hank Gay
1
Dapatkan upvote setelah 5+ tahun. :) Adapun mengapa Anda mungkin telah menerima downvote, saya tidak memenuhi syarat untuk mengatakan, tetapi secara pribadi, saya lebih suka melihat beberapa contoh, atau penjelasan daripada pertanyaan retoris penutupan.
Adam Jensen
3
Pada dasarnya semua penguraian html cepat dan kotor yang dilakukan dalam pengiriman produk atau alat internal berakhir dengan lubang keamanan yang menganga, atau bug yang menunggu untuk terjadi. Itu harus berkecil hati dengan semangat. Jika seseorang dapat menggunakan regex, seseorang dapat menggunakan parser html yang tepat.
Pasang kembali Monica
16

Sejauh penguraian, ekspresi reguler dapat berguna dalam tahap "analisis leksikal" (lexer), di mana input dipecah menjadi token. Ini kurang berguna dalam tahap "build a parse tree" yang sebenarnya.

Untuk parser HTML, saya berharap itu hanya menerima HTML yang terbentuk dengan baik dan yang membutuhkan kemampuan di luar apa yang bisa dilakukan ekspresi reguler (mereka tidak bisa "menghitung" dan memastikan bahwa sejumlah elemen pembuka diseimbangkan dengan angka yang sama elemen penutup).

Vatine
sumber
8

Karena ada banyak cara untuk "mengacaukan" HTML yang akan diperlakukan oleh peramban dengan cara yang agak liberal, tetapi perlu upaya yang cukup untuk mereproduksi perilaku liberal peramban untuk menutupi semua kasus dengan ekspresi reguler, sehingga regex Anda pasti akan gagal pada beberapa hal khusus. kasus, dan itu mungkin akan memperkenalkan celah keamanan yang serius di sistem Anda.

Tamas Czinege
sumber
1
Sangat benar, sebagian besar HTML di luar sana tampaknya mengerikan. Saya tidak mengerti bagaimana ekspresi reguler yang gagal dapat menimbulkan celah keamanan yang serius. Bisakah Anda memberi contoh?
ntownsend
4
ntownsend: Misalnya, Anda berpikir Anda telah menghapus semua tag skrip dari HTML tetapi regex Anda gagal mencakup kasus khusus (misalkan, hanya bekerja pada IE6): boom, Anda memiliki XSS kelayakan!
Tamas Czinege
1
Ini adalah contoh yang sangat hipotetis karena sebagian besar contoh dunia nyata terlalu rumit untuk dimasukkan ke dalam komentar ini, tetapi Anda dapat menemukan beberapa dengan cepat googling pada subjek.
Tamas Czinege
3
+1 untuk menyebutkan sudut keamanan. Ketika Anda berinteraksi dengan seluruh internet Anda tidak mampu untuk menulis kode "berfungsi sebagian besar waktu".
j_random_hacker
7

Masalahnya adalah bahwa sebagian besar pengguna yang mengajukan pertanyaan yang berkaitan dengan HTML dan regex melakukan ini karena mereka tidak dapat menemukan regex sendiri yang berfungsi. Maka kita harus berpikir apakah semuanya akan lebih mudah saat menggunakan DOM atau SAX parser atau yang serupa. Mereka dioptimalkan dan dibangun untuk tujuan bekerja dengan struktur dokumen seperti XML.

Tentu, ada masalah yang bisa diselesaikan dengan mudah dengan ekspresi reguler. Namun penekanannya terletak pada mudah .

Jika Anda hanya ingin menemukan semua URL yang terlihat http://.../baik-baik saja dengan regexps. Tetapi jika Anda ingin menemukan semua URL yang ada di a-Element yang memiliki kelas 'mylink' Anda mungkin lebih baik menggunakan parser yang sesuai.

okoman
sumber
6

Ekspresi reguler tidak dirancang untuk menangani struktur tag bersarang, dan paling rumit (paling buruk, tidak mungkin) untuk menangani semua kasus tepi yang mungkin Anda dapatkan dengan HTML nyata.

Peter Boughton
sumber
6

Saya percaya bahwa jawabannya terletak pada teori perhitungan. Untuk bahasa yang akan diuraikan menggunakan regex itu harus dengan definisi "biasa" ( tautan ). HTML bukan bahasa biasa karena tidak memenuhi sejumlah kriteria untuk bahasa biasa (banyak hubungannya dengan banyak tingkat bersarang yang melekat dalam kode html). Jika Anda tertarik pada teori perhitungan, saya akan merekomendasikan buku ini .

taggers
sumber
1
Saya sebenarnya sudah membaca buku itu. Tidak terpikir oleh saya bahwa HTML adalah bahasa bebas konteks.
ntownsend
4

Ekspresi ini mengambil atribut dari elemen HTML. Ini mendukung:

  • atribut yang tidak dikutip / dikutip,
  • kutipan tunggal / ganda,
  • lolos kutipan dalam atribut,
  • spasi di sekitar sama dengan tanda,
  • sejumlah atribut,
  • periksa hanya untuk atribut di dalam tag,
  • komentar lepas, dan
  • mengelola kutipan berbeda dalam nilai atribut.

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

Lihat itu . Ini berfungsi lebih baik dengan bendera "gisx", seperti pada demo.

Ivan Chaer
sumber
1
Itu sangat menarik. Tidak terbaca, mungkin sulit di-debug tetapi tetap: Pekerjaan yang mengesankan!
Eric Duminil
Ini masih samar-samar mengasumsikan bahwa HTML terbentuk dengan baik. Tanpa pencocokan konteks, ini akan cocok dengan URL yang jelas dalam konteks di mana Anda biasanya tidak ingin mencocokkannya, seperti dalam sepotong kode JavaScript di dalam <script>tag.
tripleee
4

HTML / XML dibagi menjadi markup dan konten. Regex hanya berguna melakukan penguraian tag leksikal. Saya kira Anda dapat menyimpulkan konten. Ini akan menjadi pilihan yang baik untuk parser SAX. Tag dan konten dapat dikirimkan ke fungsi yang ditentukan pengguna di mana elemen / penutupan elemen dapat dilacak.

Sejauh hanya mengurai tag, itu bisa dilakukan dengan regex dan digunakan untuk menghapus tag dari dokumen.

Selama bertahun-tahun pengujian, saya telah menemukan rahasia cara tag parse browser, baik dan buruk terbentuk.

Elemen normal diuraikan dengan bentuk ini:

Inti dari tag ini menggunakan regex ini

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

Anda akan melihat ini [^>]?sebagai salah satu alternatif. Ini akan cocok dengan kutipan yang tidak seimbang dari tag yang dibentuk dengan buruk.

Itu juga, satu-satunya akar dari semua kejahatan untuk ekspresi reguler. Cara yang digunakan akan memicu benturan untuk memenuhi wadahnya yang serakah dan harus dicocokkan.

Jika digunakan secara pasif, tidak pernah ada masalah Tapi, jika Anda memaksakan sesuatu untuk mencocokkan dengan menyelingi dengan pasangan atribut / nilai yang diinginkan, dan tidak memberikan perlindungan yang memadai dari pengulangan, itu adalah mimpi buruk yang tidak terkendali.

Ini adalah bentuk umum untuk tag lama biasa. Perhatikan yang [\w:]mewakili nama tag? Pada kenyataannya, karakter hukum yang mewakili nama tag adalah daftar karakter Unicode yang luar biasa.

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

Selanjutnya, kami juga melihat bahwa Anda tidak dapat mencari tag tertentu tanpa menguraikan SEMUA tag. Maksud saya Anda bisa, tetapi harus menggunakan kombinasi kata kerja seperti (* SKIP) (* GAGAL) tetapi semua tag harus diuraikan.

Alasannya adalah bahwa sintaksis tag mungkin disembunyikan di dalam tag lain, dll.

Jadi, untuk mem-parsing semua tag secara pasif, diperlukan regex seperti di bawah ini. Yang satu ini juga cocok dengan konten yang tidak terlihat .

Saat HTML baru atau xml atau lainnya mengembangkan konstruksi baru, tambahkan saja sebagai salah satu alternatif.


Catatan halaman web - Saya belum pernah melihat halaman web (atau xhtml / xml) yang
bermasalah dengannya. Jika Anda menemukannya, beri tahu saya.

Catatan kinerja - Cepat. Ini adalah tag parser tercepat yang pernah saya lihat
(mungkin ada yang lebih cepat, siapa tahu).
Saya punya beberapa versi spesifik. Ini juga sangat baik sebagai scraper
(jika Anda tipe tangan).


Regex mentah lengkap

<(?:(?:(?:(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]*?))))>

Tampilan diformat

 <
 (?:
      (?:
           (?:
                # 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]*? )
           )
      )
 )
 >
tripleee
sumber
3

"Tergantung". Memang benar bahwa regex tidak dan tidak dapat mem-parsing HTML dengan akurasi yang sebenarnya, untuk semua alasan yang diberikan di sini. Namun, jika konsekuensi dari kesalahan (seperti tidak menangani tag bersarang) kecil, dan jika regex sangat nyaman di lingkungan Anda (seperti saat Anda meretas Perl), silakan.

Misalkan Anda, oh, mungkin mem-parsing halaman web yang terhubung ke situs Anda - mungkin Anda menemukannya dengan pencarian tautan Google - dan Anda menginginkan cara cepat untuk mendapatkan gambaran umum tentang konteks di sekitar tautan Anda. Anda mencoba menjalankan sedikit laporan yang mungkin mengingatkan Anda untuk menautkan spam, sesuatu seperti itu.

Dalam hal ini, salah membaca beberapa dokumen tidak akan menjadi masalah besar. Tidak ada seorang pun selain Anda yang akan melihat kesalahan, dan jika Anda sangat beruntung akan ada cukup banyak yang dapat Anda tindak lanjuti secara individu.

Saya kira saya katakan itu tradeoff. Terkadang menerapkan atau menggunakan parser yang benar - semudah itu mungkin - mungkin tidak sepadan dengan masalah jika akurasi tidak kritis.

Berhati-hatilah dengan asumsi Anda. Saya dapat memikirkan beberapa cara cara pintas regexp dapat menjadi bumerang jika Anda mencoba mengurai sesuatu yang akan ditampilkan di depan umum, misalnya.

makanan kucing
sumber
3

Pasti ada kasus di mana menggunakan ekspresi reguler untuk mem-parsing beberapa informasi dari HTML adalah cara yang benar - itu sangat tergantung pada situasi tertentu.

Konsensus di atas adalah bahwa secara umum itu adalah ide yang buruk. Namun jika struktur HTML diketahui (dan tidak mungkin berubah) maka itu masih merupakan pendekatan yang valid.

Jason
sumber
3

Ingatlah bahwa meskipun HTML itu sendiri tidak teratur, bagian dari halaman yang Anda lihat mungkin teratur.

Misalnya, ini merupakan kesalahan <form>tag yang akan disarangkan; jika halaman web berfungsi dengan benar, maka menggunakan ekspresi reguler untuk mengambil <form>akan sepenuhnya masuk akal.

Baru-baru ini saya melakukan pengikisan web menggunakan hanya Selenium dan ekspresi reguler. Saya lolos karena data yang saya inginkan dimasukkan ke dalam <form>, dan dimasukkan ke dalam format tabel sederhana (jadi saya bahkan bisa mengandalkan <table>, <tr>dan <td>menjadi non-bersarang - yang sebenarnya sangat tidak biasa). Pada tingkat tertentu, ekspresi reguler bahkan hampir diperlukan, karena beberapa struktur yang perlu saya akses dibatasi oleh komentar. (Beautiful Soup dapat memberi Anda komentar, tetapi akan sulit untuk mengambil <!-- BEGIN -->dan <!-- END -->memblokir menggunakan Beautiful Soup.)

Namun, jika saya harus khawatir tentang tabel bersarang, pendekatan saya tidak akan berhasil! Saya harus kembali pada Beautiful Soup. Meski begitu, kadang-kadang, Anda dapat menggunakan ekspresi reguler untuk mengambil potongan yang Anda butuhkan, dan kemudian menelusuri dari sana.

alpheus
sumber
2

Sebenarnya, parsing HTML dengan regex sangat mungkin di PHP. Anda hanya perlu menguraikan seluruh string ke belakang menggunakan strrposuntuk menemukan <dan mengulangi regex dari sana menggunakan specifier ungreedy setiap kali untuk mendapatkan lebih dari tag bersarang. Tidak suka dan sangat lambat pada hal-hal besar, tetapi saya menggunakannya untuk editor templat pribadi saya untuk situs web saya. Sebenarnya saya tidak mem-parsing HTML, tetapi beberapa tag khusus yang saya buat untuk query entri database untuk menampilkan tabel data ( <#if()>tag saya bisa menyorot entri khusus dengan cara ini). Saya tidak siap untuk mencari parser XML hanya pada beberapa tag yang dibuat sendiri (dengan data yang sangat non-XML) di sana-sini.

Jadi, meskipun pertanyaan ini sudah sangat mati, pertanyaan itu masih muncul di pencarian Google. Saya membacanya dan berpikir "tantangan diterima" dan selesai memperbaiki kode sederhana saya tanpa harus mengganti semuanya. Memutuskan untuk menawarkan pendapat yang berbeda kepada siapa pun yang mencari alasan yang sama. Juga jawaban terakhir telah diposting 4 jam yang lalu jadi ini masih menjadi topik hangat.

Deji
sumber
2
-1 untuk menyarankan ide yang MENGERIKAN. Apakah Anda mempertimbangkan spasi kosong antara tag dan braket sudut penutup? (Misalnya, <tag >) Apakah Anda mempertimbangkan tag penutup yang dikomentari? (Misalnya, <tag> <!-- </tag> -->) Apakah Anda mempertimbangkan CDATA? Apakah Anda menganggap tag kasus tidak konsisten? (Misalnya, <Tag> </tAG>) Apakah Anda juga mempertimbangkan ini ?
rmunn
1
Dalam kasus tertentu dari beberapa tag khusus Anda, ya, ekspresi reguler berfungsi dengan baik. Jadi bukan karena Anda menggunakan mereka adalah kesalahan dalam kasus khusus Anda . Itu bukan HTML, meskipun, dan mengatakan "HTML parsing dengan regex sangat mungkin di PHP" hanya benar-benar salah, dan ide yang MENGERIKAN. Ketidakkonsistenan HTML nyata (dan ada cara lebih dari beberapa yang saya sebutkan) adalah mengapa Anda tidak boleh menguraikan HTML asli dengan ekspresi reguler. Lihat, well, semua jawaban lain untuk pertanyaan ini, serta jawaban yang saya tautkan dalam komentar saya yang lain di atas.
rmunn
2
PHP adalah bahasa turing-complete, jadi tidak sepenuhnya salah sama sekali. Segala sesuatu yang dimungkinkan secara komputasi adalah mungkin, termasuk parsing HTML. Spasi dalam tag TIDAK PERNAH menjadi masalah dan sejak itu saya mengadaptasinya untuk mendaftarkan elemen tag secara berurutan. Saya menggunakan tag yang dikoreksi secara otomatis dengan casing yang tidak konsisten, menghapus hal-hal yang dikomentari pada tahap pertama dan setelah beberapa penambahan nanti semua jenis tag dapat dengan mudah ditambahkan (meskipun case-sensitive, dengan pilihan saya sendiri). Dan saya cukup yakin CDATA sebenarnya adalah elemen XML, bukan HTML.
Deji
2
Metode lama saya (yang saya jelaskan di sini) cukup tidak efisien dan saya baru-baru ini mulai menulis ulang banyak editor konten. Ketika melakukan hal-hal ini, kemungkinan bukanlah masalahnya; cara terbaik selalu menjadi perhatian utama. Jawaban sebenarnya adalah "tidak ada cara MUDAH untuk melakukannya di PHP". NO ONE mengatakan tidak ada cara untuk melakukannya dalam PHP atau itu adalah ide yang buruk, tetapi itu tidak mungkin dengan regex, yang sejujurnya saya belum pernah mencoba, tetapi satu kelemahan utama dalam jawaban saya adalah saya berasumsi pertanyaan itu merujuk pada regex dalam konteks PHP, yang belum tentu demikian.
Deji
2

Saya mencoba tangan saya di regex untuk ini juga. Ini sebagian besar berguna untuk menemukan potongan konten yang dipasangkan dengan tag HTML berikutnya, dan tidak mencari yang cocok dengan tag dekat, tetapi itu akan mengambil tag dekat. Gulung tumpukan dalam bahasa Anda sendiri untuk memeriksanya.

Gunakan dengan opsi 'sx'. 'g' juga jika Anda merasa beruntung:

(?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

Yang ini dirancang untuk Python (mungkin bekerja untuk bahasa lain, belum mencobanya, ia menggunakan lookaheads positif, lookbehinds negatif, dan bernama backreferences). Mendukung:

  • Buka Tag - <div ...>
  • Tutup Tag - </div>
  • Komentar - <!-- ... -->
  • CDATA - <![CDATA[ ... ]]>
  • Tag Penutupan Sendiri - <div .../>
  • Nilai Atribut Opsional - <input checked>
  • Nilai Atribut Tanda Kutip / Dikutip - <div style='...'>
  • Kutipan Tunggal / Ganda - <div style="...">
  • Escaped Quotes - <a title='John\'s Story'>
    (ini bukan HTML yang benar-benar valid, tapi saya orang yang baik)
  • Spaces Around Equals Signs - <a href = '...'>
  • Capture Bernama Untuk Bit Menarik

Ini juga cukup bagus tentang tidak memicu tag salah bentuk, seperti ketika Anda lupa < atau >.

Jika rasa regex Anda mendukung tangkapan bernama berulang maka Anda adalah emas, tetapi Python retidak (saya tahu regex tidak, tapi saya perlu menggunakan vanilla Python). Inilah yang Anda dapatkan:

  • content- Semua konten hingga tag berikutnya. Anda bisa meninggalkan ini.
  • markup - Seluruh tag dengan semua yang ada di dalamnya.
  • comment - Jika itu adalah komentar, isi komentar.
  • cdata- Jika a <![CDATA[...]]>, isi CDATA.
  • close_tag- Jika ini tag dekat ( </div>), nama tag.
  • tag- Jika ini merupakan tag terbuka ( <div>), nama tag tersebut.
  • attributes- Semua atribut di dalam tag. Gunakan ini untuk mendapatkan semua atribut jika Anda tidak mendapatkan grup berulang.
  • attribute - Diulang, setiap atribut.
  • attribute_name - Diulang, setiap nama atribut.
  • attribute_value- Diulang, setiap nilai atribut. Ini termasuk kutipan jika dikutip.
  • is_self_closing- Ini adalah /jika tag penutup sendiri, jika tidak apa-apa.
  • _qdan _v- Abaikan ini; mereka digunakan secara internal untuk referensi.

Jika mesin regex Anda tidak mendukung tangkapan bernama berulang, ada bagian yang dipanggil yang bisa Anda gunakan untuk mendapatkan setiap atribut. Jalankan regex itu pada attributesgrup untuk mendapatkan masing-masing attribute, attribute_namedan attribute_valuekeluar dari sana.

Demo di sini: https://regex101.com/r/mH8jSu/11

Hounshell
sumber
1

Ekspresi reguler tidak cukup kuat untuk bahasa seperti HTML. Tentu, ada beberapa contoh di mana Anda bisa menggunakan ekspresi reguler. Tetapi secara umum tidak cocok untuk parsing.

Gumbo
sumber
0

Anda tahu ... ada banyak mentalitas yang TIDAK BISA Anda lakukan dan saya pikir semua orang di kedua sisi pagar itu benar dan salah. Anda BISA melakukannya, tetapi butuh sedikit pemrosesan lebih dari hanya menjalankan satu regex melawannya. Ambil ini (saya menulis ini dalam satu jam) sebagai contoh. Ini mengasumsikan HTML benar-benar valid, tetapi tergantung pada bahasa apa yang Anda gunakan untuk menerapkan regex yang disebutkan di atas, Anda bisa melakukan beberapa perbaikan pada HTML untuk memastikan itu akan berhasil. Misalnya, menghapus tag penutup yang tidak seharusnya ada di sana: </img>misalnya. Lalu, tambahkan slash forward HTML tunggal penutup ke elemen yang tidak ada, dll.

Saya akan menggunakan ini dalam konteks menulis perpustakaan yang akan memungkinkan saya untuk melakukan pencarian elemen HTML yang mirip dengan JavaScript [x].getElementsByTagName(), misalnya. Saya baru saja menambahkan fungsi yang saya tulis di bagian DEFINE dari regex dan menggunakannya untuk melangkah masuk ke dalam pohon elemen, satu per satu.

Jadi, apakah ini akan menjadi jawaban akhir 100% untuk memvalidasi HTML? Tidak. Tapi ini awal dan dengan sedikit lebih banyak pekerjaan, itu bisa dilakukan. Namun, mencoba melakukannya di dalam satu eksekusi regex tidak praktis, juga tidak efisien.

Erutan409
sumber