0

私はPerlに比較的慣れていません。テキストのみを抽出して別のファイルに出力したい URL のリストがあります。ここに私のコードのサンプルがあります:

#!/usr/bin/perl -w

use strict;
use locale;
use warnings;
#use diagnostics;
use utf8;

binmode(STDIN, "encoding(utf8)");
binmode(STDOUT, "encoding(utf8)");
binmode(STDERR, "encoding(utf8)");

use LWP::Simple;
use HTML::Parse;

open (CLEANURL, '<:utf8', "clean_keyword_url_5.3.txt")  || die ("Cannot open File\n");
open(STORECODE, '>:utf8', "Bstored_keyword_url_5.3.txt")  || die ("Cannot open File\n");

my $url2parse;
my @arg = <CLEANURL>;
close (CLEANURL);

foreach my $arg(@arg) {
    $url2parse = parse_html(get($arg))->format;
    print STORECODE $url2parse;
}

close (STORECODE);

には次のclean_keyword_url_5.3.txtようなリンクがあります:

http://www.ladepeche.fr/article/2013/01/31/1548850-aulon-l-activite-est-paralysee.html#xtor=RSS-6

http://tdg.ch/monde/faits-divers/Deux-alpinistes-meurent-dans-une-avalanche-en-Isere/story/10446351

主にフランスまたはスイスの地方紙です。各リンクを個別のフォルダーに出力したいのですが、ファイル ハンドルの配列と の「getstore」メソッドを使用してこれを実行しようとしましたがLWP::Simple、すべてのリンクでループを作成できません。すべてのファイルを作成しますが、それぞれに 1 つの URL のコンテンツのみを出力します。アレイでの実行に関する情報が見つかりませんLWP::Simple。誰もがこのモジュールを 1 つまたは 2 つの URL だけで使用しているようです。

また、次のようなハッシュのアイデアもありました。

#!/usr/bin/perl -w

use strict;
use locale;
use warnings;
#use diagnostics;
use utf8;

binmode(STDIN, "encoding(utf8)");
binmode(STDOUT, "encoding(utf8)");
binmode(STDERR, "encoding(utf8)");

use LWP::Simple;
use HTML::Parse;

open (CLEANURL, '<:utf8', "clean_keyword_url_5.3.txt") || die ("Cannot open File\n");
#open(STORECODE, '>:utf8', "Bstored_keyword_url_5.3.html")  || die ("Cannot open File\n");

my $url2parse;
my @arg = <CLEANURL>;
close (CLEANURL);

my @filehandles;
my $i;

for ($i = 0; $i<@arg; $i++){
    local *FILE;
    open (FILE, '>:utf8', "Bstored_keyword_url_5.3.$i.html")|| die;
    push (@filehandles, *FILE);
}

foreach my $arg(@arg) {
    $url2parse = parse_html(get($arg))->format;
    foreach my $file(@filehandles){
       my %hash = {key => $file};
       $hash{key} .= $val;
       print $file "$hash{key}";    
    }
}

#close (STORECODE);

このコードが機能しないことに気付いたかもしれません。問題は、私がそれについて自分の心を包むことができないということです.

ですので、何かお気づきの点がございましたら大変助かります。ありがとうございました !!!

4

2 に答える 2

0

(最後にアップデート 1 を参照)

一般的なコメント:

Perl を作成する際に、ベスト プラクティス (厳格、警告、および明示的なエンコーディング) を使用しようとしているのを見るのは非常に良いことです。使用localeが良い場合は議論される可能性があります…。改善できる点は次の 3 つです。

  1. 最も内側のスコープで変数を宣言します。
  2. レキシカルファイルハンドルを使う
  3. エラーをチェックする

最初のスクリプト

  • open my $fh, "<", $filename or die $!ファイルハンドルをレキシカル変数または何かに開くのがベストプラクティスと考えられています。には、失敗の$!理由が含まれています。オープンに失敗した実際の原因 (権限がない、そのようなパスがないなど)の貴重なヒントが含まれているため、出力に含めます。
  • ファイルから読み取ると、URL の末尾にまだ改行があります。chomp @argそれらを削除するかs/\s+$// for @arg;、末尾のスペース文字を削除するには、実行してください。
  • LWP::Simple失敗する可能性があります。実際に応答があるかどうかを確認する必要があります。

    my $content = get $url;
    die "Didn't receive anything from $url" unless defined $content;
    

    LWP::UserAgentこれにより、より多くのエラーチェックが可能になるため、を使用することをお勧めします。

    my $ua = LWP::UserAgent->new;
    ...;
    my $response = $ua->get($url);
    
    die "Request for $url failed: " . $response->status_line unless $response->is_sucess
    
    my $content = $response->decoded_content;
    
  • 同じことが HTML の解析にも当てはまります。モジュールのドキュメントを調べて、エラーがないか確認してください。

2 番目のスクリプト

この 2 番目のスクリプトでも同じことが言えます。加えて:

  • C スタイルの for ループでは、 o から の最大インデックスまでの範囲を反復処理します@arg。また、変数$iはループ内でのみ使用されるため、ここで宣言します。

    for my $i (0 .. $#arg) { ... }
    

    シジルは、配列の$#最高のインデックスを示します。..リストコンテキストの範囲演算子です。範囲を使用したくない場合でも、 var のように宣言できますfor(my $i = 0; $i < @arg; $i++) { ... }

  • このlocal関数は、グローバル変数の名前を受け取り、それを特別なスタックにバックアップし、その名前に新しい値を一時的に割り当てることができるようにします。現在のスコープを離れると、古い変数が復元されます (スコープは中かっこで大まかに区切られます)。本当に強制されない限り、使用しないでください (「スコーピングの対処法」を参照してください)。ベアワード、型グロブ、型グロブへの参照、およびファイルハンドルは同じではないことに注意してください。ファイルハンドルを字句変数に開くだけで、型グロブに手を出さないでください。バグが解決する可能性があります。

    my @filehandles;
    for my $i (0 .. $#arg) {
      my $filename = "Bstored_keyword_url_5.3.$i.html";
      open my $fh, "<:utf8", $filename or die "Can't open $filename: $!";
      push @filehandles, $fh;
      # lexical filehandles are automatically closed once their reference count hits zero.
    }
    

    または、少し進んで、整数とファイルハンドルの間に直接のマッピングがあることがわかり、このマッピングとして配列を表現できます。上記と完全に同等:

    # how map works: OUTPUT = map { BLOCK } INPUT-LIST
    my @filehandles = map {
      # current value is in $_
      my $filename = "Bstored_keyword_url_5.3.$_.html";
      open my $fh, "<:utf8", $filename or die "Can't open $filename: $!";
      $fh; # last statement determines what is put into the array.
    } 0 .. $#arg;
    
  • ハッシュはいいね。ハッシュは偶数値のリストとして構築されることに注意してください。は配列を構築せず、無名配列参照を構築するのと同様に、{}無名ハッシュ参照を構築します。参照は常にスカラーで保持されます。[]

    my $hashref = { foo => 1, bar => 3 };
    say $hashref->{foo}; # dereference arrow needed
    

    また

    my %hash = ( foo => 1, bar => 3 );
    say $hash{foo};
    

    括弧は優先順位を整理するためだけにあり、リストや配列を「作成」しません。

  • 使用したばかりのとおり: このsay関数は perl 5.10 以降で使用できます。use feature 'say'またはuse 5.010(またはそれ以降のバージョン番号)を使用してアクティブ化できます。とまったく同じように機能しますが、出力セパレータ(通常は改行) をprint出力に追加します。$\

  • 最後のループでは、変数を使用します$val。それはどこから来たのですか?また、ファイルハンドルに文字列を追加しないでください。これにより、ファイルハンドルが文字列化され、(a) 使用できなくなり、(b) かなり役に立たない文字列 (おそらくIO::File=GLOB(0xdeadbeef)) が提供されます。また、1 つの (!) キーにのみ使用する場合は、ハッシュを使用しないでください。

リンク

  1. LWP::簡単なドキュメント
  2. LWP::UserAgent ドキュメント
  3. HTML::Parseは非推奨だと言っています。HTML::ParserまたはHTML::TreeBuilderを参照してください。
  4. スコープへの対処: グローバル変数または「ローカル」変数を使用してはならない理由。

更新 1

あなたの新しいコードもかなりいいです (もっと多くの人に最初から使ってもらいたいstrictです…)。に何かを入れる$url2parseのを忘れていることと、ループの創造的な用途を見つけたことを除いて ;-)

getstore最近ではor mirroror と呼ばれるものを使用することをお勧めします。これは、ファイル ハンドルを手動で開く必要がないことを意味します。

これは次のようにコーディングできます。

...;
# ↓ lexical filehandles 'n stuff
open my $CLEANURL, "<:utf8", ... or die ...;
my $counter = 0;
while(my $link = <$CLEANURL>) {
  chomp $link; # remove evil newlines
  my $status = mirror($link => "Cstored_keyword_url_5.3.$counter.txt");
  200 == $status or warn "WARNING: fetching $link failed with status $status";
  $counter++;
}

ループは 3 つではなく 1 つだけです。mirror関数 from LWP::Simple(も同様に機能します)を使用するgetstoreので、ファイル名を渡すだけでよく、ファイルハンドルは気にしません。

while (my $line = <$filehandle>) { ... }、Perl でファイルを 1 行ずつ読み取るためによく使用されます。このような小規模なプログラムでは関係ありませんが、データがスケーリングされる場合は良い習慣です…</p>

手動のコードでは、上記は次のようになります。

...;
my $counter = 0;
while (my $link = <$CLEANURL>) {
  chomp $link;
  my $filename = "Cstored_keyword_url_5.3.$counter.txt";
  open my $fh, ">:utf8", $filename or die "Couldn't open $filename: $!";
  my $content = get $link;
  if (defined $content) { print $fh $content }
  else                  { warn "WARNING: failed to fetch $link" }
  # $fh autocloses here
  $counter++;
}

私はまだ を使用することに賛成ですLWP::UserAgent

URL を正しく取得して処理できるようになったら、並列処理が速度を上げるための次のステップになる可能性があります。

于 2013-02-04T15:52:07.653 に答える
0

amon のおかげで、これは更新されたコードです:

#!/usr/bin/perl -w

use strict;
use locale;
use warnings;
#use diagnostics;
use utf8;

binmode(STDIN, "encoding(utf8)");
binmode(STDOUT, "encoding(utf8)");
binmode(STDERR, "encoding(utf8)");

use LWP::Simple;
use HTML::Parse;

open (CLEANURL, '<:utf8', "clean_keyword_url_5.3.txt")  || die ("Cannot open File\n");
open (STORECODE, '>:utf8', "all_articles.txt")  || die ("Cannot open File\n");

my $counter = 0;

while(my $link = <CLEANURL>){
    chomp ($link);
    my $filename = "cstored_keyword_url_5.3.$counter.txt";
    open (my $fh, ">:utf8", $filename) || die "Couldn't open $filename: $!";
    my $content = get($link);
    unless (defined $link){
        warn "WARNING: Failed to fetch $link";
    }
    $content = parse_html($content) || die "Couldn't parse $content";
    my $text = $content->format;
    if (defined $text){
       print $fh $text;
       print STORECODE $text;
    }else{
       warn "WARNING: Failed to fetch $link";
    }
    $counter++; 
}

close (CLEANURL);
close (STORECODE);

元の投稿で既に述べたように、「clean_keyword_url_5.3.txt」にはニュース記事への URL が含まれています。LWP::Simple から get メソッドを使用するには、1 行ごとに 1 つの URL でなければなりません。再度、感謝します !!!

于 2013-02-06T08:11:40.807 に答える