Comment analyser le HTML avec une variété de langues et de bibliothèques d'analyse?
En répondant:
Les commentaires individuels seront liés aux réponses aux questions sur la façon d’analyser le HTML avec des expressions rationnelles afin de montrer la bonne façon de faire les choses.
Par souci de cohérence, je demande que l'exemple analyse un fichier HTML pour la variable href
dans les balises d'ancrage. Pour faciliter la recherche dans cette question, je vous demande de suivre ce format.
Langue: [nom de la langue]
Bibliothèque: [nom de la bibliothèque]
[example code]
Faites de la bibliothèque un lien vers la documentation de la bibliothèque. Si vous souhaitez fournir un exemple autre que l'extraction de liens, veuillez également inclure:
But: [ce que l'analyse fait]
Langue: JavaScript
Bibliothèque: jQuery
$.each($('a[href]'), function(){
console.debug(this.href);
});
(en utilisant firebug console.debug pour la sortie ...)
Et en chargeant une page HTML:
$.get('http://stackoverflow.com/', function(page){
$(page).find('a[href]').each(function(){
console.debug(this.href);
});
});
Utilisé une autre chaque fonction pour celle-ci, je pense que c'est plus propre lors de l'enchaînement des méthodes.
Langue: C #
Bibliothèque: HtmlAgilityPack
class Program
{
static void Main(string[] args)
{
var web = new HtmlWeb();
var doc = web.Load("http://www.stackoverflow.com");
var nodes = doc.DocumentNode.SelectNodes("//a[@href]");
foreach (var node in nodes)
{
Console.WriteLine(node.InnerHtml);
}
}
}
langue: Python
bibliothèque: 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
sortie:
[<a href="http://foo.com">foo</a>,
<a href="http://bar.com">bar</a>,
<a href="http://baz.com">baz</a>]
également possible:
for link in links:
print link['href']
sortie:
http://foo.com
http://bar.com
http://baz.com
Langue: Perl
Bibliothèque: 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;
}
);
langue: Shell
library: lynx (enfin, ce n'est pas une bibliothèque, mais dans Shell, chaque programme est une sorte de bibliothèque)
lynx -dump -listonly http://news.google.com/
langue: Ruby
bibliothèque: 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'] }
langue: Python
bibliothèque: 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)
langue: Perl
bibliothèque: 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);
Langue Perl
Bibliothèque: HTML :: LinkExtor
La beauté de Perl, c’est que vous avez des modules pour des tâches très spécifiques. Comme l'extraction de lien.
Programme entier:
#!/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;
}
Explication:
C'est tout.
Langue: rubis
Bibliothèque: 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"
Langue: LISP commun
Bibliothèque: Fermeture Html , Fermeture Xml , CL-WHO
(affiché à l'aide de l'API DOM sans utiliser XPATH ou l'API STP)
(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/")
Langue: Clojure
Library: Enlive (un système de modélisation et de transformation basé sur un sélecteur (à la CSS) pour Clojure)
Expression du sélecteur:
(def test-select
(html/select (html/html-resource (Java.io.StringReader. test-html)) [:a]))
Maintenant, nous pouvons faire ce qui suit au REPL (j'ai ajouté des sauts de ligne dans 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/")
Vous aurez besoin de ce qui suit pour l'essayer:
Préambule:
(require '[net.cgrand.enlive-html :as html])
Test HTML:
(def test-html
(apply str (concat ["<html><body>"]
(for [link ["foo" "bar" "baz"]]
(str "<a href=\"http://" link ".com/\">" link "</a>"))
["</body></html>"])))
Langue: Java
Bibliothèques: XOM , TagSoup
J'ai inclus du code XML intentionnellement mal formé et incohérent dans cet exemple.
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 ajoute un espace de nom XML référençant XHTML au document par défaut. J'ai choisi de supprimer cela dans cet exemple. L'utilisation du comportement par défaut nécessiterait l'appel de root.query
pour inclure un espace de noms comme suit:
root.query("//xhtml:a[@href]", new nu.xom.XPathContext("xhtml", root.getNamespaceURI())
langue: Perl
bibliothèque: XML :: Twig
#!/usr/bin/Perl
use strict;
use warnings;
use Encode ':all';
use LWP::Simple;
use XML::Twig;
#my $url = 'http://stackoverflow.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;
mise en garde: peut générer des erreurs de caractères larges avec des pages telles que celle-ci (le fait de modifier l'URL de celle mise en commentaire provoquera l'erreur), mais la solution HTML :: Parser ci-dessus ne partage pas ce problème.
Langue: Perl
Bibliothèque: HTML :: Parser
Objet: Comment puis-je supprimer les balises span HTML imbriquées inutilisées avec une expression rationnelle Perl?
Langue: JavaScript
Bibliothèque: DOM
var links = document.links;
for(var i in links){
var href = links[i].href;
if(href != null) console.debug(href);
}
(en utilisant firebug console.debug pour la sortie ...)
Langue: C #
Bibliothèque: System.XML (standard .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);
}
Langue: Raquette
Bibliothèque: (planet ashinn/html-parser: 1) et (planet clements/sxml2: 1)
(require net/url
(planet ashinn/html-parser:1)
(planet clements/sxml2:1))
(define the-url (string->url "http://stackoverflow.com/"))
(define doc (call/input-url the-url get-pure-port html->sxml))
(define links ((sxpath "//a/@href/text()") doc))
Exemple ci-dessus utilisant des paquets du nouveau système de paquets: html-parsing et sxml
(require net/url
html-parsing
sxml)
(define the-url (string->url "http://stackoverflow.com/"))
(define doc (call/input-url the-url get-pure-port html->xexp))
(define links ((sxpath "//a/@href/text()") doc))
Remarque: Installez les packages requis avec 'raco' à partir d'une ligne de commande, avec:
raco pkg install html-parsing
et:
raco pkg install sxml
Langue: Objective-C
Bibliothèque: libxml2 + Les enveloppes libxml2 de Matt Gallagher + ASIHTTPRequest de Ben Copsey
ASIHTTPRequest *request = [ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:@"http://stackoverflow.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;
}
Langue: Perl
Bibliothèque: HTML :: TreeBuilder
use strict;
use HTML::TreeBuilder;
use LWP::Simple;
my $content = get 'http://www.stackoverflow.com';
my $document = HTML::TreeBuilder->new->parse($content)->eof;
for my $a ($document->find('a')) {
print $a->attr('href'), "\n" if $a->attr('href');
}
langue: Python
bibliothèque: 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 a aussi une classe de sélecteur CSS pour parcourir le DOM, ce qui peut rendre son utilisation très similaire à celle de JQuery:
for a in tree.cssselect('a[href]'):
print a.get('href')
Langue: PHP
Bibliothèque: SimpleXML (et DOM)
<?php
$page = new DOMDocument();
$page->strictErrorChecking = false;
$page->loadHTMLFile('http://stackoverflow.com/questions/773340');
$xml = simplexml_import_dom($page);
$links = $xml->xpath('//a[@href]');
foreach($links as $link)
echo $link['href']."\n";
Langue: Java
Bibliothèque: 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"));
}
}
}
langue: Ruby
bibliothèque: 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
Quelles sorties:
/
/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:[email protected]?subject=General%20website%20feedback
Il s’agit d’une modification mineure de la précédente, qui permet d’obtenir une sortie utilisable pour un rapport. Je ne retourne que les premier et dernier éléments de la liste des hrefs:
#!/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
A l’aide de phantomjs, enregistrez ce fichier sous le nom 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();
});
courir:
$ ../path/to/bin/phantomjs extract-links.js
Langue: JavaScript/Node.js
Bibliothèque: Demande et 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"]);
});
}
});
Request Library télécharge le document HTML et Cheerio vous permet d’utiliser les sélecteurs jquery css pour cibler le document HTML.
Langue: PHP Bibliothèque: DOM
<?php
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
$doc->loadHTMLFile('http://stackoverflow.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";
Il est parfois utile de mettre le symbole @
avant le $doc->loadHTMLFile
pour supprimer les avertissements d'analyse HTML non valides
Langue: Coldfusion 9.0.1+
Bibliothèque: 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://stackoverflow.com/questions/773340/can-you-provide-examples-of-parsing-html")#">
Retourne un tableau de structures, chaque structure contient des objets HREF et TEXT.