2

毎月中国に輸入されるさまざまな商品の価値と数量 (量) を示すデータ保持テーブルの複数ページをスクレイピングして抽出しようとしています。最終的には、このデータをさらに処理するためにテキスト ファイルに書き出す必要があります。これは、そのようなページのスクリーンショットです。

インポートされた中国のデータ

具体的には、輸入される商品の名前、体積の単位 (例: トン、kg)、実際の値と体積、合計 4 つのフィールドを抽出したいと思います。私が遭遇した問題は、抽出したいテーブルが異なる深さにあるように見えることです。

「ボリューム」と「値」のフィールドは同じ深さにあるため、抽出できます。したがって、次のようなデバッグ出力が得られます。

2,314 --- 2,943
20,082 --- 80,176
7 --- 62,426

「name」および「units」フィールドは、「volume」および「value」フィールドとは異なるレベル (私が思うに) にあるため、4 つのフィールドすべてにヘッダーを使用すると、それらは取得されません。ただし、それらをサブテーブルとして抽出しようとすると、正常に動作し、次のデバッグ出力が得られます。

啤酒 --- 千升
葡萄酒 --- 千升
饲料用鱼粉 --- 万吨

これをどのように解決すればよいですか?私の最初の考えは、各テーブルを個別に抽出し、各テーブルの各行をループし、1 つのテーブルに 2 つのフィールドを追加し、もう 1 つのテーブルに 2 つのフィールドを追加して、各行に 4 つの要素を持つ配列に追加することです。(R私はデータフレームを作成してcbindこれに使用すると思います。)これは実現可能に思えますが、最適ではありません。だから最初に私は尋ねたい:

1)HTML::TableExtractテーブルの両方のサブセットを抽出してそれらを結合する簡単な方法はありますか?

2) データを 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";
    }
}
4

1 に答える 1

2

テーブルがネストされたテーブルを実装しているように見えます。その場合、テーブルを個別に解析し、後で結果をつなぎ合わせることで、アプリケーションがそれを補う必要があるように思えます。

私はあなたのサンプルコードでいくつかの自由を取り、これを思いつきました:

#!/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 がワイド文字を処理する (または処理しない) 方法に少しがっかりしています。しかし、この例ではそれは的外れだと思います。

于 2012-11-18T04:17:53.440 に答える