Parsing string HTML dengan JS

259

Saya mencari solusi tetapi tidak ada yang relevan, jadi inilah masalah saya:

Saya ingin mengurai string yang berisi teks HTML. Saya ingin melakukannya dalam JavaScript.

Saya mencoba perpustakaan ini tetapi tampaknya mem-parsing HTML halaman saya saat ini, bukan dari string. Karena ketika saya mencoba kode di bawah ini, ia mengubah judul halaman saya:

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);

Tujuan saya adalah untuk mengekstrak tautan dari halaman eksternal HTML yang saya baca seperti string.

Apakah Anda tahu API untuk melakukannya?

tahap
sumber
1
Metode pada duplikat tertaut membuat dokumen HTML dari string yang diberikan. Kemudian, Anda dapat menggunakannya doc.getElementsByTagName('a')untuk membaca tautan (atau bahkan doc.links).
Rob W
Perlu disebutkan bahwa jika Anda menggunakan kerangka kerja seperti React.js maka mungkin ada cara untuk melakukannya yang spesifik untuk kerangka kerja seperti: stackoverflow.com/questions/23616226/…
Mike Lyons
Apakah ini menjawab pertanyaan Anda? Strip HTML dari Teks JavaScript
Leif Arne Storset

Jawaban:

373

Buat elemen DOM dummy dan tambahkan string ke dalamnya. Kemudian, Anda dapat memanipulasi seperti elemen DOM.

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

Sunting: menambahkan jawaban jQuery untuk menyenangkan para penggemar!

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
Florian Margaine
sumber
9
Hanya sebuah catatan: Dengan solusi ini, jika saya melakukan "lansiran (el.innerHTML)", saya kehilangan tag <html>, <body> dan <head> ....
stage
2
Masalah: Saya perlu mendapatkan tautan dari tag <frame>. Tetapi dengan solusi ini, tag frame dihapus ...
tahap
3
@stage Saya agak terlambat ke pesta, tetapi Anda harus bisa menggunakannya document.createElement('html');untuk menyimpan <head>dan memberi <body>tag.
omninonsense
3
sepertinya Anda meletakkan elemen html di dalam elemen html
symbiont
6
Saya khawatir diangkat sebagai jawaban teratas. The parse()solusi di bawah ini lebih dapat digunakan kembali dan elegan.
Justin
233

Sederhana saja:

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

Menurut MDN , untuk melakukan ini di chrome Anda perlu mengurai XML seperti:

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

Saat ini tidak didukung oleh webkit dan Anda harus mengikuti jawaban Florian, dan itu tidak diketahui untuk bekerja dalam kebanyakan kasus di browser seluler.

Sunting: Sekarang didukung secara luas

Cilan
sumber
35
Perlu dicatat bahwa pada 2016 DOMParser sekarang didukung secara luas. caniuse.com/#feat=xml-serializer
aendrew
5
Patut dicatat bahwa semua tautan relatif dalam dokumen yang dibuat rusak, karena dokumen dibuat dengan mewarisi documentURLdari window, yang kemungkinan besar berbeda dari URL string.
ceving
2
Patut dicatat bahwa Anda hanya perlu menelepon new DOMParsersekali dan kemudian menggunakan kembali objek yang sama sepanjang sisa skrip Anda.
Jack Giffin
1
The parse()solusi di bawah ini lebih dapat digunakan kembali dan spesifik untuk HTML. Ini bagus jika Anda memerlukan dokumen XML.
Justin
Bagaimana saya bisa menampilkan halaman web yang diuraikan ini pada kotak dialog atau sesuatu? Saya tidak dapat menemukan solusi untuk itu
Shariq Musharaf
18

EDIT: Solusi di bawah ini hanya untuk "fragmen" HTML karena html, head, dan body dihapus. Saya kira solusi untuk pertanyaan ini adalah metode parseFromString () DOMParser.


Untuk fragmen HTML, solusi yang tercantum di sini berfungsi untuk sebagian besar HTML, namun untuk kasus tertentu itu tidak akan berfungsi.

Misalnya coba parsing <td>Test</td>. Yang ini tidak akan bekerja pada solusi div.innerHTML atau DOMParser.prototype.parseFromString atau range.createContextualFragment solution. Tag td hilang dan hanya teks yang tersisa.

Hanya jQuery yang menangani case itu dengan baik.

Jadi solusi masa depan (MS Edge 13+) adalah dengan menggunakan tag template:

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

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

Untuk peramban yang lebih lama, saya telah mengekstraksi parseHTML () metode jQuery menjadi intisari independen - https://gist.github.com/Munawwar/6e6362dbdf77c7865a99

Munawwar
sumber
Jika Anda ingin menulis kode yang kompatibel dengan forward yang juga berfungsi pada browser lama, Anda dapat melakukan polyfill pada <template>tag . Hal ini tergantung pada unsur-unsur kustom yang Anda juga mungkin perlu polyfill . Bahkan Anda mungkin hanya ingin menggunakan webcomponents.js untuk polyfill elemen kustom, template, dom bayangan, janji, dan beberapa hal lainnya sekaligus.
Jeff Laughlin
12
var doc = new DOMParser().parseFromString(html, "text/html");
var links = doc.querySelectorAll("a");
Mathieu
sumber
4
Mengapa Anda mengawali $? Juga, sebagaimana disebutkan dalam duplikat tertaut , text/htmltidak didukung dengan sangat baik, dan harus diimplementasikan menggunakan polyfill.
Rob W
1
Saya menyalin baris ini dari sebuah proyek, saya terbiasa dengan variabel awalan dengan $ dalam aplikasi javascript (bukan di perpustakaan). hanya untuk avoir yang memiliki konflik dengan perpustakaan. itu tidak terlalu berguna karena hampir setiap variabel dicakup tetapi biasanya bermanfaat. itu juga (mungkin) membantu mengidentifikasi variabel dengan mudah.
Mathieu
1
Sayangnya DOMParsertidak bekerja text/htmldi chrome, halaman MDN ini memberikan solusi.
Jokester
Catatan keamanan: ini akan dijalankan tanpa konteks browser apa pun, jadi tidak ada skrip yang akan berjalan. Itu harus sesuai untuk input yang tidak dipercaya.
Leif Arne Storset
6

Cara tercepat untuk mem-parsing HTML di Chrome dan Firefox adalah Range # createContextualFragment:

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

Saya akan merekomendasikan untuk membuat fungsi pembantu yang menggunakan createContextualFragment jika tersedia dan kembali ke innerHTML sebaliknya.

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

Joel Richard
sumber
Perhatikan bahwa, seperti (yang sederhana) innerHTML, ini akan mengeksekusi <img>'s onerror.
Ry-
Masalah dengan ini adalah bahwa, html seperti '<td> test </td>' akan mengabaikan td dalam konteks document.body (dan hanya membuat simpul teks 'test') .OTOH, jika itu digunakan secara internal dalam mesin templating maka konteks yang tepat akan tersedia.
Munawwar
BTW, IE 11 juga mendukung createContextualFragment.
Munawwar
Pertanyaannya adalah bagaimana mengurai dengan JS - bukan Chrome atau Firefox
sea26.2
Catatan keamanan: ini akan menjalankan skrip apa pun dalam input, dan karenanya tidak cocok untuk input yang tidak dipercaya.
Leif Arne Storset
6

Fungsi berikut parseHTMLakan mengembalikan:

  • a Documentketika file Anda dimulai dengan sebuah DOCTYPE.

  • a DocumentFragmentketika file Anda tidak dimulai dengan sebuah DOCTYPE.


Kode :

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;
    }
}

Cara Penggunaan :

var links = parseHTML('<!doctype html><html><head></head><body><a>Link 1</a><a>Link 2</a></body></html>').getElementsByTagName('a');
John Slegers
sumber
Saya tidak bisa menjalankan ini pada IE8. Saya mendapatkan kesalahan "Objek tidak mendukung properti atau metode ini" untuk baris pertama dalam fungsi. Saya tidak berpikir fungsi createHTMLDocument ada
Sebastian Carroll
Apa tepatnya kasus penggunaan Anda? Jika Anda hanya ingin mem-parsing HTML dan HTML Anda ditujukan untuk isi dokumen Anda, Anda bisa melakukan yang berikut: (1) var div = document.createElement ("DIV"); (2) div.innerHTML = markup; (3) hasil = div.childNodes; --- Ini memberi Anda koleksi childnodes dan harus bekerja tidak hanya di IE8 tetapi bahkan di IE6-7.
John Slegers
Terima kasih untuk opsi alternatif, saya akan mencobanya jika saya perlu melakukan ini lagi. Untuk saat ini saya menggunakan solusi JQuery di atas.
Sebastian Carroll
@SebastianCarroll Perhatikan bahwa IE8 tidak mendukung trimmetode string. Lihat stackoverflow.com/q/2308134/3210837 .
Sikat gigi
2
@Toothbrush: Apakah dukungan IE8 masih relevan pada awal 2017?
John Slegers
4

Jika Anda terbuka untuk menggunakan jQuery, ia memiliki beberapa fasilitas bagus untuk membuat elemen DOM terpisah dari string HTML. Ini kemudian dapat ditanyakan melalui cara biasa, misalnya:

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();

Sunting - hanya melihat jawaban @ Florian yang benar. Ini pada dasarnya persis apa yang dia katakan, tetapi dengan jQuery.

jmar777
sumber
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>`) );


Hanya anak-anak yang valid Nodedalam orangtua Node(mulai dari Range) yang akan diuraikan. Jika tidak, hasil yang tidak terduga dapat terjadi:

// <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>');
AnthumChris
sumber
Catatan keamanan: ini akan menjalankan skrip apa pun dalam input, dan karenanya tidak cocok untuk input yang tidak dipercaya.
Leif Arne Storset
0

dengan kode sederhana ini Anda dapat melakukannya:

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"]'));
NaabNuts
sumber