HTML'yi ayrıştırmak için normal ifadeler kullanma: neden olmasın?


207

Askerlerin HTML'den bazı bilgileri almak için regex'i kullandığı stackoverflow'daki her sorunun kaçınılmaz olarak HTML'yi ayrıştırmak için regex kullanmamayacağını söyleyen bir "cevabı" olacaktır.

Neden olmasın? Güzel Çorba gibi alıntı-unquote "gerçek" HTML ayrıştırıcıları olduğunu biliyorum ve güçlü ve kullanışlı olduklarından eminim, ancak sadece basit, hızlı veya kirli bir şey yapıyorsanız, neden birkaç regex ifadeleri işe yarayacak zaman çok karmaşık bir şey kullanmak rahatsız?

Dahası, regex hakkında anlamadığım temel bir şey var mı, bu da onları genel olarak ayrıştırmak için kötü bir seçim haline getiriyor mu?



23
Sadece Chuck Norris Çünkü can Ayrıştırma HTML regex ile (: Bu ünlü Zalgo şey açıklanmıştır stackoverflow.com/questions/1732348/... ).
takeshin

1
Bu soru, bir şekilde ilişkili olan başka bir soru sormamı istedi. İlgilenmeniz durumunda: HTML / XML'i ayrıştırmak için regex'i neden kullanamıyorsunuz: layman'ın terimleriyle resmi bir açıklama
mac


Bu soru, "Ortak Doğrulama Görevleri" altında Yığın Taşması Düzenli İfade SSS'ye eklenmiştir .
aliteralmind

Yanıtlar:


212

Normal ifadelerde HTML ayrıştırma işleminin tamamı mümkün değildir, çünkü regexps ile mümkün olmayan açılış ve kapanış etiketinin eşleştirilmesine bağlıdır.

Normal ifadeler yalnızca normal dillerle eşleşebilir , ancak HTML bağlamsız bir dildir ve normal bir dil değildir (@StefanPochmann'ın işaret ettiği gibi, normal diller de bağlamsızdır, bu nedenle bağlamdan bağımsız olarak düzenli olmak zorunda değildir). HTML'deki regexps ile yapabileceğiniz tek şey sezgisel taramadır, ancak bu her koşulda işe yaramaz. Herhangi bir normal ifade ile yanlış eşleştirilecek bir HTML dosyası sunmak mümkün olmalıdır.


26
Şimdiye kadarki en iyi cevap. Yalnızca normal gramerlerle eşleşebiliyorsa, HTML gibi bağlamsız bir gramer ayrıştırmak için sonsuz büyük bir regexp'ye ihtiyacımız olurdu. Bu şeylerin net teorik cevapları olduğunda seviyorum.
ntownsend

2
Aslında Perl-tipi regexleri tartıştıklarını, aslında normal ifadeler olmadığını düşündüm.
Hank Gay

5
Aslında, .Net normal ifadeleri, dengeleme grupları ve dikkatle hazırlanmış bir ifade kullanarak açılışları kapanış etiketleriyle bir dereceye kadar eşleştirebilir. İçeren tüm bir regexp'nin o hala tabii deli, büyük kod Chtulhu gibi görünecektir ve muhtemelen de gerçek bir çağırmak olacaktır. Ve sonunda hala tüm davalarda işe yaramayacak. Herhangi bir HTML'yi doğru şekilde ayrıştırabilen normal bir ifade yazarsanız, evrenin kendi üzerine çökeceğini söylerler.
Alex Paven

5
Bazı normal ifadeler özyinelemeli düzenli ifadeler yapabilir (etkili bir şekilde düzenli olmayan ifadeler yapar :)
Ondra Žižka

43
-1 Bu cevap doğru argümanlardan ("HTML'yi Regex ile ayrıştırmak kötü bir fikirdir") yanlış argümanlardan ("HTML normal bir dil olmadığı için") ortaya çıkar. Günümüzde çoğu insanın "regex" (PCRE) dediklerinde kastettiği şey, sadece bağlamdan bağımsız gramerleri (aslında önemsizdir) ayrıştırmakla kalmayıp aynı zamanda bağlama duyarlı gramerleri de ayrıştırma yeteneğine sahiptir (bkz. Stackoverflow.com/questions/7434272/ … ).
NikiC

35

Quick´n´dirty için normal ifade iyi olur. Ancak bilinmesi gereken temel şey, HTML'yi doğru bir şekilde ayrıştıracak bir regexp oluşturmanın imkansız olmasıdır .

Bunun nedeni normal ifadelerin keyfi olarak iç içe ifadeleri işleyememesidir. Bkz. Normal ifadeler, iç içe geçmiş kalıplarla eşleşmek için kullanılabilir mi?


1
Bazı normal ifadeler özyinelemeli düzenli ifadeler yapabilir (etkili bir şekilde düzenli olmayan ifadeler yapar :)
Ondra Žižka

23

( Http://htmlparsing.com/regexes adresinden )

<img> etiketlerinden URL'leri çıkarmaya çalıştığınız bir HTML dosyanız olduğunu varsayalım.

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

Yani Perl'de böyle bir regex yazıyorsunuz:

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

Bu durumda, $urlgerçekten içerecektir http://example.com/whatever.jpg. Ancak şu şekilde HTML almaya başladığınızda ne olur:

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

veya

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

veya

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

veya

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

ya da yanlış pozitifler almaya başlıyorsunuz

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

Çok basit görünüyor ve tek, değişmeyen bir dosya için basit olabilir, ancak keyfi HTML verilerinde yapacağınız her şey için, normal ifadeler gelecekteki kalp ağrısı için bir reçetedir.


4
Bu gerçek cevap gibi görünüyor - muhtemelen regex ile rastgele HTML ayrıştırmak mümkün olsa da, bugünkü regex'ler sadece sınırlı bir otomatadan daha fazlasıdır, regexp'de sadece bir HTML ayrıştırıcısını rastgele bir sayfayı değil, rastgele bir html'i ayrıştırmak için ve regexes kesinlikle 1000 kat okunamaz hale gelir.
Smit Johnth

1
Hey Andy, bahsettiğiniz davaları destekleyen bir ifade bulmaya zaman ayırdım. stackoverflow.com/a/40095824/1204332 Ne düşündüğünüzü bana bildirin! :)
Ivan Chaer

2
Bu cevap akıl yürütme edilir yolu modası geçmiş ve başlangıçta yana daha da az bugün (ı etmedi düşünüyorum) geçerlidir. (Alıntı: "Basit, hızlı veya kirli bir şey yapıyorsanız ...").
Sz.

16

İki hızlı neden:

  • kötü niyetli girdilere dayanabilecek bir normal ifade yazmak zordur; önceden oluşturulmuş bir araç kullanmaktan çok daha zor
  • kaçınılmaz olarak sıkışıp kalacağınız saçma işaretleme ile çalışabilecek bir normal ifade yazmak zordur; önceden oluşturulmuş bir araç kullanmaktan çok daha zor

Normal ifadelerin ayrıştırma için uygunluğuna ilişkin olarak: bunlar uygun değildir. Çoğu dili ayrıştırmak için ihtiyaç duyduğunuz tür regexleri gördünüz mü?


2
Vay? 2+ yıldan sonra bir düşüş? Herkesin merak etmesi durumunda, "Teorik olarak imkansız" demedim çünkü soru açıkça "doğru" değil, "çabuk ve kirli" diye sordu. OP, teorik olarak imkansız olan bölgeyi kapsayan ve hâlâ tatmin edici olmayan cevapları açıkça okudu.
Hank Gay

1
5+ yıldan sonra bir oy verin. :) Downvote'u neden almış olabileceğinize gelince, söylemek için nitelikli değilim, ama kişisel olarak, kapanış retorik sorusundan ziyade bazı örnekler veya açıklamalar görmek isterdim.
Adam Jensen

3
Esasen, nakliye ürünlerinde veya dahili araçlarda yapılan tüm hızlı ve kirli html ayrıştırma, boşluklu bir güvenlik deliği veya gerçekleşmeyi bekleyen bir hata haline gelir. Gusto ile caydırılmalıdır. Bir regex kullanabiliyorsa, uygun bir html ayrıştırıcı kullanılabilir.
Monica'yı

16

Ayrıştırma ile ilgili olarak, girdilerin jetonlara ayrıldığı "sözcüksel analiz" (lexer) aşamasında düzenli ifadeler yararlı olabilir. Gerçek "ayrıştırma ağacı oluşturma" aşamasında daha az kullanışlıdır.

Bir HTML ayrıştırıcı için, yalnızca iyi biçimlendirilmiş HTML'yi kabul etmesini beklerim ve bu, normal bir ifadenin yapabileceklerinin dışında yetenekler gerektirir ("sayamazlar" ve belirli sayıda açılış öğesinin aynı sayı ile dengelendiğinden emin olurlar) kapatma elemanları).


8

Tarayıcıların oldukça liberal bir şekilde ele alacağı HTML'yi "sıkıştırmanın" birçok yolu olduğundan, düzenli ifadelerle tüm durumları kapsamak için tarayıcının liberal davranışını yeniden oluşturmak oldukça çaba gerektirecektir, bu nedenle normal ifadeniz bazı özel durumlarda kaçınılmaz olarak başarısız olacaktır. ve sisteminizde ciddi güvenlik boşlukları oluşmasına neden olabilir.


1
Çok doğru, dışarıda HTML çoğunluğu korkunç gibi görünüyor. Başarısız bir düzenli ifadenin nasıl ciddi güvenlik açıkları oluşturabileceğini anlamıyorum. Bir örnek verebilir misin?
ntownsend

4
ntownsend: Örneğin, tüm komut dosyası etiketlerini HTML'den çıkardığınızı düşünüyorsunuz, ancak normal ifadeniz özel bir vakayı (örneğin, IE6 üzerinde çalışır) karşılayamıyor: Boom, bir XSS vulgrability var!
Tamas Czinege

1
Gerçek dünyadaki örneklerin çoğu bu yorumlara sığmayacak kadar karmaşık olduğu için bu kesinlikle varsayımsal bir örnektir, ancak konuyla ilgili hızlı bir şekilde çalışarak birkaçını bulabilirsiniz.
Tamas Czinege

3
Güvenlik açısından bahsetmek için +1. Eğer tüm internet ile arayüzey hacky "çoğu zaman çalışır" kodu yazmak göze alamaz.
j_random_hacker

7

Sorun, HTML ve regex ile ilgili bir soru soran çoğu kullanıcının, çalışan kendi regex'i bulamadıkları için bunu yapmasıdır. Daha sonra bir DOM veya SAX ayrıştırıcısı veya benzeri bir şey kullanırken her şeyin daha kolay olup olmayacağını düşünmek gerekir. XML benzeri belge yapılarıyla çalışmak için optimize edilmiş ve oluşturulmuştur.

Elbette, düzenli ifadelerle kolayca çözülebilecek sorunlar var. Ama vurgu kolayca yatıyor .

Yalnızca http://.../normal ifadelerle iyi görünen tüm URL'leri bulmak istiyorsanız . Ancak, 'mylink' sınıfına sahip bir a-Elementindeki tüm URL'leri bulmak istiyorsanız, muhtemelen uygun bir ayrıştırıcı kullanmanız daha iyi olur.


6

Düzenli ifadeler, iç içe bir etiket yapısını işlemek için tasarlanmamıştır ve gerçek HTML ile elde edebileceğiniz olası tüm uç durumları işlemek en iyi ihtimalle (en kötü ihtimalle, imkansız) karmaşıktır.


6

Cevabın hesaplama teorisinde olduğuna inanıyorum. Bir dilin normal ifade kullanılarak ayrıştırılabilmesi için "normal" ( bağlantı ) tanımına göre olması gerekir . HTML, normal bir dil için bir dizi kriteri karşılamadığı için normal bir dil değildir (html kodunda bulunan birçok iç içe yerleştirme düzeyi ile ilgilidir). Eğer hesaplama teorisi ile ilgileniyorsanız bu kitabı tavsiye ederim .


1
Aslında o kitabı okudum. HTML'nin bağlamsız bir dil olduğu bana hiç gelmedi.
ntownsend

4

Bu ifade, HTML öğelerinden öznitelikleri alır. Destekler:

  • alıntılanmamış / alıntılanmış özellikler,
  • tek / çift tırnak,
  • özniteliklerin içinde kaçan alıntılar,
  • çevresindeki boşluklar eşittir işaretler,
  • herhangi bir sayıda özellik,
  • yalnızca etiketlerin içindeki özellikleri kontrol edin,
  • yorumlardan kaç ve
  • bir özellik değeri içindeki farklı tırnak işaretlerini yönetebilir.

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

Şuna bir bak . Demoda olduğu gibi "gisx" bayraklarıyla daha iyi çalışır.


1
Bu çok ilginç. Okunabilir değil, muhtemelen hata ayıklaması zor ama yine de: Etkileyici çalışma!
Eric Duminil

Bu hala belirsiz bir şekilde HTML'nin iyi biçimlendirildiğini varsayar. İçerik eşleşmesi olmadan bu, görünen URL'leri, genellikle bir <script>etiket içindeki JavaScript kodunda olduğu gibi, genellikle eşleştirmek istemediğiniz bağlamlarda eşleştirecektir .
tripleee

4

HTML / XML biçimlendirme ve içeriğe ayrılmıştır. Normal ifade yalnızca sözlüksel etiket ayrıştırma işlemi yaparken kullanışlıdır. Sanırım içeriği çıkarabilirsin. Bir SAX ayrıştırıcı için iyi bir seçim olacaktır. Etiketler ve içerik, öğelerin iç içe yerleştirilmesinin / kapatılmasının takip edilebildiği kullanıcı tanımlı bir işleve iletilebilir.

Etiketleri ayrıştırmakla birlikte, normal ifade ile yapılabilir ve bir belgedeki etiketleri çıkarmak için kullanılabilir.

Yıllarca süren testlerde, tarayıcıların etiketleri iyi ayrıştırmalarının ve kötü şekillendirilmesinin sırrını buldum.

Normal elemanlar bu formla ayrıştırılır:

Bu etiketlerin çekirdeği bu normal ifadeyi kullanıyor

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

Bunu [^>]?alternatiflerden biri olarak göreceksiniz . Bu, kötü biçimlendirilmiş etiketlerdeki dengesiz tekliflerle eşleşir.

Aynı zamanda, tüm kötülüklerin düzenli ifadelere tek köküdür . Kullanılma şekli, açgözlü, eşleşmesi gereken nicelikli kabı karşılamak için bir çarpma tetikleyecektir.

Pasif olarak kullanılırsa, asla bir sorun yoktur. Ancak, bir şeyi istenen bir özellik / değer çifti ile serpiştirerek eşleştirmeye zorlarsanız ve geri izlemeye karşı yeterli koruma sağlamazsanız, bu kontrolden çıkmış bir kabus olur.

Bu sadece düz eski etiketler için genel formdur. Uyarı [\w:]etiket adı temsil eden? Gerçekte, etiket adını temsil eden yasal karakterler Unicode karakterlerin inanılmaz bir listesidir.

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

Devam ederken, TÜM etiketleri ayrıştırmadan belirli bir etiketi arayamayacağınızı da görüyoruz . Demek istediğim, ama (* SKIP) (* FAIL) gibi fiillerin bir kombinasyonunu kullanmak zorunda kalacaktı, ancak yine de tüm etiketler ayrıştırılmalıdır.

Bunun nedeni, etiket sözdiziminin diğer etiketlerin vb. İçine gizlenmiş olabilmesidir.

Bu nedenle, tüm etiketleri pasif olarak ayrıştırmak için aşağıdaki gibi bir normal ifadeye ihtiyaç vardır. Bu özel içerik de görünmez içerikle eşleşiyor .

Yeni HTML veya xml veya başka herhangi bir yeni yapı geliştirdikçe, alternatiflerden biri olarak ekleyin.


Web sayfası notu - Hiç sorun yaşamadığım bir web sayfası (veya xhtml / xml) görmedim
. Birini bulursan bana haber ver.

Performans notu - Hızlı. Bu gördüğüm en hızlı etiket ayrıştırıcısıdır
(daha hızlı olabilir, kim bilir).
Birkaç özel versiyonum var. Ayrıca kazıyıcı olarak mükemmeldir
(eğer el tipi iseniz).


Tam ham regex

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

Biçimlendirilmiş görünüm

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

Yine de "bağlıdır". Normal ifadelerin, burada verilen tüm nedenlerden dolayı HTML'yi gerçek doğrulukla ayrıştırmadığı ve ayrıştıramadığı doğrudur. Bununla birlikte, yanlış anlamanın sonuçları (iç içe etiketlerin işlenmemesi gibi) küçükse ve regex'ler ortamınızda (Perl'i hacklediğinizde olduğu gibi) süper uygunsa, devam edin.

Sitenize bağlantı veren web sayfalarını - belki de bir Google bağlantı aramasıyla bulduğunuz - ayrıldığınızı ve bağlantınızı çevreleyen bağlam hakkında genel bir fikir edinmek için hızlı bir yol istediğinizi varsayalım. Spam gibi bir bağlantı oluşturmanız konusunda sizi uyarabilecek küçük bir rapor çalıştırmaya çalışıyorsunuz.

Bu durumda, bazı belgelerin yanlış hazırlanması büyük bir sorun olmayacaktır. Sizden başka kimse hataları görmeyecek ve eğer çok şanslıysanız, bireysel olarak takip edebileceğiniz kadar az olacaktır.

Sanırım bunun bir ödünleşme olduğunu söylüyorum. Bazen doğruluk kritik değilse, doğru bir ayrıştırıcıyı uygulamak veya kullanmak - olabildiğince kolay - sorun yapmaya değmeyebilir.

Sadece varsayımlarınıza dikkat edin. Örneğin, herkese açık olarak gösterilecek bir şeyi ayrıştırmaya çalışıyorsanız normal ifade kısayolunun geri tepebileceği birkaç yolu düşünebilirim.


3

HTML'den bazı bilgileri ayrıştırmak için normal bir ifade kullanmanın doğru yol olduğu kesin durumlar vardır - bu, belirli duruma çok bağlıdır.

Yukarıdaki fikir birliği, genel olarak kötü bir fikir olduğudur. Bununla birlikte, HTML yapısı biliniyorsa (ve değişmesi olası değilse), hala geçerli bir yaklaşımdır.


3

HTML'nin kendisi düzenli olmasa da , baktığınız bir sayfanın bölümlerinin düzenli olabileceğini unutmayın .

Örneğin, <form>etiketlerin iç içe yerleştirilmesi bir hatadır ; web sayfası düzgün çalışıyorsa, a almak için normal bir ifade kullanmak <form>tamamen makul olacaktır.

Son zamanlarda sadece Selenyum ve düzenli ifadeler kullanarak bazı web kazıma yaptım. İstediğim veriler kondu çünkü yakalanmadı <form>ve (ben bile güvenebileceğim böylece basit bir tablo biçiminde koymak <table>, <tr>ve <td>- aslında son derece sıradışı olan olmayan iç içe olmak). Bir dereceye kadar düzenli ifadeler neredeyse gerekliydi, çünkü erişmem gereken yapının bir kısmı yorumlar tarafından sınırlandırıldı. (Güzel Çorba size yorum yapabilir, ancak Güzel Çorba kullanarak kapmak <!-- BEGIN -->ve <!-- END -->bloklamak zor olurdu .)

İç içe geçmiş tablolar hakkında endişelenmem gerekirse, yaklaşımım işe yaramazdı! Güzel Çorba'ya geri dönmek zorunda kalırdım. Ancak o zaman bile, bazen ihtiyacınız olan parçayı almak için düzenli bir ifade kullanabilir ve daha sonra oradan ayrıntıya inebilirsiniz.


2

Aslında, regex ile HTML ayrıştırma PHP'de mükemmel bir şekilde mümkündür. Yuvalanmış etiketleri almak için her seferinde ungreedy belirteçleri kullanarak normal ifadeyi strrposbulmak <ve tekrarlamak için kullanarak tüm dizeyi geriye doğru ayrıştırmanız yeterlidir. Süslü ve büyük şeylerde çok yavaş değil, ancak web sitem için kendi kişisel şablon düzenleyicim için kullandım. Aslında HTML'yi ayrıştırmıyordum, ancak veri tablolarını görüntülemek için veritabanı girişlerini sorgulamak için yaptığım birkaç özel etiket ( <#if()>etiketim bu şekilde özel girişleri vurgulayabilir). Burada ve orada sadece birkaç kendi içinde oluşturulan etiketleri (içinde çok XML olmayan veriler ile) bir XML ayrıştırıcı gitmek hazır değildi.

Bu nedenle, bu soru oldukça ölü olsa da, yine de bir Google aramasında ortaya çıkıyor. Ben okudum ve "meydan kabul kabul" düşündüm ve her şeyi değiştirmek zorunda kalmadan basit kodumu düzeltmeyi bitirdi. Benzer bir sebep arayan herkese farklı bir fikir sunmaya karar verdim. Ayrıca son cevap 4 saat önce yayınlanmıştır, bu yüzden bu hala sıcak bir konudur.


2
KORKUNÇ bir fikir önerdiği için -1. Etiket ve kapanış açısı köşeli ayraç arasında boşluk olduğunu düşündünüz mü? (Örn. <tag >) Yorum yapılan kapanış etiketlerini düşündünüz mü? (Örn. <tag> <!-- </tag> -->) CDATA'yı düşündünüz mü? Tutarsız vaka etiketlerini düşündünüz mü? (Örneğin, <Tag> </tAG>) düşündünüz mü bu sıra?
rmunn

1
Birkaç özel etiketiniz söz konusu olduğunda, evet, normal ifadeler iyi çalışır. Bu nedenle, bunları kullanmanız sizin özel durumunuzda bir hata değildi . Bu HTML değil, ve "PHP'de regex ile HTML ayrıştırma mükemmel mümkündür" demek sadece düz yanlış ve KORKUNÇ bir fikir. Gerçek HTML'nin tutarsızlıkları (ve listelediğim birkaçdan fazla yol var), neden gerçek HTML'yi normal ifadelerle ayrıştırmamanız gerektiğidir. Bu soruya verilen diğer tüm yanıtların yanı sıra yukarıdaki diğer yorumumda da bağlantı kurduğum soruya bakın.
rmunn

2
PHP bir tur-tam dildir, bu yüzden hiç de yanlış değildir. HTML'yi ayrıştırma da dahil olmak üzere, hesaplamalı olarak mümkün olan her şey mümkündür. Etiketlerdeki boşluklar ASLA bir sorun değildi ve o zamandan beri etiket öğelerini sırayla listelemek için uyarladım. Kullanımım otomatik olarak düzeltilmiş etiketleri tutarsız kasa, ilk aşamada soyulmuş yorumlanmış şeyler ve daha sonraki bazı eklemelerden sonra her türlü etiket kolayca eklenebilir (büyük / küçük harf duyarlı olmasına rağmen) kendi seçimime göre). Ve eminim CDATA aslında bir HTML elemanı değil, bir XML elemanıdır.
Deji

2
Eski yöntemim (burada açıkladığım) oldukça verimsizdi ve son zamanlarda birçok içerik düzenleyicisinin yeniden yazılmasına başladım. Bu şeyleri yapmaya gelince, sorun mesele değildir; en iyi yol her zaman ana husustur. Gerçek cevap "PHP bunu yapmanın KOLAY bir yolu yoktur". HİÇBİRİ PHP'de bunu yapmanın bir yolu olmadığını ya da korkunç bir fikir olduğunu söylüyor, ancak dürüstçe hiç denemediğim regex ile imkansız olduğunu söylüyor, ancak cevabımdaki en büyük kusur, sorunun regex'e atıfta bulunduğu varsayıldı. PHP bağlamında, durum böyle değildir.
Deji

2

Ben de bunun için bir regex elimi denedim. Çoğunlukla bir sonraki HTML etiketiyle eşleştirilen içerik parçalarını bulmak için yararlıdır ve eşleşen yakın etiketleri aramaz , ancak yakın etiketleri alır. Bunları kontrol etmek için bir yığını kendi dilinizde yuvarlayın.

'Sx' seçenekleriyle kullanın. 'g' şanslı hissediyorsanız:

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

Bu Python için tasarlanmıştır (diğer diller için işe yarayabilir, denememiş olabilir, pozitif ileriye dönük, negatif geriye dönük ve geri referanslar kullanır). Destekler:

  • Etiketi Aç - <div ...>
  • Etiketi Kapat - </div>
  • Yorum Yap - <!-- ... -->
  • CDATA - <![CDATA[ ... ]]>
  • Kendiliğinden Kapanan Etiket - <div .../>
  • İsteğe Bağlı Özellik Değerleri - <input checked>
  • Sıralanmamış / Alıntılanmış Özellik Değerleri - <div style='...'>
  • Tek / Çift Tırnaklar - <div style="...">
  • Kaçan Tırnaklar - <a title='John\'s Story'>
    (bu gerçekten geçerli bir HTML değil, ama iyi biriyim)
  • Etrafındaki Boşluklar İşaretlere Eşittir - <a href = '...'>
  • İlginç Bitler İçin Adlandırılmış Yakalamalar

Ayrıca değil bir unuttuğunuzda gibi hatalı oluşturulmuş etiketlere tetikleme konusunda oldukça iyi <veya >.

Regex lezzetiniz tekrarlanan adlandırılmış yakalamaları destekliyorsa, o zaman altınsınız, ancak Python re(regex'in yaptığını biliyorum, ancak vanilya Python kullanmam gerekiyor). İşte elde ettiğiniz:

  • content- Bir sonraki etikete kadar tüm içerik. Bunu dışarıda bırakabilirsin.
  • markup - İçinde her şeyin olduğu etiketin tamamı.
  • comment - Bu bir yorumsa, yorum içeriği.
  • cdata- A ise <![CDATA[...]]>, CDATA içeriği.
  • close_tag- Yakın etiket ( </div>) ise etiket adı.
  • tag- Açık bir etiketse ( <div>), etiket adı.
  • attributes- Etiketteki tüm özellikler. Tekrarlanan gruplar almazsanız tüm nitelikleri almak için bunu kullanın.
  • attribute - Tekrarlanan her özellik.
  • attribute_name - Her özellik adı tekrarlanır.
  • attribute_value- Her öznitelik değeri tekrarlanır. Buna, alıntılanmışsa tırnak işaretleri de dahildir.
  • is_self_closing- Bu /kendiliğinden kapanan bir etiketse, aksi takdirde hiçbir şey değildir.
  • _qve _v- Bunları dikkate almayın; dahili referanslar için kullanılırlar.

Normal ifade motorunuz tekrarlanan adlandırılmış yakalamaları desteklemiyorsa, her bir özelliği almak için kullanabileceğiniz bir bölüm vardır. Sadece o regex çalıştırmak attributesher almak için gruptan attribute, attribute_nameve attribute_valuebunun dışında.

Burada demo: https://regex101.com/r/mH8jSu/11


1

Normal ifadeler, HTML gibi bir dil için yeterince güçlü değildir. Elbette, düzenli ifadeleri kullanabileceğiniz bazı örnekler var. Ancak genel olarak ayrıştırma için uygun değildir.


0

Sen orada senin zihniyetin çok benziyor ... biliyorum CAN NOT bunu ve ben çitin her iki tarafında herkesin doğru ve yanlış olduğunu düşünüyorum. Sen CAN bunu, ama biraz daha adil buna karşı bir regex çalışan daha işleme alır. Al bu bir örnek olarak (Bir saat bu iç yazdım). HTML'nin tamamen geçerli olduğunu varsayar, ancak yukarıda belirtilen regex'i uygulamak için hangi dili kullandığınıza bağlı olarak, başarılı olmasını sağlamak için HTML'nin bazı düzeltmelerini yapabilirsiniz. Örneğin, orada olması gerekmeyen kapanış etiketlerinin kaldırılması: </img>örneğin. Ardından, onları eksik olan öğelere kapatma tekli HTML eğik çizgisini ekleyin.

Bunu [x].getElementsByTagName(), örneğin JavaScript'inki gibi HTML öğesi alma işlemini gerçekleştirmeme izin verecek bir kütüphane yazma bağlamında kullanırdım . Normal ifadenin DEFINE bölümüne yazdığım işlevselliği eklerdim ve bir kerede bir element ağacının içine adım atmak için kullanırdım.

Peki, bu HTML'yi doğrulamak için son% 100 cevap olacak mı? Hayır. Ama bu bir başlangıç ​​ve biraz daha çalışma ile yapılabilir. Bununla birlikte, bunu bir regex yürütmesi içinde yapmaya çalışmak pratik ya da verimli değildir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.