繁体   English   中英

为什么xpath在html标签外返回文本?

[英]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的子级之前,它会创建htmlbody元素并将其注入到文档中。

后来,它看到了实际的<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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM