【问题标题】:How to find text across HTML tag boundaries?如何跨 HTML 标签边界查找文本?
【发布时间】:2018-02-16 18:41:17
【问题描述】:

我有这样的 HTML:

<div>Lorem ipsum <b>dolor sit</b> amet.</div>

如何在此 HTML 中为我的搜索字符串 ipsum dolor 找到基于纯文本的匹配项?我需要匹配的开始和结束 XPath 节点指针,以及指向这些开始和停止节点内的字符索引。我使用 Nokogiri 来处理 DOM,但是任何 Ruby 的解决方案都可以。

难度:

  • 我不能通过 DOM node.traverse {|node| … } 在遇到文本节点时进行纯文本搜索,因为我的搜索字符串可以跨越标签边界。

  • 在将 HTML 转换为纯文本后,我无法进行纯文本搜索,因为我需要 XPath 索引作为结果。

我可以通过基本的树遍历自己实现它,但在我这样做之前,我会问是否有一个 Nokogiri 函数或技巧可以更舒适地完成它。

【问题讨论】:

    标签: ruby dom xpath html-parsing nokogiri


    【解决方案1】:

    最后,我们使用了如下代码。它针对问题中给出的示例进行了显示,但也适用于任意深度 HTML 标记嵌套的一般情况。 (这是我们需要的。)

    此外,我们以一种可以忽略一行中多余(≥2)空白字符的方式实现它。这就是为什么我们必须搜索匹配的结尾,而不能只使用搜索字符串/引号的长度和匹配位置的开始:搜索字符串和搜索匹配中的空白字符数可能不同。

    doc = Nokogiri::HTML.fragment("<div>Lorem ipsum <b>dolor sit</b> amet.</div>")
    quote = 'ipsum dolor'
    
    
    # (1) Find search string in document text, "plain text in plain text".
    
    quote_query = 
      quote.split(/[[:space:]]+/).map { |w| Regexp.quote(w) }.join('[[:space:]]+')
    
    start_index = doc.text.index(/#{quote_query}/i)
    end_index = start_index+doc.text[/#{quote_query}/i].size
    
    
    # (2) Find XPath values and character indexes for our search match.
    # 
    # To do this, walk through all text nodes and count characters until 
    # encountering both the start_index and end_index character counts 
    # of our search match.
    
    start_xpath, start_offset, end_xpath, end_offset = nil
    i = 0
    
    doc.xpath('.//text() | text()').each do |x|
      offset = 0
      x.text.split('').each do
        if i == start_index
          e = x.previous
          sum = 0
          while e
            sum+= e.text.size
            e = e.previous
          end
          start_xpath = x.path.gsub(/^\?/, '').gsub(
            /#{Regexp.quote('/text()')}.*$/, ''
          )
          start_offset = offset+sum
        elsif i+1 == end_index
          e = x.previous
          sum = 0
          while e
            sum+= e.text.size
            e = e.previous
          end
          end_xpath = x.path.gsub(/^\?/, '').gsub(
            /#{Regexp.quote('/text()')}.*$/, ''
          )
          end_offset = offset+1+sum
        end
        offset+=1
        i+=1
      end
    end
    

    此时,我们可以为搜索匹配的开始和停止检索所需的 XPath 值(此外,指向 XPath 指定元素内的确切字符的字符偏移量用于搜索匹配的开始和停止) .我们得到:

    puts start_xpath
      /div
    puts start_offset
      6
    puts end_xpath
      /div/b
    puts end_offset
      5
    

    【讨论】:

      【解决方案2】:

      你可以这样做:

      doc.search('div').find{|div| div.text[/ipsum dolor/]}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多