我正在尝试抓取和提取多页数据保存表,显示每月进口到中国的各种商品的价值和数量(体积)。最终我想将此数据写入文本文件以进行进一步处理。这是这样一个页面的屏幕截图。
具体来说,我想提取进口商品的名称、体积单位(例如吨、公斤)、实际价值和体积,总共4个字段。我遇到的问题是我想要提取的表似乎处于不同的深度。
我可以提取“体积”和“值”的字段,因为它们处于相同的深度,所以我得到如下调试输出:
2,314 --- 2,943
20,082 --- 80,176
7 --- 62,426
“名称”和“单位”字段与“数量”和“值”字段处于不同的级别(我认为),因此当我对所有 4 个字段使用标题时,不会拾取它们。但是,如果我尝试将它们提取为子表,它可以正常工作,给出以下调试输出:
啤酒 --- 千升
葡萄酒 --- 千升
饲料用鱼粉 --- 万吨
我该如何解决这个问题?我的第一个想法是单独提取每个表,循环遍历每个表的每一行,将一个表中的 2 个字段和另一个表中的 2 个字段添加到一个数组中,该数组每行有 4 个元素。 (在
R
中,我想我会创建一个数据框并使用 cbind
来实现这一点。)这似乎可行,但感觉不是最佳的。首先我想问:
1)是否有一种直接的方法告诉
HTML::TableExtract
提取表的两个子集并将它们组合起来?
2)如果我必须将数据提取为两个单独的表并将它们组合起来,最有效的方法是什么?
我到目前为止的代码如下:
use strict;
use HTML::TableExtract;
use Encode;
use utf8;
use WWW::Mechanize;
use Data::Dumper;
binmode STDOUT, ":utf8";
# Chinese equivalents of the various headings
my $txt_header = "单位:千美元";
my $txt_name = "商品名称";
my $txt_units = "计量单位";
my $txt_volume = "数量";
my $txt_value = "金额";
# Chinese Customs site
my $url = "http://www.chinacustomsstat.com/aspx/1/newdata/record_class.aspx?page=2&guid=951";
my $mech = WWW::Mechanize->new( agent => 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)');
my $page = $mech->get( $url );
my $htmlstuff = $mech->content();
print ("\nFirst table with two headers (volume and value) at same depth\n\n");
my $te = new HTML::TableExtract( depth => 1, headers => [ ( $txt_volume, $txt_value ) ]);
$te->parse($htmlstuff);
# See what we have
foreach my $ts ( $te->tables ) {
print "Table (", join( ',', $ts->coords ), "):\n";
foreach my $row ( $ts->rows ) {
print join( ' --- ', @$row ), "\n";
}
}
print ("\nSecond table with 'name' and 'units'\n");
$te = new HTML::TableExtract( headers => [ ( $txt_name, $txt_units ) ]);
$te->parse($htmlstuff);
# See what we have in the other table
foreach my $ts ( $te->tables ) {
print "Table (", join( ',', $ts->coords ), "):\n";
foreach my $row ( $ts->rows ) {
print join( ' --- ', @$row ), "\n";
}
}
看起来该表确实是实现了嵌套表。 既然如此,在我看来,您的应用程序将需要通过单独解析表格并随后将结果拼接在一起来弥补这一点。
我对你的示例代码进行了一些修改,并提出了这个:
#!/usr/bin/perl
use utf8;
use strict;
use warnings;
{
use WWW::Mechanize;
use HTML::TableExtract;
use Data::Dumper;
use Text::FormatTable;
}
binmode STDOUT, ':utf8';
my $txt_name = '商品名称';
my $txt_units = '计量单位';
my $txt_volume = '数量';
my $txt_value = '金额';
my $url
= 'http://www.chinacustomsstat.com'
. '/aspx/1/newdata/record_class.aspx'
. '?page=2&guid=951';
my $mech = WWW::Mechanize->new(
agent => 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)' );
my $page = $mech->get($url);
my $html = $mech->content();
my %data_for;
{
my %config_for = (
products => {
values => [],
headers => [ $txt_name, $txt_units ],
},
data => {
values => [],
headers => [ $txt_volume, $txt_value ],
},
);
for my $type ( keys %config_for ) {
my $config_rh = $config_for{$type};
my $te = HTML::TableExtract->new( headers => $config_rh->{headers} );
$te->parse($html);
for my $ts ( $te->tables() ) {
for my $row_ra ( $ts->rows() ) {
if ( defined $row_ra->[0] ) {
push @{ $config_rh->{values} }, $row_ra;
}
}
}
}
if ( @{ $config_for{products}->{values} }
!= @{ $config_for{data}->{values} } )
{
warn 'not as many value rows were parsed as product rows';
}
for my $i ( 0 .. $#{ $config_for{products}->{values} } ) {
my $product_ra = $config_for{products}->{values}->[$i];
my $data_ra = $config_for{data}->{values}->[$i];
my ( $product, $units ) = @{$product_ra};
my ( $volume, $value ) = @{$data_ra};
$data_for{$product} = {
units => $units,
volume => $volume,
value => $value,
};
}
}
# process results in %data_for hash
{
my $table = Text::FormatTable->new('| l | l | l | l |');
$table->head( $txt_name, $txt_units, $txt_volume, $txt_value, );
$table->rule('=');
for my $product ( keys %data_for ) {
$table->row(
$product,
@{ $data_for{$product} }{qw( units volume value )}
);
$table->rule('-');
}
print $table->render();
}
__END__
我对 Text::FormatTable 处理(或不处理)宽字符的方式有点失望。但我认为这不是这个例子的重点。