掲示板にアクセスしてHTMLデータを取得するところまではできました。これをそのまま保存するだけではなく、「タイトル」「投稿者名」「投稿時間」「本文」といった具合に該当箇所だけを抜き出して保存したいと思っています。
そうなると、HTML全データ($dataとします)からパターンマッチでそれぞれのデータのみを取得する必要がありますが、この$dataからそれぞれのデータをパターンで抜き出す方法が分かりません。
現在までにやってみた方法です。
$data =~ /(タイトルを抜き出すパターン)/g;
$data =~ /(名前を抜き出すパターン)/g;
...
$title = $1;
$name = $2;
しかしこれだと、後方参照の最後の値のみが記憶されているのみで、意図するような結果が得られません。こんな風に、ひとかたまりのデータから複数のパターンマッチで抜き出すには、どうすればよいのでしょうか。ざっと調べただけでは分かりませんでした。よろしくお願いします。
ソースを見てみました。
目的の投稿内容は、途中に改行を含んでいますね。
.(ピリオド)は改行文字を含まないのではありませんか?
perl から離れて久しいので、チョット調べてみました。
/s を付けないと改行は含まれない様ですね。
正規表現マッチの後で変数に代入する。
$data =~ /(タイトルを抜き出すパターン)/g; $title = $1; $data =~ /(名前を抜き出すパターン)/g; $name = $1; ...
あるいは先読み正規表現を使いましょう。
$data =~ /(?=.*?タイトルを抜き出すパターン)(?=.*?名前を抜き出すパターン)/g # gはいらないかも $title = $1; $name = $2;
ありがとうございます!
先読み正規表現というのは初めてで、ちょっとググッたところすぐには理解できそうにない感じがしたので、正規表現マッチの後で変数に代入する方法でやってみました。
ところが、$1に入るデータが重複するという現象が起きてしまいました。書き込み日時や書き込み内容も取得するので、それらもまとめると
$data =~ /(タイトルを抜き出すパターン)/g; $title = $1; $data =~ /(名前を抜き出すパターン)/g; $name = $1; $data =~ /(日時を抜き出すパターン)/g; $date = $1; $data =~ /(内容を抜き出すパターン)/g; $content = $1; print $title, $name, $date, $content;
こんな感じになったのですが、結果は「タイトル、名前、日時、日時」と表示されてしまいます。うーむ…。
追記:どうも、最後の$contentがうまくマッチできていないだけのような気がしてきました。もうちょっとやってみます。
文字列が出現する順番が決まっているなら、
$data =~ /(タイトルを抜き出すパターン).*?(名前を抜き出すパターン)/gm; $title = $1; $name = $2;
とでもしておけばいいはず。
順番が決まっていないなら、地道に
$data =~ /(タイトルを抜き出すパターン)/g; $title = $1; $data =~ /(名前を抜き出すパターン)/g; $name = $1;
とでもするしかないかな。
b-windさんいつも感謝です。
最初のやり方はrubikitchさんがおっしゃる先読み正規表現という事でよろしいでしょうか。実はまったく知らなかったので、分かりやすく説明しているサイトがあれば、ポインタ示していただけると助かります。ざっとググってみたんですが今ひとつ理解できませんでした。
後の方のやり方では、上で述べたように$1に重複してデータが入ってしまうようです。
ありがとうございます。具体的に書かないと分かりにくいですよね。特に問題ないと思いますので、取得したい掲示板のアドレスと、今書いているコードをそのまま書きます。
http://otd1.jbbs.livedoor.jp/18972/bbs_plain?base=1&range=1
こちらの掲示板なのですが、上記パラメータのbase=1の部分をインクリメントして行って、3000件くらいある書き込みデータを引っ張ってこようとしています。
かなり古いHTMLなので名前やタイトルを取得するのにフォントタグ等のパターンで判別するしかないという状態です。
現時点でのコードです。まだループはしていません。
#!/usr/bin/perl use strict; use Encode; use Encode::Guess qw/euc-jp shift-jis/; use LWP::UserAgent; my $ua = LWP::UserAgent->new; my $url = 'http://otd1.jbbs.livedoor.jp/18972/bbs_plain?base= 1&range=1'; my $request = HTTP::Request->new('GET', $url); $ua->request($request, \&parse); sub parse{ my($data, $response, $protocol) = @_; my $decoder = Encode::Guess->guess($data); my $string = $decoder->decode($data); chomp $data; $string =~ /<a name="(.+)">/g; my $serial= $1; $string =~ /<font color="#ff0000">(?!Powered by)(.+)<\/font>/g; my $title = $1; $title = encode('utf8', $title); $string =~ /<font color="#000080">(.+)<\/font>/g; my $name = $1; $name = encode('utf8', $name); $string =~ /<td nowrap>(.+)<\/td>/g; my $mydate = $1; $string =~ /<font color="#006000">(.+)<\/font>/g; my $content = $1; $content = encode('utf8', $content); print $serial, $title, $name, $mydate, $content; }
結果は「1新しい掲示板のテストREDHOT1999/11/07 03:291999/11/07 03:29」となり、最後の$contentがきちんと取得できていないようです。
何度も見直したのですが、なぜマッチしないのか分かりません。見落としている点があると思いますので、ご指摘いただければ幸いです。
ソースを見てみました。
目的の投稿内容は、途中に改行を含んでいますね。
.(ピリオド)は改行文字を含まないのではありませんか?
perl から離れて久しいので、チョット調べてみました。
/s を付けないと改行は含まれない様ですね。
おお!おっしゃるとおり/sをつけてみたら本文取れました!すごい。
「掲示板を新しくしたので<br>トです。」
という感じで改行周辺で抜け落ちてしまっていますが、もう少しやってみますね。うーむやっぱり聞いてよかった。ありがとうございます。
追記:文字が抜け落ちる現象は、どうもUTF8のフラグが怪しいかなぁという印象なので、上記のencode部分を全部euc-jpにしてみたら、無事取得する事ができました。お答えいただいたみなさん、ありがとうございました。はまってしまっていたので大助かりです。
おお!おっしゃるとおり/sをつけてみたら本文取れました!すごい。
という感じで改行周辺で抜け落ちてしまっていますが、もう少しやってみますね。うーむやっぱり聞いてよかった。ありがとうございます。
追記:文字が抜け落ちる現象は、どうもUTF8のフラグが怪しいかなぁという印象なので、上記のencode部分を全部euc-jpにしてみたら、無事取得する事ができました。お答えいただいたみなさん、ありがとうございました。はまってしまっていたので大助かりです。