我正在使用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
,则该方法将运行,但不会获取任何相关信息(显然)。我认为这是由于我用来获取字段的复杂选择器造成的,但是我不太确定。
有人遇到类似的问题,您能给我一些建议吗?
是,该错误意味着您的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查询可能看起来很吓人。让我解释一下它们是如何工作的:
//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
会找到第一个匹配表。
.//tr[2]/td
.
从当前节点开始(此表)//tr[2]
…找到第二个<tr>
,它是任意级别的后代/td
…,然后找到该子项的<td>
个子代。同样,因为我们使用的是at_xpath
,所以找到第一个匹配的<td>
。
.//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同样脆弱,它依赖于行的顺序而不是行的标签文本。