你能提供解析HTML的例子吗?
Posted
技术标签:
【中文标题】你能提供解析HTML的例子吗?【英文标题】:Can you provide examples of parsing HTML? 【发布时间】:2010-10-20 21:00:42 【问题描述】:如何使用多种语言和解析库解析 html?
回答时:
在回答有关如何使用正则表达式解析 HTML 的问题时,将链接到各个 cmets,以显示正确的做事方式。
为了保持一致性,我要求该示例为锚标记中的href
解析 HTML 文件。为了方便搜索这个问题,我要求您遵循这种格式
语言:[语言名称]
库:[库名称]
[example code]
请将该库设为该库文档的链接。如果您想提供除提取链接以外的示例,还请包括:
目的:[解析的作用]
【问题讨论】:
重复每个示例,HTML builder 代码毫无意义 以及为什么你用毫无意义/无用的使用指令来混淆 perl 代码? (警告和严格) 自包含,工作示例更好。所有 Perl 代码都应该包含严格和警告,它们并非毫无意义;它们是 Modern Perl 的一部分。如果您认为代码“毫无意义”和“无用”,我会不寒而栗。 在我的代码中,我总是使用警告和严格;在 THIS 上下文中,它们毫无意义。这些示例中的大多数都不是“自包含的”(例如 jquery、ruby 和其他答案),那么为什么还要使用基于 perl 的解决方案呢? 因为你可以,而且 javascript 示例是自包含在它们的环境中的。我没有更改 nokogiri 示例,因为我无法在我的机器上安装 nokogiri。我不想更改我不理解的代码。但我会改变它;一方面,它看起来不像是在解决这个例子。至于为正在学习的人使用严格的、不安全的代码是一种犯罪行为。他们需要所有能得到的强化。 【参考方案1】:语言:JavaScript 图书馆:jQuery
$.each($('a[href]'), function()
console.debug(this.href);
);
(使用 firebug console.debug 进行输出...)
并加载任何 html 页面:
$.get('http://***.com/', function(page)
$(page).find('a[href]').each(function()
console.debug(this.href);
);
);
为此使用了另一个 each 函数,我认为链接方法时更简洁。
【讨论】:
好吧,如果你这样看的话。 :) 但是使用 javascript/jquery 来解析 HTML 感觉很自然,非常适合这样的东西。 使用浏览器作为解析器是终极解析器。给定浏览器中的 DOM是文档树。【参考方案2】:语言:C# 图书馆:HtmlAgilityPack
class Program
static void Main(string[] args)
var web = new HtmlWeb();
var doc = web.Load("http://www.***.com");
var nodes = doc.DocumentNode.SelectNodes("//a[@href]");
foreach (var node in nodes)
Console.WriteLine(node.InnerHtml);
【讨论】:
【参考方案3】:语言:Python 图书馆:BeautifulSoup
from BeautifulSoup import BeautifulSoup
html = "<html><body>"
for link in ("foo", "bar", "baz"):
html += '<a href="http://%s.com">%s</a>' % (link, link)
html += "</body></html>"
soup = BeautifulSoup(html)
links = soup.findAll('a', href=True) # find <a> with a defined href attribute
print links
输出:
[<a href="http://foo.com">foo</a>,
<a href="http://bar.com">bar</a>,
<a href="http://baz.com">baz</a>]
也可以:
for link in links:
print link['href']
输出:
http://foo.com
http://bar.com
http://baz.com
【讨论】:
这很好,但是 BeautifulSoup 是否提供了一种查看标签以获取属性的方法? 去查看文档 第一个示例中的输出只是匹配链接的文本表示,它们实际上是您可以做各种有趣事情的对象。 是的,我刚刚阅读了文档,您在修复代码方面比我强。我确实添加了 try/catch 以防止它在 href 不存在时爆炸。显然“链接中的'href'”不起作用。 一定要用beautifulsoup crummy.com/software/BeautifulSoup/3.1-problems.html【参考方案4】:语言:Perl 图书馆:pQuery
use strict;
use warnings;
use pQuery;
my $html = join '',
"<html><body>",
(map qq(<a href="http://$_.com">$_</a>) qw/foo bar baz/),
"</body></html>";
pQuery( $html )->find( 'a' )->each(
sub
my $at = $_->getAttribute( 'href' );
print "$at\n" if defined $at;
);
【讨论】:
太棒了。从来不知道 pQuery,但它看起来很酷。 你能像在 jQuery 中那样搜索 'a[@href]' 或 'a[href]' 吗?它会简化代码,而且肯定会更快。 这里有一些其他带有 pQuery 答案的 *** 问题...***.com/questions/713827/…***.com/questions/574199/…***.com/questions/254345/…***.com/questions/221091/… @code-is-art:不幸的是,还没有......引用文档中的作者“选择器语法仍然非常有限。(仅限单个标签、ID 和类)”。检查测试,因为 pQuery 确实具有文档中没有的功能,例如。说'带有“blah”内容的语言:贝壳 库:lynx(嗯,它不是库,但在 shell 中,每个程序都是某种库)
lynx -dump -listonly http://news.google.com/
【讨论】:
+1 表示尝试,+1 表示可行的解决方案,-1 表示无法推广到其他任务的解决方案:net +1 好吧,任务定义得很好——它必须从“a”标签中提取链接。 :) 是的,但它被定义为一个示例来展示如何解析,我可以很容易地要求您打印具有类“phonenum”的语言:Ruby 图书馆:Hpricot
#!/usr/bin/ruby
require 'hpricot'
html = '<html><body>'
['foo', 'bar', 'baz'].each |link| html += "<a href=\"http://#link.com\">#link</a>"
html += '</body></html>'
doc = Hpricot(html)
doc.search('//a').each |elm| puts elm.attributes['href']
【讨论】:
【参考方案7】:语言:Python 图书馆:HTMLParser
#!/usr/bin/python
from HTMLParser import HTMLParser
class FindLinks(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
def handle_starttag(self, tag, attrs):
at = dict(attrs)
if tag == 'a' and 'href' in at:
print at['href']
find = FindLinks()
html = "<html><body>"
for link in ("foo", "bar", "baz"):
html += '<a href="http://%s.com">%s</a>' % (link, link)
html += "</body></html>"
find.feed(html)
【讨论】:
【参考方案8】:语言:Perl 图书馆:HTML::Parser
#!/usr/bin/perl
use strict;
use warnings;
use HTML::Parser;
my $find_links = HTML::Parser->new(
start_h => [
sub
my ($tag, $attr) = @_;
if ($tag eq 'a' and exists $attr->href)
print "$attr->href\n";
,
"tag, attr"
]
);
my $html = join '',
"<html><body>",
(map qq(<a href="http://$_.com">$_</a>) qw/foo bar baz/),
"</body></html>";
$find_links->parse($html);
【讨论】:
使用 LWP::Simple 下载此页面(正如我在下面的 perl 示例中所做的那样)表明您找到了没有 href 的 a (但有名称),所以我们只想检查在打印之前有 一个 href。【参考方案9】:语言 Perl 图书馆:HTML::LinkExtor
Perl 的美妙之处在于您拥有用于非常具体任务的模块。比如链接提取。
整个程序:
#!/usr/bin/perl -w
use strict;
use HTML::LinkExtor;
use LWP::Simple;
my $url = 'http://www.google.com/';
my $content = get( $url );
my $p = HTML::LinkExtor->new( \&process_link, $url, );
$p->parse( $content );
exit;
sub process_link
my ( $tag, %attr ) = @_;
return unless $tag eq 'a';
return unless defined $attr 'href' ;
print "- $attr'href'\n";
return;
解释:
使用严格 - 打开“严格”模式 - 简化潜在的调试,而不是完全 与示例相关 使用 HTML::LinkExtor - 加载有趣的模块 使用 LWP::Simple - 只是获取一些用于测试的 html 的简单方法 my $url = 'http://www.google.com/' - 我们将从哪个页面提取 url 我的 $content = get( $url ) - 获取页面 html my $p = HTML::LinkExtor->new( \&process_link, $url ) - 创建 LinkExtor 对象,为其提供将用作每个 url 回调的函数的引用,并将 $url 用作相对的 BASEURL网址 $p->parse($content) - 我猜很明显 退出 - 程序结束 子 process_link - 函数 process_link 的开始 my ($tag, %attr) - 获取参数,即标签名称及其属性 return unless $tag eq 'a' - 如果标签不是 则跳过处理 除非定义否则返回 $attr'href' - 如果 标记没有 href 属性则跳过处理 打印“-$attr'href'\n”; - 我猜很明显:) 返回; - 完成功能就是这样。
【讨论】:
很好,但我认为您错过了问题的重点,示例在那里是为了使代码相似,而不是因为我想要链接。用更一般的术语来思考。目标是为人们提供使用解析器而不是正则表达式的工具。 我可能遗漏了一些东西,但我在问题描述中读到:“为了保持一致性,我要求该示例为锚标记中的 href 解析 HTML 文件。”如果您要求例如解析语言:红宝石 图书馆:Nokogiri
#!/usr/bin/env ruby
require 'nokogiri'
require 'open-uri'
document = Nokogiri::HTML(open("http://google.com"))
document.css("html head title").first.content
=> "Google"
document.xpath("//title").first.content
=> "Google"
【讨论】:
【参考方案11】:语言:通用 Lisp 图书馆:Closure Html、Closure Xml、CL-WHO
(使用 DOM API 显示,不使用 XPATH 或 STP API)
(defvar *html*
(who:with-html-output-to-string (stream)
(:html
(:body (loop
for site in (list "foo" "bar" "baz")
do (who:htm (:a :href (format nil "http://~A.com/" site))))))))
(defvar *dom*
(chtml:parse *html* (cxml-dom:make-dom-builder)))
(loop
for tag across (dom:get-elements-by-tag-name *dom* "a")
collect (dom:get-attribute tag "href"))
=>
("http://foo.com/" "http://bar.com/" "http://baz.com/")
【讨论】:
是否 collect 或 dom:get-attribute 正确处理未设置 href 的标签? 取决于正确性的定义。在显示的示例中,将为没有“href”属性的“a”标签收集空字符串。如果循环被重写为 (loop for tag across (dom:get-elements-by-tag-name dom "a") when (string/= (dom:get-attribute tag "href") "") collect (dom:get-attribute tag "href")) 那么只会收集非空的 "href"。 其实不是when (string/= (dom:get-attribute tag "href") "") 而是when (dom:has-attribute tag "href") 没有循环宏你会怎么做?【参考方案12】:语言:Clojure 库:Enlive(Clojure 的基于选择器(à la CSS)的模板和转换系统)
选择器表达式:
(def test-select
(html/select (html/html-resource (java.io.StringReader. test-html)) [:a]))
现在我们可以在 REPL 中执行以下操作(我在 test-select
中添加了换行符):
user> test-select
(:tag :a, :attrs :href "http://foo.com/", :content ["foo"]
:tag :a, :attrs :href "http://bar.com/", :content ["bar"]
:tag :a, :attrs :href "http://baz.com/", :content ["baz"])
user> (map #(get-in % [:attrs :href]) test-select)
("http://foo.com/" "http://bar.com/" "http://baz.com/")
您需要以下内容才能试用:
序言:
(require '[net.cgrand.enlive-html :as html])
测试 HTML:
(def test-html
(apply str (concat ["<html><body>"]
(for [link ["foo" "bar" "baz"]]
(str "<a href=\"http://" link ".com/\">" link "</a>"))
["</body></html>"])))
【讨论】:
不确定我是否将 Enlive 称为“解析器”,但我肯定会用它来代替它,所以——这里有一个例子。【参考方案13】:语言:Perl 图书馆:XML::Twig
#!/usr/bin/perl
use strict;
use warnings;
use Encode ':all';
use LWP::Simple;
use XML::Twig;
#my $url = 'http://***.com/questions/773340/can-you-provide-an-example-of-parsing-html-with-your-favorite-parser';
my $url = 'http://www.google.com';
my $content = get($url);
die "Couldn't fetch!" unless defined $content;
my $twig = XML::Twig->new();
$twig->parse_html($content);
my @hrefs = map
$_->att('href');
$twig->get_xpath('//*[@href]');
print "$_\n" for @hrefs;
警告:像这样的页面可能会出现宽字符错误(将 url 更改为被注释掉的页面会出现此错误),但上面的 HTML::Parser 解决方案不存在此问题。
【讨论】:
很好,我一直在使用 XML::Twig,但从未意识到有 parse_html 方法。【参考方案14】:语言:Perl 图书馆:HTML::Parser 目的:How can I remove unused, nested HTML span tags with a Perl regex?
【讨论】:
【参考方案15】:语言:Java 库:XOM、TagSoup
我在此示例中故意包含格式错误且不一致的 XML。
import java.io.IOException;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Node;
import nu.xom.Nodes;
import nu.xom.ParsingException;
import nu.xom.ValidityException;
import org.ccil.cowan.tagsoup.Parser;
import org.xml.sax.SAXException;
public class HtmlTest
public static void main(final String[] args) throws SAXException, ValidityException, ParsingException, IOException
final Parser parser = new Parser();
parser.setFeature(Parser.namespacesFeature, false);
final Builder builder = new Builder(parser);
final Document document = builder.build("<html><body><ul><li><a href=\"http://google.com\">google</li><li><a HREF=\"http://reddit.org\" target=\"_blank\">reddit</a></li><li><a name=\"nothing\">nothing</a><li></ul></body></html>", null);
final Element root = document.getRootElement();
final Nodes links = root.query("//a[@href]");
for (int linkNumber = 0; linkNumber < links.size(); ++linkNumber)
final Node node = links.get(linkNumber);
System.out.println(((Element) node).getAttributeValue("href"));
TagSoup 默认为文档添加一个引用 XHTML 的 XML 命名空间。我选择在这个示例中抑制它。使用默认行为需要调用root.query
来包含这样的命名空间:
root.query("//xhtml:a[@href]", new nu.xom.XPathContext("xhtml", root.getNamespaceURI())
【讨论】:
我相信两者都可以正常工作。 TagSoup 可以解析任何你可以扔给它的东西。【参考方案16】:语言:C# 库:System.XML(标准 .NET)
using System.Collections.Generic;
using System.Xml;
public static void Main(string[] args)
List<string> matches = new List<string>();
XmlDocument xd = new XmlDocument();
xd.LoadXml("<html>...</html>");
FindHrefs(xd.FirstChild, matches);
static void FindHrefs(XmlNode xn, List<string> matches)
if (xn.Attributes != null && xn.Attributes["href"] != null)
matches.Add(xn.Attributes["href"].InnerXml);
foreach (XmlNode child in xn.ChildNodes)
FindHrefs(child, matches);
【讨论】:
如果 HTML 不是有效的 xml(例如未封闭的 img 标签),这会起作用吗?【参考方案17】:语言:php 库:SimpleXML(和 DOM)
<?php
$page = new DOMDocument();
$page->strictErrorChecking = false;
$page->loadHTMLFile('http://***.com/questions/773340');
$xml = simplexml_import_dom($page);
$links = $xml->xpath('//a[@href]');
foreach($links as $link)
echo $link['href']."\n";
【讨论】:
【参考方案18】:语言:JavaScript 图书馆:DOM
var links = document.links;
for(var i in links)
var href = links[i].href;
if(href != null) console.debug(href);
(使用 firebug console.debug 进行输出...)
【讨论】:
【参考方案19】:语言:Racket
库:(planet ashinn/html-parser:1) 和 (planet clements/sxml2:1)
(require net/url
(planet ashinn/html-parser:1)
(planet clements/sxml2:1))
(define the-url (string->url "http://***.com/"))
(define doc (call/input-url the-url get-pure-port html->sxml))
(define links ((sxpath "//a/@href/text()") doc))
以上示例使用来自新包系统的包:html-parsing 和 sxml
(require net/url
html-parsing
sxml)
(define the-url (string->url "http://***.com/"))
(define doc (call/input-url the-url get-pure-port html->xexp))
(define links ((sxpath "//a/@href/text()") doc))
注意:从命令行使用“raco”安装所需的软件包,使用:
raco pkg install html-parsing
和:
raco pkg install sxml
【讨论】:
【参考方案20】:语言:Python 图书馆:lxml.html
import lxml.html
html = "<html><body>"
for link in ("foo", "bar", "baz"):
html += '<a href="http://%s.com">%s</a>' % (link, link)
html += "</body></html>"
tree = lxml.html.document_fromstring(html)
for element, attribute, link, pos in tree.iterlinks():
if attribute == "href":
print link
lxml 也有一个 CSS 选择器类用于遍历 DOM,这使得使用它与使用 JQuery 非常相似:
for a in tree.cssselect('a[href]'):
print a.get('href')
【讨论】:
嗯,当我尝试运行它时,我收到“ImportError: No module named html”,除了 python-lxml 之外我还需要什么吗? 啊,我有 1.3.6 版本,2.0 及更高版本附带 确实如此。如果您愿意,我可以提供一个使用 lxml.etree 来完成这项工作的示例吗? lxml.html 更能容忍损坏的 HTML。【参考方案21】:语言:Objective-C 图书馆:libxml2 + Matt Gallagher's libxml2 wrappers + Ben Copsey's ASIHTTPRequest
ASIHTTPRequest *request = [ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://***.com/questions/773340"];
[request start];
NSError *error = [request error];
if (!error)
NSData *response = [request responseData];
NSLog(@"Data: %@", [[self query:@"//a[@href]" withResponse:response] description]);
[request release];
else
@throw [NSException exceptionWithName:@"kMyHTTPRequestFailed" reason:@"Request failed!" userInfo:nil];
...
- (id) query:(NSString *)xpathQuery WithResponse:(NSData *)resp
NSArray *nodes = PerformHTMLXPathQuery(resp, xpathQuery);
if (nodes != nil)
return nodes;
return nil;
【讨论】:
【参考方案22】:语言:Perl 图书馆:HTML::TreeBuilder
use strict;
use HTML::TreeBuilder;
use LWP::Simple;
my $content = get 'http://www.***.com';
my $document = HTML::TreeBuilder->new->parse($content)->eof;
for my $a ($document->find('a'))
print $a->attr('href'), "\n" if $a->attr('href');
【讨论】:
也是错误的,必须调用$document->eof;如果你使用 $document->parse($html);并在未设置 href 时打印空行。 恢复到我原来的代码; ->eof() 在此示例中无用;在此示例中,检查 href 是否存在也毫无意义 您是否有不想使用 new_from_content 的原因?【参考方案23】:语言:PHP 库:DOM
<?php
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
$doc->loadHTMLFile('http://***.com/questions/773340');
$xpath = new DOMXpath($doc);
$links = $xpath->query('//a[@href]');
for ($i = 0; $i < $links->length; $i++)
echo $links->item($i)->getAttribute('href'), "\n";
有时将@
符号放在$doc->loadHTMLFile
之前很有用,可以抑制无效的html 解析警告
【讨论】:
几乎和我的 PHP 版本一样 (***.com/questions/773340/…) 你不需要 getAttribute 调用【参考方案24】:语言:Python 图书馆:HTQL
import htql;
page="<a href=a.html>1</a><a href=b.html>2</a><a href=c.html>3</a>";
query="<a>:href,tx";
for url, text in htql.HTQL(page, query):
print url, text;
简单直观。
【讨论】:
【参考方案25】:语言:Ruby 图书馆:Nokogiri
#!/usr/bin/env ruby
require "nokogiri"
require "open-uri"
doc = Nokogiri::HTML(open('http://www.example.com'))
hrefs = doc.search('a').map |n| n['href']
puts hrefs
哪些输出:
/
/domains/
/numbers/
/protocols/
/about/
/go/rfc2606
/about/
/about/presentations/
/about/performance/
/reports/
/domains/
/domains/root/
/domains/int/
/domains/arpa/
/domains/idn-tables/
/protocols/
/numbers/
/abuse/
http://www.icann.org/
mailto:iana@iana.org?subject=General%20website%20feedback
这是对上面的一个小改动,产生可用于报告的输出。我只返回 href 列表中的第一个和最后一个元素:
#!/usr/bin/env ruby
require "nokogiri"
require "open-uri"
doc = Nokogiri::HTML(open('http://nokogiri.org'))
hrefs = doc.search('a[href]').map |n| n['href']
puts hrefs
.each_with_index # add an array index
.minmax |a,b| a.last <=> b.last # find the first and last element
.map |h,i| '%3d %s' % [1 + i, h ] # format the output
1 http://github.com/tenderlove/nokogiri
100 http://yokolet.blogspot.com
【讨论】:
【参考方案26】:语言:Java 图书馆:jsoup
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.xml.sax.SAXException;
public class HtmlTest
public static void main(final String[] args) throws SAXException, ValidityException, ParsingException, IOException
final Document document = Jsoup.parse("<html><body><ul><li><a href=\"http://google.com\">google</li><li><a HREF=\"http://reddit.org\" target=\"_blank\">reddit</a></li><li><a name=\"nothing\">nothing</a><li></ul></body></html>");
final Elements links = document.select("a[href]");
for (final Element element : links)
System.out.println(element.attr("href"));
【讨论】:
【参考方案27】:使用 phantomjs,将此文件另存为 extract-links.js:
var page = new WebPage(),
url = 'http://www.udacity.com';
page.open(url, function (status)
if (status !== 'success')
console.log('Unable to access network');
else
var results = page.evaluate(function()
var list = document.querySelectorAll('a'), links = [], i;
for (i = 0; i < list.length; i++)
links.push(list[i].href);
return links;
);
console.log(results.join('\n'));
phantom.exit();
);
运行:
$ ../path/to/bin/phantomjs extract-links.js
【讨论】:
【参考方案28】:语言:Coldfusion 9.0.1+
图书馆:jSoup
<cfscript>
function parseURL(required string url)
var res = [];
var javaLoader = createObject("javaloader.JavaLoader").init([expandPath("./jsoup-1.7.3.jar")]);
var jSoupClass = javaLoader.create("org.jsoup.Jsoup");
//var dom = jSoupClass.parse(html); // if you already have some html to parse.
var dom = jSoupClass.connect( arguments.url ).get();
var links = dom.select("a");
for(var a=1;a LT arrayLen(links);a++)
var s=;s.href= links[a].attr('href'); s.text= links[a].text();
if(s.href contains "http://" || s.href contains "https://") arrayAppend(res,s);
return res;
//writeoutput(writedump(parseURL(url)));
</cfscript>
<cfdump var="#parseURL("http://***.com/questions/773340/can-you-provide-examples-of-parsing-html")#">
返回一个结构数组,每个结构包含一个 HREF 和 TEXT 对象。
【讨论】:
【参考方案29】:语言:JavaScript/Node.js
库:Request 和 Cheerio
var request = require('request');
var cheerio = require('cheerio');
var url = "https://news.ycombinator.com/";
request(url, function (error, response, html)
if (!error && response.statusCode == 200)
var $ = cheerio.load(html);
var anchorTags = $('a');
anchorTags.each(function(i,element)
console.log(element["attribs"]["href"]);
);
);
请求库下载 html 文档,而 Cheerio 允许您使用 jquery css 选择器来定位 html 文档。
【讨论】:
以上是关于你能提供解析HTML的例子吗?的主要内容,如果未能解决你的问题,请参考以下文章