Получить текст абзаца со всей разметкой (и их содержанием), удаленной - PullRequest
1 голос
/ 06 сентября 2011

Как получить только текст узла <p>, в котором есть другие теги, например:

<p>hello my website is <a href="www.website.com">click here</a> <b>test</b></p>

Я хочу только "hello my website is"

Вот что я попробовал:

begin
  node = html_doc.css('p')
  node.each do |node|
    node.children.remove
  end
  return (node.nil?) ? ''  : node.text
rescue
  return ''
end

Ответы [ 3 ]

1 голос
/ 07 сентября 2011

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

  • Если вы хотите превратить <p>Hello <b>World</b>!</p> в "Hello !", то удаление детей - один из способов сделать это.Проще (и менее разрушительно) просто найти все текстовые узлы и соединить их:

    require 'nokogiri'
    html = Nokogiri::HTML('<p>Hello <b>World</b>!</p>')
    
    # Find the first paragraph (in this case the only one)
    para = html.at('p') 
    
    # Find all the text nodes that are children (not descendants),
    # change them from nodes into the strings of text they contain,
    # and then smush the results together into one big string.
    p para.search('text()').map(&:text).join 
    #=> "Hello !"
    
  • Если вы хотите превратить <p>Hello <b>World</b>!</p> в "Hello " (без восклицательного знака)тогда вы можете просто сделать:

    p para.children.first.text # if you know that text is the first child
    p para.at('text()').text   # if you want to find the first text node
    

Как показал @Iwe, вы можете использовать метод String#strip для удаления начальных / конечных пробелов из результата, если хотите.

1 голос
/ 06 сентября 2011

Обновление 2 : хорошо, хорошо, вы удаляете все дочерние элементы с node.children.remove, включая текстовые узлы, предлагаемое решение может выглядеть следующим образом:

# 1. select all <p> nodes
doc.css('p').
  # 2. map children, and flatten
  map { |node| node.children }.flatten.
  # 3. select text nodes only
  select { |node| node.text? }.
  # 4. get text and join
  map { |node| node.text }.join(' ').strip

Этот пример возвращает"Привет, мой веб-сайт", но обратите внимание, что doc.css('p') также находит <p> тегов в <p> тегах.

Обновление : извините, неправильно прочитал ваш вопрос, вы только хотите "приветмой веб-сайт ", см. решение выше, оригинальный ответ:

Не напрямую с nokogiri, но может потребоваться дезинфицировать камень: https://github.com/rgrove/sanitize/

Sanitize.clean(html, {}) # => " hello my website is click here test "

К вашему сведению, он использует nokogiri внутри страны.

0 голосов
/ 06 сентября 2011

Есть другой способ сделать это. Вместо удаления узлов удалите текст, содержащийся в этих узлах:

require 'nokogiri'

doc = Nokogiri::HTML('<p>hello my website is <a href="www.website.com">click here</a> <b>test</b></p>')
text = doc.search('p').map{ |p|
  p_text = p.text
  a_text = p.at('a').text
  p_text[a_text] = ''
  p_text
}

puts text

>>hello my website is  test

Это простой пример, но идея состоит в том, чтобы найти теги <p>, а затем отсканировать внутри них теги, содержащие текст, который вам не нужен. Для каждого из этих нежелательных тегов возьмите их текст и удалите его из окружающего текста.

В примере кода у вас будет список нежелательных узлов в присваивании a_text, зациклите их и итеративно удалите текст, например:

text = doc.search('p').map{ |p|
  p_text = p.text
  %w[a].each do |bad_nodes|
    bad_nodes_text = p.at(bad_nodes).text
    p_text[bad_nodes_text] = ''
  end
  p_text
}

Вы получите обратно text, который является массивом измененного текстового содержимого узлов <p>.

...