2013-07-04 4 views
1

У меня есть HTML-документ в виде строки. Я проанализирован его с помощью Nokogiri:Как выполнять поиск и замену узлов Nokogiri?

doc_str = <<-mydoc 
    <p>Lorem ipsum dolor sit foo.</p> 
    <h2>Consectetur adipisicing bar</h2> 
    <p>Foo do <a href="/c-foo.aspx" class = "foo" title="Foo bar.">foofoo foo</a>.</p> 
mydoc 

doc = Nokogiri::HTML doc_str 

Я хочу, чтобы заменить "foo"/"Foo" на "Bar"/"bar" во всех видимых текстов:

desired = <<-mydoc 
    <p>Lorem ipsum dolor sit bar.</p> 
    <h2>Consectetur adipisicing bar</h2> 
    <p>Bar do <a href="/c-foo.aspx" class = "foo" title="Bar bar.">foofoo bar</a>.</p> 
mydoc 

Как я могу это сделать?

Я попытался прочитать Nokogiri tutorial, в котором описаны Nokogiri::HTML::Document#at_css. Используя Ruby 2.0 и новейший Nokogiri, doc.at_css 'h1' возвращает nil, поэтому h1.content = "something" даже не возможно.

Даже если это сработает, это будет только первый шаг к решению проблемы поиска и замены.

ответ

2

doc.at_css 'h1' возвращает nil, потому что в вашем HTML-коде нет элементов h1. doc.at_css 'h2' правильно возвращает объект Nokogiri::XML::Element для элемента h2.

Селекторы CSS не могут выбирать текстовые узлы и являются плохим инструментом для такого рода вещей. XPath сделает все, что делает CSS, и многое другое. Текстовым узлом в любом месте под корнем документа является просто //text().

Редактировать Я только что заметил, что вы, похоже, хотите, чтобы содержание атрибутов менялось одинаково. @* соответствует любому атрибуту, поэтому выражение XPath равно //@* | //text(). Хотя я не совсем понимаю, как href="/c-foo.aspx" и class="foo" остаются без изменений, но title="Foo bar." становится title="bar bar.". Я уверен, что вы можете разобраться в этом сами.

Вам нужно найти все текстовые узлы с помощью XPath, а затем использовать content для извлечения текстового значения для каждого узла. Измените его по своему усмотрению и используйте content= для его замены.

Эта программа демонстрирует. Метод to_html обертывает данные в теги, необходимые для его корректного HTML.

require 'nokogiri' 

doc_str = <<-HTML 
    <p>Lorem ipsum dolor sit foo.</p> 
    <h2>Consectetur adipisicing bar</h2> 
    <p>Foo do <a href="/c-foo.aspx" class = "foo" title="Foo bar.">foofoo foo</a>.</p> 
HTML 

doc = Nokogiri::HTML(doc_str) 

doc.xpath('//@*', '//text()').each do |node| 
    node.content = node.content.gsub(/\bfoo\b/, 'bar').gsub(/\bFoo\b/, 'Bar') 
end 

puts doc.to_html 

выход

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> 
<html><body> 
<p>Lorem ipsum dolor sit bar.</p> 
    <h2>Consectetur adipisicing bar</h2> 
    <p>Bar do <a href="/c-bar.aspx" class="bar" title="Bar bar.">foofoo bar</a>.</p> 
</body></html> 
+0

+1 за обучение меня так много, но, пожалуйста, обратите внимание, что ваш пример не совсем правильно, так как он также заменить 'класс = "Foo"' 'в классе = «bar» ', который он не должен, потому что это не видимый текст. Напротив, пример '# traverse' @pguardiario не страдает проблемой' class = "bar", он пропускает '\ b', обертывая' foo' и 'Foo'. Не могли бы вы рассказать мне больше о аргументе метода '# xpath'? Я имею в виду, что '// // @ * | // текст() ''. Что это означает небеса? –

+0

@BorisStitnicky: Значит, разница между атрибутами в вашем вопросе преднамеренно? Это совсем не ясно. Вам нужно будет перечислять имена атрибутов, которые вы хотите обработать, чтобы получить это правильно. Термины XPath 'text()' и '@ *' я уже объяснил. '//' означает «потомок», и когда он находится в начале выражения, это означает «потомок корня», т. е. в любом месте документа. Труба '|' является «объединением», так что полное выражение выбирает все узлы атрибутов и все текстовые узлы в любом месте. [Посмотрите здесь спецификации XPath 1.0.] (Http://www.w3.org/TR/xpath/) – Borodin

+0

Измените '@ *' на '@ title', и я думаю, вы его пригвоздили. –

Смежные вопросы