用Nokogiri刮擦网站

问题描述 投票:2回答:1

我正在使用Nokogiri抓取网站,当我尝试从表中获取字段时遇到问题。我正在使用selector gadget查找表的CSS选择器。我正在从government website that details information on motor carriers抓取数据。

我正在使用的方法如下:

def scrape_database
  url = "http://safer.fmcsa.dot.gov/query.asp?searchtype=ANY&query_type=queryCarrierSnapshot&query_param=USDOT&query_string=#{self.dot}#Inspections"
  doc = Nokogiri::HTML(open(url))
  self.name = doc.at_css("tr:nth-child(4) .queryfield").text
  self.address = doc.at_css("tr:nth-child(6) .queryfield").text
end

我使用该语法获取上表中的所有字段,并且该方法运行良好,但是下面的崩溃率/检查表却有问题。

这是我用来获取该信息的内容:

self.vehicle_inspections = doc.at_css("center:nth-child(13) tr:nth-child(2) :nth-child(2)").text

undefined method `text' for nil:NilClass

如果我从此末尾删除text,则该方法将运行,但不会获取任何相关信息(显然)。我认为这是由于我用来获取字段的复杂选择器造成的,但是我不太确定。

有人遇到类似的问题,您能给我一些建议吗?

ruby ruby-on-rails-3 css-selectors nokogiri web-scraping
1个回答
4
投票

是,该错误意味着您的CSS选择器没有找到该信息; at_css返回nil,并且nil.text无效。您可以像这样来防范它:

insp = doc.at_css("long example css selector")
self.vehicle_inspections = insp && insp.text

但是,在我看来,您“需要”此数据。由于您没有提供HTML页面或CSS选择器,因此我无法帮助您构建有效的CSS或XPath选择器。

对于以后的问题或对此问题的编辑,请注意,与挥舞和松散地描述代码的样子相比,实际(精简的)代码是首选的。如果您向我们显示HTML页面或相关代码段,并描述所需的元素/文本/属性,我们可以告诉您如何选择它。

我在该页面上看到六个表格。哪个是“崩溃率/检查率”表?假设您的URL末尾包含#Inspections,我假设您是在谈论“美国检查/崩溃”部分下面的两个表。这是每个匹配的XPath选择器:

require 'nokogiri'
require 'open-uri'

url = "http://safer.fmcsa.dot.gov/query.asp?searchtype=ANY&query_type=queryCarrierSnapshot&query_param=USDOT&query_string=800585"
doc = Nokogiri::HTML(open(url))
table1 = doc.at_xpath('//table[@summary="Inspections"][preceding::h4[.//a[@name="Inspections"]]]')
table2 = doc.at_xpath('//table[@summary="Crashes"][preceding::h4[.//a[@name="Inspections"]]]')

# Find a row by index (1 is the first row)
vehicle_inspections    = table1.at_xpath('.//tr[2]/td').text.to_i

# Find a row by header text
out_of_service_drivers = table1.at_xpath('.//tr[th="Out of Service"]/td[2]').text.to_i

p [ vehicle_inspections, out_of_service_drivers ]
#=> [6, 0]

tow_crashes = table2.at_xpath('.//tr[th="Crashes"]/td[3]').text.to_i
p tow_crashes
#=> 0

XPath查询可能看起来很吓人。让我解释一下它们是如何工作的:

  1. //table[@summary="Inspections"][preceding::h4[.//a[@name="Inspections"]]]

    • [//table在文档的任何位置查找<table>
    • [[@summary="Inspections"]…但仅当它具有具有该值的summary属性时
    • [[preceding::h4…]…并且仅当您可以在文档的前面找到<h4>元素时
    • [.//a…]…特别是<h4>在其下方某处具有<a>
      • [[@name="Inspections"]…并且<a>必须具有此文本的name属性。

    这实际上将匹配两个表(在页面的后面还有另一个summary="Inspections"表,但是使用at_xpath会找到第一个匹配表。

  2. .//tr[2]/td

    • .从当前节点开始(此表)
    • [//tr[2]…找到第二个<tr>,它是任意级别的后代
    • [/td…,然后找到该子项的<td>个子代。

    同样,因为我们使用的是at_xpath,所以找到第一个匹配的<td>

  3. .//tr[th="Out of Service"]/td[2]

    • .从当前节点开始(此表)
    • //tr…找到任何级别的后代<tr>
      • [[th="Out of Service]…但仅包含带有此文本的<tr>子项的<th>
    • [/td[2]…,然后找到这些的第二个<td>子级。

    在这种情况下,只有一个<tr>符合条件,因此只有一个<td>符合条件,但是我们仍然使用at_xpath,以便我们直接获取该节点,而不是在其中包含单个元素的NodeSet。它。

这里的目标(以及任何屏幕抓取的目的是锁定页面上有意义的值,而不是任意索引。)>

例如,我可以将table1 xpath编写为:

# Find the first table with this summary
table1 = doc.at_xpath('//table[@summary="Inspections"][1]')

…甚至是…

# Find the 20th table on the page
//table[20]

但是,这些是脆弱

。有人在页面上添加新部分,或者恰巧添加或删除格式表的代码都会导致这些表达式中断。您想寻找可能不会改变的强大属性和文本,然后根据此锚定搜索。

vehicle_inspections XPath同样脆弱,它依赖于行的顺序而不是行的标签文本。

© www.soinside.com 2019 - 2024. All rights reserved.