HTML dizesini JS ile ayrıştırma


261

Bir çözüm aradım ama hiçbir şey ilgili değildi, işte benim sorunum:

HTML metni içeren bir dizeyi ayrıştırmak istiyorum. JavaScript ile yapmak istiyorum.

Bu kütüphane denedim ama bir dize değil, geçerli sayfamın HTML ayrıştırdığı görünüyor. Çünkü aşağıdaki kodu denediğimde sayfamın başlığını değiştiriyor:

var parser = new HTMLtoDOM("<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>", document);

Amacım, tıpkı bir dize gibi okuduğum bir HTML harici sayfasından linkleri çıkarmak.

Bunu yapmak için bir API biliyor musunuz?



1
Bağlantılı yinelenen yöntem belirli bir dizeden bir HTML belgesi oluşturur. Ardından, doc.getElementsByTagName('a')bağlantıları (hatta çift doc.links) okumak için kullanabilirsiniz .
Rob W

: Eğer React.js gibi bir çerçeve kullanıyorsanız o zaman gibi çerçeveye özgü olan bunun yollarını olabileceğini belirterek It değerinde stackoverflow.com/questions/23616226/...
Mike Lyons

Yanıtlar:


374

Bir kukla DOM öğesi oluşturun ve dizeyi buna ekleyin. Ardından, herhangi bir DOM öğesi gibi değiştirebilirsiniz.

var el = document.createElement( 'html' );
el.innerHTML = "<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>";

el.getElementsByTagName( 'a' ); // Live NodeList of your anchor elements

Düzenleme: hayranları memnun etmek için bir jQuery cevap ekleyerek!

var el = $( '<div></div>' );
el.html("<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>");

$('a', el) // All the anchor elements

9
Sadece bir not: Bu çözümle, bir "alert (el.innerHTML)" yaparsam, <html>, <body> ve <head> etiketini kaybederim
sahne

2
Sorun: <frame> etiketinden bağlantılar almam gerekiyor. Ancak bu çözümle çerçeve etiketi silinir ...
aşaması

3
@stage Partiye biraz geç kaldım, ancak ve etiketlerini document.createElement('html');korumak için kullanabilmelisiniz . <head><body>
omninonsense

3
Görünüşe göre bir html öğesi içine bir html öğesi koyuyorsunuz
symbiont

6
Endişeleniyorum en iyi cevap olarak değerlendirildi. Aşağıdaki parse()çözüm daha yeniden kullanılabilir ve zariftir.
Justin

234

Oldukça basit:

var parser = new DOMParser();
var htmlDoc = parser.parseFromString(txt, 'text/html');
// do whatever you want with htmlDoc.getElementsByTagName('a');

MDN'ye göre, bunu kromda yapmak için XML olarak ayrıştırmanız gerekir:

var parser = new DOMParser();
var htmlDoc = parser.parseFromString(txt, 'text/xml');
// do whatever you want with htmlDoc.getElementsByTagName('a');

Şu anda webkit tarafından desteklenmiyor ve Florian'ın cevabını takip etmeniz gerekiyor ve çoğu durumda mobil tarayıcılarda çalıştığı bilinmiyor.

Düzenleme: Şimdi yaygın olarak desteklenmektedir


35
2016'da DOMParser'ın artık geniş çapta desteklendiğini belirtmek gerekir. caniuse.com/#feat=xml-serializer
aendrew

5
Belge miras yarattığı olur çünkü oluşturulan dokümandaki tüm göreli bağlantılar, kırık olduğunu fazlalaştı documentURLait windowdizesinin URL'den, hangi büyük olasılıkla farklılık.
ceving

2
Sadece birnew DOMParser kez çağırmanız ve daha sonra aynı nesneyi komut dosyanızın geri kalanında yeniden kullanmanız gerektiğini belirtmek gerekir .
Jack Giffin

1
Aşağıdaki parse()çözüm daha çok kullanılabilir ve HTML'ye özgüdür. Bununla birlikte, bir XML belgesine ihtiyacınız varsa bu hoş.
Justin

Bu ayrıştırılmış web sayfasını bir iletişim kutusunda veya başka bir şeyde nasıl görüntüleyebilirim? Bunun için çözüm bulamadım
Shariq Musharaf

18

DÜZENLEME: HTML, baş ve gövde kaldırıldığı için aşağıdaki çözüm yalnızca HTML "parçaları" içindir. Bu sorunun çözümü DOMParser'ın parseFromString () yöntemidir sanırım.


HTML parçaları için, burada listelenen çözümler çoğu HTML için çalışır, ancak bazı durumlarda çalışmaz.

Örneğin, ayrıştırmayı deneyin <td>Test</td>. Bu, div.innerHTML çözümü veya DOMParser.prototype.parseFromString veya range.createContextualFragment çözümü üzerinde çalışmaz. Td etiketi kaybolur ve yalnızca metin kalır.

Sadece jQuery bu durumu iyi işler.

Gelecekteki çözüm (MS Edge 13+) şablon etiketini kullanmaktır:

function parseHTML(html) {
    var t = document.createElement('template');
    t.innerHTML = html;
    return t.content.cloneNode(true);
}

var documentFragment = parseHTML('<td>Test</td>');

Eski tarayıcılar için jQuery'nin parseHTML () yöntemini bağımsız bir özdeyişe çıkardım - https://gist.github.com/Munawwar/6e6362dbdf77c7865a99


Eski tarayıcılarda da çalışan ileri uyumlu kod yazmak istiyorsanız , <template>etiketi çoklu doldurabilirsiniz . Çoklu doldurmanız gerekebilecek özel öğelere bağlıdır . Aslında sadece kullanmak isteyebilirsiniz webcomponents.js tek seferde polyfill özel öğelerle, şablonlar, gölge dom, vaatler ve birkaç diğer şeyler herkese.
Jeff Laughlin

13
var doc = new DOMParser().parseFromString(html, "text/html");
var links = doc.querySelectorAll("a");

4
Neden ön ek veriyorsunuz $? Belirtildiği gibi Ayrıca, bağlantılı ikişer , text/htmlçok iyi desteklenir ve bir polyfill kullanılarak uygulanabilir zorundadır değildir.
Rob W

1
Bu satırı bir projeden kopyaladım, javascript uygulamasında (kütüphanede değil) $ değişkenleri öneklemeye alışkınım. sadece bir kütüphane ile çatışmaktan kaçınmaktır. bu hemen hemen her değişkenin kapsamı içinde olduğu için çok yararlı değil ama eskiden faydalıydı. değişkenleri kolayca tanımlamaya da yardımcı olabilir.
Mathieu

1
Ne yazık ki DOMParserhiçbiri text/htmlkrom üzerinde çalışmaz , bu MDN sayfası geçici bir çözüm sağlar.
Jokester

Güvenlik notu: Bu, herhangi bir tarayıcı bağlamı olmadan yürütülür, böylece komut dosyaları çalışmaz. Güvenilmeyen girdiler için uygun olmalıdır.
Leif Arne Storset

6

HTML'yi Chrome ve Firefox'ta ayrıştırmanın en hızlı yolu Range # createContextualFragment:

var range = document.createRange();
range.selectNode(document.body); // required in Safari
var fragment = range.createContextualFragment('<h1>html...</h1>');
var firstNode = fragment.firstChild;

Varsa createContextualFragment kullanan ve aksi takdirde innerHTML'ye düşen bir yardımcı işlev oluşturmanızı öneririm.

Deney: http://jsperf.com/domparser-vs-createelement-innerhtml/3


Unutmayın, (basit) gibi innerHTML, bu bir <img>'s onerror.
Ry-

Bununla ilgili bir sorun, '<td> test </td>' gibi html'nin, dahili bir motorda dahili olarak kullanıldığı takdirde, document.body bağlamındaki td'yi yok sayacağı (ve yalnızca 'test' metin düğümü oluşturabileceği) .OTOH. o zaman doğru bağlam kullanılabilir olacaktır.
Munawwar

Ayrıca BTW, IE 11, createContextualFragment'ı destekler.
Munawwar

Chrome ya da Firefox - soru JS ile ayrıştırmak nasıl oldu
sea26.2

Güvenlik notu: bu, girişteki herhangi bir komut dosyasını yürütür ve bu nedenle güvenilmeyen girişler için uygun değildir.
Leif Arne Storset

6

Aşağıdaki işlev parseHTMLde geri döner:

  • bir Document dosyanız bir doküman ile başladığında.

  • a DocumentFragmentdosyanız bir dokümanla başlamadığında.


Kod:

function parseHTML(markup) {
    if (markup.toLowerCase().trim().indexOf('<!doctype') === 0) {
        var doc = document.implementation.createHTMLDocument("");
        doc.documentElement.innerHTML = markup;
        return doc;
    } else if ('content' in document.createElement('template')) {
       // Template tag exists!
       var el = document.createElement('template');
       el.innerHTML = markup;
       return el.content;
    } else {
       // Template tag doesn't exist!
       var docfrag = document.createDocumentFragment();
       var el = document.createElement('body');
       el.innerHTML = markup;
       for (i = 0; 0 < el.childNodes.length;) {
           docfrag.appendChild(el.childNodes[i]);
       }
       return docfrag;
    }
}

Nasıl kullanılır :

var links = parseHTML('<!doctype html><html><head></head><body><a>Link 1</a><a>Link 2</a></body></html>').getElementsByTagName('a');

IE8 üzerinde çalışmak için bunu alamadım. İşlevin ilk satırı için "Nesne bu özelliği veya yöntemi desteklemiyor" hatasını alıyorum. CreateHTMLDocument işlevinin var olduğunu düşünmüyorum
Sebastian Carroll

Kullanım durumunuz tam olarak nedir? HTML'yi ayrıştırmak istiyorsanız ve HTML'niz belgenizin gövdesine yönelikse, aşağıdakileri yapabilirsiniz: (1) var div = document.createElement ("DIV"); (2) div.innerHTML = işaretleme; (3) sonuç = div.childNodes; --- Bu size bir alt düğüm koleksiyonu verir ve sadece IE8'de değil, IE6-7'de bile çalışmalıdır.
John Slegers

Alternatif seçenek için teşekkürler, bunu tekrar yapmam gerekirse deneyeceğim. Şimdilik yukarıdaki JQuery çözümünü kullandım.
Sebastian Carroll

@SebastianCarroll IE8'in trimdizelerde yöntemi desteklemediğini unutmayın . Bkz. Stackoverflow.com/q/2308134/3210837 .
Diş Fırçası

2
@ Diş fırçası: IE8 desteği 2017'nin şafağında hala geçerli mi?
John Slegers

4

JQuery kullanmaya açıksanız, HTML dizelerinden ayrılmış DOM öğeleri oluşturmak için bazı güzel olanaklara sahiptir. Bunlar daha sonra normal yollarla sorgulanabilir, örn:

var html = "<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>";
var anchors = $('<div/>').append(html).find('a').get();

Düzenle - @ Florian'ın cevabını doğru gördüm. Bu tam olarak söylediği şey, ama jQuery ile.


4
const parse = Range.prototype.createContextualFragment.bind(document.createRange());

document.body.appendChild( parse('<p><strong>Today is:</strong></p>') ),
document.body.appendChild( parse(`<p style="background: #eee">${new Date()}</p>`) );


Yalnızca Nodeüst öğedeki geçerli çocuk Node(başlangıcı Range) ayrıştırılır. Aksi takdirde, beklenmedik sonuçlar ortaya çıkabilir:

// <body> is "parent" Node, start of Range
const parseRange = document.createRange();
const parse = Range.prototype.createContextualFragment.bind(parseRange);

// Returns Text "1 2" because td, tr, tbody are not valid children of <body>
parse('<td>1</td> <td>2</td>');
parse('<tr><td>1</td> <td>2</td></tr>');
parse('<tbody><tr><td>1</td> <td>2</td></tr></tbody>');

// Returns <table>, which is a valid child of <body>
parse('<table> <td>1</td> <td>2</td> </table>');
parse('<table> <tr> <td>1</td> <td>2</td> </tr> </table>');
parse('<table> <tbody> <td>1</td> <td>2</td> </tbody> </table>');

// <tr> is parent Node, start of Range
parseRange.setStart(document.createElement('tr'), 0);

// Returns [<td>, <td>] element array
parse('<td>1</td> <td>2</td>');
parse('<tr> <td>1</td> <td>2</td> </tr>');
parse('<tbody> <td>1</td> <td>2</td> </tbody>');
parse('<table> <td>1</td> <td>2</td> </table>');

Güvenlik notu: bu, girişteki herhangi bir komut dosyasını yürütür ve bu nedenle güvenilmeyen girişler için uygun değildir.
Leif Arne Storset

0

bu basit kod ile bunu yapabilirsiniz:

let el = $('<div></div>');
$(document.body).append(el);
el.html(`<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>`);
console.log(el.find('a[href="test0"]'));
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.