2009-09-30 11 views
9

Es gibt viele Beispiele zum Entfernen von HTML-Tags aus einem Dokument mit Ruby, Hpricot und Nokogiri haben inner_text Methoden, die alle HTML für Sie einfach und schnell entfernen.Streifen Text aus HTML-Dokument mit Ruby

Was ich versuche, ist das Gegenteil, entfernen Sie den gesamten Text aus einem HTML-Dokument, nur die Tags und ihre Attribute zu verlassen.

Ich überlegte, die Dokumenteinstellung inner_html durchzugehen bis nil, aber dann müssten Sie das umgekehrt tun, da das erste Element (root) einen inneren_html des gesamten restlichen Dokuments hat, also hätte ich es idealerweise Beginne beim innersten Element und setze inner_html auf Null, während du dich durch die Vorfahren bewegst.

Kennt jemand einen netten kleinen Trick, um dies effizient zu tun? Ich dachte, dass Regex es vielleicht tun könnte, aber wahrscheinlich nicht so effizient wie ein HTML-Tokenizer/Parser.

+0

Sie werden mit schlechtem Markup umgehen müssen? (unescaped entities, etc.) – Neall

+0

Es ist möglich - das Markup, das ich verarbeite, kommt von Endbenutzern, daher kann man sich nicht darauf verlassen. – davidsmalley

Antwort

38

Dies funktioniert auch:

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

Ausgezeichnet! Wirf am Ende ein '.to_s', um eine Schnur zu bekommen und du kannst loslegen! –

2

Sie können die Zeichenfolge scannen eine Reihe von "Token", zu erstellen und dann nur diejenigen auswählen, die HTML-Tags sind:

>> 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>" 

== == Bearbeiten

Oder noch besser, nur Scan für hTML-Tags;)

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

Um alles nicht in einem Tag zu greifen, können Sie nokogiri wie folgt verwenden:

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

natürlich, dass Sachen wie die Inhalte von <script> oder <style> Tags greifen wird, so könnte man auch die schwarze Liste gesetzt Tags entfernen:

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

Sie könnten auch die weiße Liste, wenn Sie bevorzugt, aber das ist wahrscheinlich mehr los zu sein zeitintensiv:

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 

Sie könnten auch einfach einen großen XPath-Ausdruck erstellen und eine Suche durchführen. Ich weiß ehrlich nicht, welcher Weg schneller ist oder ob es sogar einen nennenswerten Unterschied gibt.

0

ich damit gerade aufkam, aber @ andre-r-Lösung ist soo viel besser!

#!/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>