【问题标题】:How to scrape non-standard HTML with Nokogiri如何使用 Nokogiri 抓取非标准 HTML
【发布时间】:2017-11-09 04:49:25
【问题描述】:

如何解压非标准的 HTML:

<body>
    <div class="open">
        <div style='style'>Raw name 1</div>
        <p>Text_1</p>
        <p>Text_2</p>
        <p>Text_3</p>
        <p>Text_4</p>
        <p>Text_5</p>         
        <div style='style'>Raw name 5</div>
        <p>Text_1</p>
        <p>Text_2</p>
        <p>Text_3</p>
        <p>Text_4</p>
        <p>Text_5</p>
    </div>
</body>

我想得到类似的结果:

['Raw name 1', Text_1, Text_2, Text_3, Text_4, Text_5]
...
['Raw name 5', Text_1, Text_2, Text_3, Text_4, Text_5]

我试图在这个例子上做点什么How to parse a HTML table with Nokogiri?,但什么也没发生。

是否可以从此类 HTML 中获取我想要的信息?

【问题讨论】:

  • 欢迎来到 Stack Overflow。您已经给了我们一些数据和所需的输出,但您没有向我们展示您尝试了什么。 “解压非标准 HTML”和“什么都没发生”是什么意思?我没有看到正常的 &lt;head&gt; 标签,但无法判断您的示例或原始来源中是否缺少该标签。 (Nokogiri 没关系。)请阅读“minimal reproducible example”。我们需要演示问题的最少代码。没有它,您似乎没有尝试并希望我们为您编写它。
  • 您的示例不能使用表解析。表格嵌套在行和列中,这使得如何对其进行迭代非常合乎逻辑。您的数据没有嵌套,除了在第一个 &lt;div&gt; 内。它是一个列表,一旦应用 CSS,可能出现为列表列表,但视觉布局与我们必须检索数据的方式无关。
  • 非标准是什么意思?无论如何,只要它是有效的 HTML,是否标准并不重要。

标签: ruby-on-rails ruby html-parsing nokogiri


【解决方案1】:

如果我理解正确,这可能对你有用

require 'nokogiri'
body = <<-BODY 
<body>
    <div class="open">
        <div style='style'>Raw name 1</div>
        <p>Text_1</p>
        <p>Text_2</p>
        <p>Text_3</p>
        <p>Text_4</p>
        <p>Text_5</p>         
        <div style='style'>Raw name 5</div>
        <p>Text_1</p>
        <p>Text_2</p>
        <p>Text_3</p>
        <p>Text_4</p>
        <p>Text_5</p>
    </div>
</body>   
BODY

doc = Nokogiri::HTML(body)
doc.xpath('//body/div').children.each_with_object({}) do |node,obj|
    text = node.text.strip
    obj[text] = [] if node.name == 'div'
    obj[obj.keys.last] << text if node.name == 'p'
end
#=> {"Raw name 1"=>["Text_1", "Text_2", "Text_3", "Text_4", "Text_5"], 
#     "Raw name 5"=>["Text_1", "Text_2", "Text_3", "Text_4", "Text_5"]}

步骤:

  • 这跟随xpath 到第一个div (doc.xpath('//body/div'))
  • 然后将该 div 的每个子项 (.children) 与一个对象 (.each_with_object({}) do |node,obj|) 一起传递给块,在这种情况下作为累加器。
  • 然后它为每个 div 标签添加一个键并将其分配给一个空数组 (obj[text] = [] if node.name == 'div')。
  • 它使用以下 p 标签 (obj[obj.keys.last] &lt;&lt; text if node.name == 'p') 填充最后一个键

结果是Hash,其中键是divs,值是以下p 标记文本的Array,直到它到达下一个div

【讨论】:

    【解决方案2】:

    我会这样做:

    require 'nokogiri'
    
    doc = Nokogiri::HTML(<<EOT)
    <body>
        <div class="open">
            <div style='style'>Raw name 1</div>
            <p>Text_1</p>
            <p>Text_2</p>         
            <div style='style'>Raw name 5</div>
            <p>Text_1</p>
            <p>Text_2</p>
        </div>
    </body>
    EOT
    
    doc.at('.open').elements.slice_before { |e| e.name == 'div' }.map { |ary|
      ary.map(&:text)
    }
    # => [["Raw name 1", "Text_1", "Text_2"], ["Raw name 5", "Text_1", "Text_2"]]
    

    稍微分解一下:

    doc.at('.open').elements.map(&:name) # => ["div", "p", "p", "div", "p", "p"]
    doc.at('.open').elements.slice_before { |e| e.name == 'div' }.map { |a| a.map(&:name) } # => [["div", "p", "p"], ["div", "p", "p"]]
    

    elementsslice_before 是这里的魔法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-03
      • 1970-01-01
      相关资源
      最近更新 更多