[英]why xpath is returning text outside html tags?
我正在使用在<html>
标记之外包含一些text
的文档。 当我读取体内的数据时,它还会返回甚至不在html标签中的文本。
page_text = Nokogiri::HTML(open(file_path)).xpath("//body").text
p page_text
输出:
"WARC/1.0\\nWARC-Type: response\\nWARC-Date: 2012-02-11T04:48:01Z\\nWARC-TREC-ID: clueweb12-0000tw-13-04988\\nWARC-IP-Address: 184.85.26.15\\nWARC-Payload-Digest: sha1:PNCB5NNAA766RLLISZ6ODV3FJZBCATKR\\nWARC-Target-URI: http://www.allchocolate.com/health/basics/\\nWARC-Record-ID: \\nContent-Type: application/http; msgtype=response\\nContent-Length: 14577\\n\\n\\n\\n\\n sample document\\n\\n\\n hello world\\n\\n"
文献:
WARC/1.0
WARC-Type: response
WARC-Date: 2012-02-11T04:48:01Z
WARC-TREC-ID: clueweb12-0000tw-13-04988
WARC-IP-Address: 184.85.26.15
WARC-Payload-Digest: sha1:PNCB5NNAA766RLLISZ6ODV3FJZBCATKR
WARC-Target-URI: http://www.allchocolate.com/health/basics/
WARC-Record-ID: <urn:uuid:ff32c863-5066-4f51-802a-f31d4af074d5>
Content-Type: application/http; msgtype=response
Content-Length: 14577
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>sample document</title>
</head>
<body>
hello world
</body>
</html>
Nokogiri尝试将文件内容解析为HTML文档,但这不是有效的文档。 它是一个文本文件,恰好包含HTML文件。 当然,Nokogiri不知道这一点,并且它无法挑选出HTML本身,因此它试图解析整个内容。 由于它不是有效的HTML,因此会产生错误。
解析时,Nokogiri尝试尽最大可能修复这些错误,但是在这种情况下不起作用,并导致您在此处看到奇怪的输出。
特别是,当Nokogiri在HTML之前看到该文本时,它将假定它应该是HTML文档主体的一部分。 因此,在将文本添加为body
的子级之前,它会创建html
和body
元素并将其注入到文档中。
后来,它看到了实际的<body>
标签,但是由于知道它已经具有一个body
元素,并且只能有一个这样的元素,因此它将忽略它。
您需要确保仅提供有效的HTML(或尽可能接近有效的HTML,纠错可以解决小问题)。 您可能需要以某种方式对文件进行预处理,以删除开头的多余文本。
显然,前导文本是一个问题,但末尾文本不是问题。 XML是一种高度结构化的语言,将XML解析器应用于HTML至少意味着您必须拥有有效的HTML。 如果您没有有效的HTML,那么您将得到Nokogiri吐出的任何东西。
在我看来,Nokogiri将整个内容包装在默认的根节点中,然后返回其中的所有文本节点,实际上忽略了//body
xpath。 有趣的是,如果将文本包装在div
并搜索xpath //div
,则不会出现问题,因此可能会提出解决方案。
看来Nokogiri认为//body
等于根节点。 啊! 也许Nokogiri使用<body>
作为根节点。 不,xpath /body//body
不起作用。
对评论的回应:
您可以使用正则表达式搜索<body>
标签,然后插入div标签。 但是使用简单的正则表达式搜索html将是一个脆弱的解决方案,并且并非在所有情况下都有效。
顺便说一句,您可以通过解析仅包含text:hello world的文档,然后打印出Nokogiri找到的所有节点,来了解Nokogiri如何处理标签外的文本:
require 'nokogiri'
nodes = Nokogiri::HTML(open('html.html')).xpath('//*')
nodes.each do |node|
puts node.name
end
--output:--
html
body
p
因此,Nokogiri将文本包装在三个标签中。
或者,更好的是,您可以解析文档并将其打印为html:
require 'nokogiri'
doc = Nokogiri::HTML(open('./html.html'))
puts doc.to_html
--output:--
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html><body><p>WARC/1.0
WARC-Type: response
WARC-Date: 2012-02-11T04:48:01Z
WARC-TREC-ID: clueweb12-0000tw-13-04988
WARC-IP-Address: 184.85.26.15
WARC-Payload-Digest: sha1:PNCB5NNAA766RLLISZ6ODV3FJZBCATKR
WARC-Target-URI: http://www.allchocolate.com/health/basics/
WARC-Record-ID: <uuid:ff32c863-5066-4f51-802a-f31d4af074d5>
Content-Type: application/http; msgtype=response
Content-Length: 14577
<title>sample document</title>
hello world
</uuid:ff32c863-5066-4f51-802a-f31d4af074d5></p></body></html>
这意味着您可以像下面这样hello world
:
require 'nokogiri'
doc = Nokogiri::HTML(open('./html.html'))
title = doc.at_xpath('//title')
puts title.next.text.strip
--output:--
hello world
另一种方法是在用Nokogiri解析之前摆脱非HTML内容:
require 'nokogiri'
infile = File.open('html.html')
non_html = infile.gets(sep="\n\n")
html = infile.gets(nil) #Slurp the rest of the file
doc = Nokogiri::HTML(html)
puts doc.at_xpath('//body').text.strip
--output:--
hello world
假定始终有一个空白行,将非html内容与html内容分隔开。
首先,@ 7stud答案很明显,您可以在\\n\\n
上断开文件,但在我的文档集中,并非总是\\n\\n
在实际的html代码之前。
因此,使用相同的想法,我想到了另一个解决方法,即使用正则表达式删除html
start标记之前的所有文本,然后将其传递给Nokogiri
进行解析。
file = File.read(file_path).to_s
file = file.sub(/.*?(?=<html)/im,"")
page = Nokogiri::HTML(file)
现在工作正常。
在将内容传递给Nokogiri之前,对其进行预处理很简单:
require 'nokogiri'
text = '
WARC/1.0
WARC-Type: response
WARC-Date: 2012-02-11T04:48:01Z
WARC-TREC-ID: clueweb12-0000tw-13-04988
WARC-IP-Address: 184.85.26.15
WARC-Payload-Digest: sha1:PNCB5NNAA766RLLISZ6ODV3FJZBCATKR
WARC-Target-URI: http://www.allchocolate.com/health/basics/
WARC-Record-ID: <urn:uuid:ff32c863-5066-4f51-802a-f31d4af074d5>
Content-Type: application/http; msgtype=response
Content-Length: 14577
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>sample document</title>
</head>
<body>
hello world
</body>
</html>
'
doc = Nokogiri::HTML(text[/<!DOCTYPE.+/m])
doc.to_html # => "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n <title>sample document</title>\n</head>\n<body>\n hello world\n</body>\n</html>\n"
诀窍是:
text[/<!DOCTYPE.+/m]
它告诉Ruby浏览文本并将所有文本从<!DOCTYPE
到字符串的末尾,即有效的HTML。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.