ラベル Perl の投稿を表示しています。 すべての投稿を表示
ラベル Perl の投稿を表示しています。 すべての投稿を表示

☆Twitterの投稿時刻をPerlでローカル時刻にする

Twitterの投稿時刻は投稿してから24時間以内は"約○○前"のように現在との時間差で表示されます。
ログインしていない場合、1日以上経過しているつぶやき時刻は日本との時差がなぜか17時間(PSTの場合。PDTの時期は16時間)もあります。
"約○○前"の表示はあまり好きではありませんし、自分のつぶやき時刻が17時間もずれて表示されているとびっくりしてしまいます。
そこでPerlで時刻の表示を一律 %Y/%m/%d %H:%M:%S の形式に変更しようと思いました。
使用する環境はUbuntu 9.10 Karmic koalaです。

目をつけたのは、下記の data="{time: の後ろの文字列です。

<span class="published timestamp" data="{time:'Tue Mar 16 12:44:41 +0000 2010'}">約1時間前</span>
この形式を扱えるモジュールを探します。
"perl Datetime"で検索すると、結構たくさんのモジュールが引っかかるのですが、どれを使えばよいのかわかりません。
とりあえず、DateTime::Format::W3CDTF と DateTime::Format::Mail を試したのですが、うまくいきません。
検索条件をいろいろ変えてみて、Twitter のStreaming APIにおける日時表現の解析についてというサイトに行き着きました。
DateTime::Format::DateParse で可能と書いてありました。私が扱おうとしているのはStreaming APIではないのですが、時刻の形式は一緒です。

DateTime::Format::DateParseは "libdatetime-format-dateparse-perl" パッケージに含まれています。
依存関係は

Depends: perl (>= 5.6.0-16), libdatetime-timezone-perl (>= 0.27), libdatetime-perl (>= 0.29), libtimedate-perl (>= 1.16)
となっています。

Manpageをみると

SYNOPSIS
           use DateTime::Format::DateParse;

           my $dt = DateTime::Format::DateParse->parse_datetime( $date );
           my $dt = DateTime::Format::DateParse->parse_datetime( $date, $zone );

となっているのですが、なぜか引数を2つとる形式はうまく動きませんでした。

DateTime::Format::DateParseを先日のtw_rev.plに組み込むと下記のようになります。

tw_rev2.pl
#!/usr/bin/perl
use DateTime;
use DateTime::Format::DateParse;
my $tzhere = DateTime::TimeZone->new( name => 'local' );

sub repTime
{
  my $gmt = shift;
  $dt = DateTime::Format::DateParse->parse_datetime($gmt);
  $dt->set_time_zone($tzhere);
  return $dt->strftime('%Y/%m/%d %H:%M:%S');
}


# search start html header
while(<>){
  print;
  last if m#<head>#;
}

# For relative link. ex list in right side.
print qq(<base href="http://twitter.com/" />\n);

# search start of tweets
while(<>){
  print;
  last if m#<ol id='timeline' class='statuses'>#;
}

# stack each tweets
my(@TWEET);
OUTER:
while(1){
  my(@tweet);
  while(<>){
    last OUTER if m#</ol>#;
    s#(<span class="published timestamp" data="{time:'([^']+)'}">)[^<]+</span>#$1 . repTime($2) . "</span>"#e;
    push @tweet, $_;
    last if m#</li>#;
  }
  unshift @TWEET, \@tweet;
}

# output each tweets
my($tweet);
for $tweet (@TWEET){
  my($line);
  for $line (@$tweet){
    print $line;
  }
}
print qq(</ol>\n);

# output remaining
while(<>){
  print;
}

☆TwitterのHTMLをPerlでDOM的に扱おうとして絶望

これは失敗の記録です。PerlでTwitterのHTMLをDOM的に扱おうとして断念しました。

●やりたかったこと

Twitterのタイムラインを通常の逆順(投稿時刻の昇順)に表示する。
先日作ったperlスクリプトは改行に依存している部分があってTwitterのHTMLが少し変わっただけで影響を受けてしまいます。
この欠点をXHTMLを解析できるモジュールを使って克服したい。

●そもそもなぜTwitterのAPIではなくHTMLを扱おうとしたか

GUIアプリを作るのは大変手間がかかる(LinuxではそもそもGUIアプリを作るスキルがない)ので、ブラウザで表示したい。
APIでとってきたXMLまたはJSONをHTMLに組み立てなおすのは結構手間がかかります。

●何がうまくいかなかったか

Twitterの返すHTMLは & がエンティティ化されておらず、XML::DOM::Parser、XML::LibXMLともにエラーが発生しました。
& をエンティティ化しようとすれば、それがリンクの一部でないかなどの判定が必要で、実質的にHTMLを解析する必要があります。
これではモジュールを使って手間を省く意味がありません。
また蛇足ですが、XML::DOMを使う場合、XML::DOM::DocumentはgetElemetByIDメソッドが存在しないので、
つぶやきの&lt;li&gt;を持っている&lt;ol&gt;ノード"&lt;ol id='timeline' class='statuses'&gt;"を取得するために余計なロジックが必要になります。
XML::LibXMLは&に引っかからない場合、idの重複でエラーが出ます。これはbody要素とひとつのdiv要素に同じid="profile"が存在するためです。
(XML::DOMがエラーを吐かないのが不思議です。)

参考:XML::DOM::Parserのエラーメッセージ(&が原因)
not well-formed (invalid token) at line 577, column 181, byte 27722 at /usr/lib/perl5/XML/Parser.pm line 187

参考:XML::LibXMLのエラーメッセージ(&が原因)
test.html:681: parser error : EntityRef: expecting ';'
参考:XML::LibXMLのエラーメッセージ(id重複が原因)
test.html:933: validity error : ID profile already defined

●その他、雑記

■Perlのモジュールに対応するUbuntuのdebパッケージを探す方法について

apt-cache searchはあてにならない気がします。
http://packages.ubuntu.com/ の"Search the contents of packages"は検索文字列に"/"が入っているとまったくマッチしなくなるようなので使えない。
apt-fileを使うのがベストだと思います。導入と使い方は Ubuntu Weekly Recipe 第16回参照。
XML::DOM::Parserを探したい場合、apt-file XML/DOM/Parser とやればOKです。

■debパッケージのないPerlモジュールはどうする

自分では試していませんが、Ubuntu日本語フォーラムの記事を見てみたらdh-make-perlでdeb化するのがよさげです。

■debパッケージがなかったので試さなかったPerlモジュール
▼HTML::TagParser

http://www.kawa.net/works/perl/html/tagparser.html
これはパースできるだけの模様。ノードを付け加えたり、削ってからHTMLを吐くというのは出来なさそうです。

▼XML::Liberal

http://search.cpan.org/dist/XML-Liberal/lib/XML/Liberal.pm
XML::Liberal - Super liberal XML parser that parses broken XML とあるので多少XMLに反する部分があってもやってくれそうです。
試してないのでわかりません。

●結局

☆Twitterのタイムラインを逆順に表示するで作成した、tw_rev.plは、つぶやきが含まれているol要素の始まりと終わり、各li要素の区切さえうまく捕まえることが出来ればよいように出来ています。
であれば、多少Twitterの吐くHTMLが変わったとしてもそれを補正してやるフィルタをかませてからtw_rev.plに入力すればいいので、大抵の場合、比較的簡単に対応が出来そうです。
直近にあった</li>のあとに<li>や</ol>が来てしまう変更は、tw_rev.plを直そうとすると以外に面倒ですが、sedで事前に改行を入れてやるだけで済みました。