Düzenli ifade kullanmıyorlarsa HTML ayrıştırmaları nasıl çalışır?


96

Her gün bir HTML dizgisinden bir şeyin nasıl ayrıştırılacağını veya çıkarılacağını soran sorular görüyorum ve ilk cevap / yorum her zaman "Öfkeyi hissetmemek için HTML'yi ayrıştırmak için RegEx'i kullanmayın!" (bu son kısım bazen ihmal edilir).

Bu benim için oldukça kafa karıştırıcı, her zaman genel olarak herhangi bir karmaşık dizeyi çözümlemenin en iyi yolunun düzenli bir ifade kullanmak olduğunu düşünmüşümdür. Peki bir HTML ayrıştırıcı nasıl çalışır? Ayrıştırmak için normal ifadeler kullanmıyor mu?

Normal bir ifade kullanmak için özel bir argüman, her zaman bir ayrıştırma alternatifinin olmamasıdır (JavaScript gibi, DOMDocument evrensel olarak kullanılabilen bir seçenek değildir). Örneğin jQuery, bir HTML dizesini DOM düğümlerine dönüştürmek için bir düzenli ifade kullanarak gayet iyi yönetiyor gibi görünüyor.

Bunu CW yapıp yapmama konusunda emin değilim, cevaplanmasını istediğim gerçek bir soru ve gerçekten bir tartışma başlığı olmayı amaçlamıyorum.


Ayrıştırma ve html-ayrıştırma eklemek için yeniden etiketlendi - @Andy E, umarım senin için sorun olmaz - bunun yararlı olacağını düşündüm.
JXG

@JXG: Benim için sorun değil, teşekkürler :-)
Andy E

Yanıtlar:


65

Genellikle bir belirteç kullanarak. Taslak HTML5 spesifikasyonu, "gerçek dünya HTML" yi işlemek için kapsamlı bir algoritmaya sahiptir .


1
İyi bul ... alıntı yapmak için "Bu durumları ele almak için, ayrıştırıcıların başlangıçta sıfıra ayarlanması gereken bir komut dosyası iç içe geçme seviyesi ve başlangıçta yanlış olarak ayarlanması gereken bir ayrıştırıcı duraklama bayrağı vardır." - Başka bir deyişle, bunu kendiniz yinelemeli ve birçok özel mantığa sahip olmalısınız: P
Timothy Khouri

1
Olumlu oy verin. Bazı teknolojiler yerine algoritmik karmaşıklığı vurgulamak daha iyidir.
Arnis Lapsa

1
Bunu kendi kendinize birçok özel mantıkla yinelemek o kadar da iyi bir fikir değil. Mümkünse standart algoritmayı destekleyen bir kitaplık kullanın. ör. search.cpan.org/~tobyink/HTML-HTML5-Parser-0.03/lib/HTML/HTML5/… / code.google.com/p/html5lib
Quentin

8
HTML ayrıştırıcılarla ilgili temel sorun, bir hatayla karşılaştığınızda, "Ayrıştırma hatasını" söyleyip bunu böyle bırakmanızın uygun olmamasıdır. Tuhaflıklar moduna giriyorsunuz ve uyuşmayan etiketler, [{]} stil taraması ve her türlü tuhaflık dahil olmak üzere karşılaştığınız karmaşadan elinizden gelenin en iyisini yapmaya çalışıyorsunuz ve sonucu olabildiğince iyi ve kaçınılmaz hale getirmeye çalışıyorsunuz. başarısızlık en az acı verici ... bu normal ifadelerle yapabileceğiniz bir şey değil.
SF.

7
@Timothy K: 'Not: Bu algoritmanın öğelerin ebeveynleri değiştirmesine neden olma şekli nedeniyle, "evlat edinme kurumu algoritması" olarak adlandırılmıştır ("ensest algoritması" dahil yanlış iç içe geçmiş içerikle başa çıkmak için diğer olası algoritmaların aksine, "gizli ilişki algoritması" ve "Heisenberg algoritması"). '
JXG

133

Peki bir HTML ayrıştırıcı nasıl çalışır? Ayrıştırmak için normal ifadeler kullanmıyor mu?

Hayır.

Beyninizde bir hesaplama teorisi kursuna geri dönerseniz, bir derse veya bir derleyici kursuna veya benzer bir şeye gittiyseniz, farklı dil türleri ve hesaplama modelleri olduğunu hatırlayabilirsiniz. Tüm ayrıntılara girecek nitelikte değilim, ancak birkaç önemli noktayı sizinle birlikte gözden geçirebilirim.

En basit dil ve hesaplama türü (bu amaçlar için) normal bir dildir. Bunlar normal ifadelerle oluşturulabilir ve sonlu otomatlarla tanınabilir. Temel olarak, bu, bu dillerdeki dizgelerin "ayrıştırılmasının" durum kullandığı anlamına gelir, ancak yardımcı belleği kullanmaz. HTML kesinlikle normal bir dil değildir. Bunu düşünürseniz, etiket listesi keyfi bir şekilde derinlemesine yerleştirilebilir. Örneğin, tablolar tablo içerebilir ve her tablo çok sayıda iç içe geçmiş etiket içerebilir. Normal ifadelerle, bir çift etiket seçebilirsin, ama kesinlikle keyfi olarak iç içe yerleştirilmiş hiçbir şey olamaz.

Normal olmayan klasik bir basit dil, parantezlerle doğru şekilde eşleşir. Yapabildiğiniz kadar deneyin, her zaman işe yarayacak bir düzenli ifade (veya sonlu otomat) asla oluşturamazsınız. Yuvalama derinliğini takip etmek için hafızaya ihtiyacınız var.

Bellek yığınına sahip bir durum makinesi, hesaplama modelinin bir sonraki gücüdür. Buna aşağı itmeli otomat denir ve bağlamdan bağımsız gramerler tarafından oluşturulan dilleri tanır. Burada, doğru eşleştirilmiş parantezleri tanıyabiliriz - aslında bir yığın, onun için mükemmel bir bellek modelidir.

Peki, bu HTML için yeterince iyi mi? Üzgünüm hayır. Belki de tüm etiketlerin her zaman mükemmel bir şekilde sıralandığı, dikkatlice doğrulanmış XML için olabilir. Gerçek dünya HTML'sinde, gibi parçacıkları kolayca bulabilirsiniz <b><i>wow!</b></i>. Bu açıkça yuvalanmaz, bu yüzden doğru şekilde ayrıştırmak için bir yığın yeterince güçlü değildir.

Bir sonraki hesaplama düzeyi, genel gramerler tarafından üretilen ve Turing makineleri tarafından tanınan dillerdir. Bu genellikle var olan en güçlü hesaplama modeli olarak kabul edilir - belleği herhangi bir yerde değiştirilebilen yardımcı belleğe sahip bir durum makinesi. Programlama dillerinin yapabileceği şey budur. Bu, HTML'nin yaşadığı karmaşıklık düzeyidir.

Buradaki her şeyi tek bir cümleyle özetlemek gerekirse: genel HTML'yi ayrıştırmak için normal bir ifadeye değil gerçek bir programlama diline ihtiyacınız var.

HTML, diğer dillerin çözümlendiği şekilde çözümlenir: sözcük oluşturma ve ayrıştırma. Sözcük oluşturma adımı, bireysel karakterlerin akışını anlamlı belirteçlere böler. Ayrıştırma adımı, durumları ve belleği kullanarak simgeleri, üzerinde işlem yapılabilecek mantıksal olarak tutarlı bir belge halinde birleştirir.


22

Normal ifadeler yalnızca bir ayrıştırıcı biçimidir. Dürüstlükten iyiye doğru bir HTML ayrıştırıcısı , metni doğru bir şekilde yorumlamak için yinelemeli iniş , tahmin ve diğer birkaç tekniği kullanarak normal ifadelerde ifade edilenden çok daha karmaşık olacaktır . Gerçekten içine girmek istiyorsan, lex & yacc ve benzeri araçları inceleyebilirsin.

HTML ayrıştırması için normal ifadelerin kullanılmasına karşı yasaklama muhtemelen şu şekilde daha doğru yazılmalıdır: "HTML'yi ayrıştırmak için saf normal ifadeler kullanmayın ..." (öfke hissetmemek için) "... ve sonuçları dikkatli bir şekilde ele alın." Belirli belirli hedefler için, bir normal ifade tamamen yeterli olabilir, ancak normal ifadenizin sınırlamalarının farkında olmak için çok dikkatli olmanız ve ayrıştırdığınız metnin kaynağına uygun olduğu kadar dikkatli olmanız gerekir (ör. kullanıcı girişi, gerçekten çok dikkatli olun).


+1, iyi bir cevap. İtiraf etmeliyim ki, daha önce HTML'nin kontrolünde olmadığım zamanlarda bile normal ifadeleri kullandım, ancak herkese açık olarak yayınlanan herhangi bir uygulamada kullanmadım. Ben de "gazabı hissettim" çünkü saftı. Ama bu uzun zaman önceydi :-)
Andy E

6

HTML Ayrıştırma, doğrusal bir metnin bir ağaç yapısına dönüştürülmesidir. Normal ifadeler genellikle ağaç yapılarını işleyemez. Bir sonraki jetonu almak için her noktada ihtiyaç duyduğunuz normal ifade her zaman değişir. Bir ayrıştırıcıda normal ifadeler kullanabilirsiniz, ancak her olası ayrıştırma durumu için tam bir düzenli ifade dizisine ihtiyacınız olacaktır.


2

% 100 bir çözüme sahip olmak istiyorsanız: HTML'de karakter karakter yinelenen kendi özel kodunuzu yazmanız gerekir ve mevcut düğümü durdurup başlatmanız gerekip gerekmediğini belirlemek için muazzam miktarda mantığa sahip olmanız gerekir. Sonraki.

Bunun nedeni, bunun geçerli HTML olmasıdır:

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

Ama bu da öyle:

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

"% 90 çözüm" nde sorun yoksa: O zaman bir belgeyi yüklemek için bir XML ayrıştırıcı kullanmak iyidir. Veya Regex kullanarak (içeriğin ustası iseniz xml daha kolaydır).


4
XML ayrıştırıcı daha çok% 1'lik bir çözüme benzer. İyi biçimlendirilmiş XML olan HTML belgelerinin sayısı azdır.
Quentin

4
Evet, onlar ... bir şeyleri yayınlamaya çalışabileceğiniz için, kelimenin tam anlamıyla "karakter karakter" almazlar. Ama benim açımdan, kendi ayrıştırıcınızı yazmanız gerektiğidir. Yeni yaşlı programcılar bu tür kod yazmaya alışık değiller ... "HtmlDocumentUtility.Load" ve bunun gibi şeylere alışkınız :)
Timothy Khouri

4
@Andy E: Normal ifadeler sihir değildir, aynı zamanda başka herhangi bir tür ayrıştırma veya başka herhangi bir dizge işlevi gibi karakter karakter de çalışırlar.
Bart van Heukelom

1
BTW: İlk örneğiniz sadece "yarı geçerli HTML" değil. Aslında geçerli HTML 4.01 Katı. Bunu doğrulamak için örneğin W3C doğrulayıcısını kullanabilirsiniz. Kapanış etiketi <li> için resmi olarak isteğe bağlıdır (HTML 4 spesifikasyonuna bakın).
sleske

2
@Bart: İyi nokta, bazen beynim tüm mantığı unutuyor ve işlerin sihirle yürüdüğünü düşünüyor.
Andy E
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.