我正在处理一个text
外<html>
标记的文档。当我在body中读取数据时,它也会返回甚至不在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>
答案 0 :(得分:2)
Nokogiri正在尝试将文件内容解析为HTML文档,但它不是有效的文档。它恰好包含一个HTML文档的文本文档。当然Nokogiri不知道这一点,并且它无法自己挑选出那个HTML部分,所以它试图解析整个事情。由于它不是有效的HTML,因此会产生错误。
在解析时,Nokogiri试图尽可能地修复这些错误,但在这种情况下这不起作用,并导致你在这里看到奇怪的输出。
特别是,当Nokogiri在HTML之前看到文本时,它假定它应该是HTML文档正文的一部分。因此,在将文本作为此html
的子项添加之前,它会在文档中创建并注入body
和body
元素。
稍后它会看到实际的<body>
标记,但由于它知道它已经有一个body
元素,并且只能有一个这样的元素,它会忽略它。
您需要确保只提供有效的HTML(或尽可能接近有效 - 错误纠正可以解决小问题)。您可能需要以某种方式预处理文件,以便在开头删除额外的文本。
答案 1 :(得分:1)
显然,前导文本是一个问题,但不是尾随文本。 XML是一种高度结构化的语言,将XML解析器应用于HTML意味着至少必须拥有有效的HTML。如果您没有有效的HTML,那么您可以获得Nokogiri吐出的任何内容。
在我看来,Nokogiri将整个事物包装在默认的根节点中,然后返回其中的所有文本节点,基本上忽略了//body
xpath。有趣的是,如果您将文本包装在div
中并搜索xpath //div
,则没有任何问题,因此可能会提出解决方案。
似乎Nokogiri认为//body
等于根节点。啊!也许Nokogiri使用<body>
作为根节点。不,xpath /body//body
不起作用。
对评论的回应:
您可以使用正则表达式搜索<body>
标记,然后插入div标记。但是使用简单的正则表达式搜索html将是一个脆弱的解决方案,它在所有情况下都不会起作用。
顺便说一下,你可以看到Nokogiri如何处理标签之外的文本,解析一个只有文本的文件:hello world,然后打印出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内容之间总是有一个空行。
答案 2 :(得分:1)
首先,@ 7stud的答案就是你可以在\n\n
打破你的文件
在我的文档集合中,在实际的html代码之前并不总是\n\n
。
因此,使用相同的想法,我提出了另一种解决方法,即使用正则表达式删除html
开始标记之前的所有文本,然后将其传递给Nokogiri
进行解析。
file = File.read(file_path).to_s
file = file.sub(/.*?(?=<html)/im,"")
page = Nokogiri::HTML(file)
现在工作正常。
答案 3 :(得分:0)
在将内容传递给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。