在嵌套信息上使用HTML::TreeBuilder::XPath



假设一个HTML页面是一个具有重复结构的报告:

<html>
  <body>
    <h1>Big Hairy Report Page</h1>
    <div class="customer">
      <div class="customer_id">001</div>
      <div class="customer_name">Joe Blough</div>
      <div class="customer_addr">123 That Road</div>
      <div class="customer_city">Smallville</div>
      <div class="customer_state">Nebraska</div>
      <div class="order_info">
        <div class="shipping_details">
          <ul>
             <li>Large crate</li>
             <li>Fragile</li>
             <li>Express</li>
          </ul>
        </div>
        <div class="order_item">Deluxe Hoodie</div>
        <div class="payment">35.95</div>
        <div class="order_id">000123456789</div>
      </div>
      <div class="comment">StackOverflow rocks!</div>
    </div>
   <div class="customer">
     <div class="customer_id">002</div>
 ....  and so forth for a list of 150 customers

这种报表页面经常出现。我的目标是使用HTML::TreeBuilder::XPath将每个客户的相关信息提取到一些合理的数据结构中。

我知道做一些基本的工作,把文件读入$tree。但是,如何才能简洁地遍历该树并获得每个客户的相关信息簇呢?例如,如何根据这些信息创建按客户编号排序的地址标签列表?如果我想按州对所有客户信息进行排序,该怎么办?

我没有要求整个perl(我可以读取我的文件,输出到文件,等等)。我只需要帮助理解如何向HTML::TreeBuilder::XPath请求这些相关数据束,以及如何解引用它们。如果用输出语句更容易表达这一点(例如,Joe Blough订购了1件Deluxe Hoodie并留下了1条评论),那么这也很酷。

非常感谢那些解决这个问题的人,这对我来说似乎有点难以应付。

这将满足您的需求。

它首先将所有<div class="customer">元素拉入数组@customers并从中提取信息。

我已经采用了您的地址标签示例,按客户编号排序(我假设您指的是class="customer_id"字段)。所有的地址值都从数组中提取到散列%customers中,由客户ID和元素类的名称作为键值。然后按ID的顺序打印信息。

use strict;
use warnings;
use HTML::TreeBuilder::XPath;
my $tree = HTML::TreeBuilder::XPath->new_from_file('html.html');
my @customers = $tree->findnodes('/html/body/div[@class="customer"');
my %customers;
for my $cust (@customers) {
  my $id = $cust->findvalue('div[@class="customer_id"]');
  for my $field (qw/ customer_name customer_addr customer_city customer_state /) {
    my $xpath = "div[@class='$field']";
    my $val = $cust->findvalue($xpath);
    $customers{$id}{$field} = $val;
  }
}
for my $id (sort keys %customers) {
  my $info = $customers{$id};
  print "Customer ID $idn";
  print $info->{customer_name}, "n";
  print $info->{customer_addr}, "n";
  print $info->{customer_city}, "n";
  print $info->{customer_state}, "n";
  print "n";
}

Customer ID 001
Joe Blough
123 That Road
Smallville
Nebraska
use HTML::TreeBuilder::XPath;
...
my @customers;
my $tree = HTML::TreeBuilder::XPath->new_from_content( $mech->content() );
foreach my $customer_section_node ( $tree->findnodes('//div[ @class = "customer" ]') ) {
   my $customer = {};
   $customer->{id} = find_customer_id($customer_section_node);
   $customer->{name} = find_customer_name($customer_section_node);
   ...
   push @customers, $customer;
}
$tree->delete();
sub find_customer_id {
    my $node = shift;
    my ($id) = $node->findvalues('.//div[ @class = "customer_id" ]');
    return $id
}

我将使用XML::LibXML,因为它更快,我很熟悉它,但是如果你愿意的话,将我发布的内容从XML::LibXML转换为HTML::TreeBuilder::XPath应该是非常简单的。

use XML::LibXML qw( );
sub get_text { defined($_[0]) ? $_[0]->textContent() : undef }
my $doc = XML::LibXML->load_html(...);
my @customers;
for my $cust_node ($doc->findnodes('/html/body/div[@class="customer"]')) {
   my $id   = get_text( $cust_node->findnodes('div[@class="customer_id"]') );
   my $name = get_text( $cust_node->findnodes('div[@class="customer_name"]') );
   ...
   push @customers, {
      id   => $id,
      name => $name,
      ...
   };
}

实际上,考虑到数据的规律性,您不必硬编码字段名。

use XML::LibXML qw( );
sub parse_list {
   my ($node) = @_;
   return [
      map parse_field($_),
       $node->findnodes('li')
   ];
}
sub parse_field {
   my ($node) = @_;
   my @children = $node->findnodes('*');
   return $node->textContent() if !@children;
   return parse_list($children[0]) if $children[0]->nodeName() eq 'ul';
   return {
      map { $_->getAttribute('class') => parse_field($_) }
       @children
   };
}
{
   my $doc = XML::LibXML->load_html( ... );
   my @customers =
      map parse_field($_),
       $doc->findnodes('/html/body/div[@class="customer"]');
   ...
}

相关内容

  • 没有找到相关文章

最新更新