2009-09-30 5 views
9

Ci sono molti esempi di come rimuovere tag HTML da un documento usando Ruby, Hpricot e Nokogiri hanno metodi inner_text che rimuovono tutto l'HTML per te facilmente e rapidamente.Striscia di testo da un documento HTML usando Ruby

Quello che sto cercando di fare è l'opposto, rimuovere tutto il testo da un documento HTML, lasciando solo i tag e i loro attributi.

Ho preso in considerazione il passaggio dall'impostazione del documento inner_html a zero, ma in realtà dovresti farlo in senso inverso in quanto il primo elemento (root) ha un inner_html dell'intero resto del documento, quindi idealmente avrei iniziare dall'elemento più interno e impostare inner_html su zero mentre si muove attraverso gli antenati.

Qualcuno sa un piccolo trucco per farlo in modo efficiente? Stavo pensando che forse la regex potrebbe farlo ma probabilmente non con la stessa efficienza di un tokenizzatore/parser HTML.

+0

Hai intenzione di avere a che fare con un brutto markup? (entità senza escape, ecc.) – Neall

+0

È possibile - il markup che sto elaborando proviene dagli utenti finali quindi non può essere considerato affidabile. – davidsmalley

risposta

38

Questo funziona troppo:

doc = Nokogiri::HTML(your_html) 
doc.xpath("//text()").remove 
+1

Eccellente! Toss un '.to_s' alla fine per ottenere una stringa e sei a posto! –

2

È possibile acquisire la stringa per creare un array di "gettoni", e quindi selezionare solo quelli che sono i tag HTML:

>> some_html 
=> "<div>foo bar</div><p>I like <em>this</em> stuff <a href='http://foo.bar'> long time</a></p>" 
>> some_html.scan(/<\/?[^>]+>|[\w\|`[email protected]#\$%^&*\(\)\-_\+=\[\]{}:;'",\.\/?]+|\s+/).select { |t| t =~ /<\/?[^>]+>/ }.join("") 
=> "<div></div><p><em></em><a href='http://foo.bar'></a></p>" 

== Modifica ==

O, meglio ancora, basta scansione per i tag HTML;)

>> some_html.scan(/<\/?[^>]+>/).join("") 
=> "<div></div><p><em></em><a href='http://foo.bar'></a></p>" 
3

di afferrare tutto ciò che non in un tag, è possibile utilizzare nokogiri come questo:

doc.search('//text()').text 

Naturalmente, che catturerà roba come il contenuto di <script> o <style> tag, così si potrebbe anche rimuovere i tag nella lista nera:

blacklist = ['title', 'script', 'style'] 
nodelist = doc.search('//text()') 
blacklist.each do |tag| 
    nodelist -= doc.search('//' + tag + '/text()') 
end 
nodelist.text 

Si potrebbe anche whitelist se avremmo preferito, ma che è destinata probabilmente ad essere più Tempo intenso:

whitelist = ['p', 'span', 'strong', 'i', 'b'] #The list goes on and on... 
nodelist = Nokogiri::XML::NodeSet.new(doc) 
whitelist.each do |tag| 
    nodelist += doc.search('//' + tag + '/text()') 
end 
nodelist.text 

Si potrebbe anche solo creare una grande espressione XPath e fare una ricerca. Onestamente non so da che parte è più veloce o se c'è anche una differenza apprezzabile.

0

Ho appena si avvicinò con questo, ma la soluzione di @ Andre-r è soo molto meglio!

#!/usr/bin/env ruby 

require 'nokogiri' 

def strip_text doc 
    Nokogiri(doc).tap { |doc| 
    doc.traverse do |node| 
     node.content = nil if node.text? 
    end 
    }.to_s 
end 

require 'test/unit' 
require 'yaml' 
class TestHTMLStripping < Test::Unit::TestCase 
    def test_that_all_text_gets_strippped_from_the_document 
    dirty, clean = YAML.load DATA 
    assert_equal clean, strip_text(dirty) 
    end 
end 
__END__ 
--- 
- | 
    <!DOCTYPE html> 
    <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'> 
    <head> 
     <meta http-equiv='Content-type'  content='text/html; charset=UTF-8' /> 
     <title>Test HTML Document</title> 
     <meta http-equiv='content-language' content='en' /> 
    </head> 
    <body> 
     <h1>Test <abbr title='Hypertext Markup Language'>HTML</abbr> Document</h1> 
     <div class='main'> 
      <p> 
       <strong>Test</strong> <abbr title='Hypertext Markup Language'>HTML</abbr> <em>Document</em> 
      </p> 
     </div> 
    </body> 
    </html> 
- | 
    <!DOCTYPE html> 
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
    <head> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
    <title></title> 
    <meta http-equiv="content-language" content="en"> 
    </head> 
    <body><h1><abbr title="Hypertext Markup Language"></abbr></h1><div class="main"><p><strong></strong><abbr title="Hypertext Markup Language"></abbr><em></em></p></div></body> 
    </html>