Hatena::ブログ(Diary)

c/fe

2011-10-25

今日もPerl、Net::FTPでサーバー内のファイルを全部リスト出力する

仕事で大量にあるサーバーの中身をクロールしてファイル数などを勘定する必要があったので。


正直Getopt::Longのちゃんとした使いかたがわかってない。無駄な使われ方もいい所。

@tomitaさんのCPAN本の書き方だとなぜかエラーになったなー…。


#!perl

# perl c.pl --h='great.website.com' --u='famous_username' --p='super_strong_pass' --b='/htdocs' > file.list


use Net::FTP;
use warnings;
use strict;
use feature 'say';
use Getopt::Long;
use Data::Dumper;

my $opt = {
	h=>'localhost',
	u=>'web',
	p=>'pass',
	d=>'/htdocs',
};
GetOptions($opt, qw/h=s u=s p=s b=s/) or exit 1 ;

my $HOSTNAME = $opt->{h};
my $USERNAME = $opt->{u};
my $PASSWORD = $opt->{p};
my $BASE_DIR = $opt->{b};

my $ftp = Net::FTP->new($HOSTNAME, Debug => 0)
      or die "Cannot connect: $@";

$ftp->login($USERNAME, $PASSWORD) or warn("Login Error!\n");
listing($ftp, $BASE_DIR);
$ftp->quit;


sub listing{
	my ($ftp, $dirname) = @_;
	
	say $dirname ."/" ;
    $ftp->cwd($dirname);
    my @dir = $ftp->dir;

    my @file_names = grep /^\-/, @dir;
    my @file_names_full = @file_names[2..$#file_names];
    my @file_names_short = map { (split)[8]  } @file_names_full;

    foreach(@file_names_short){
		say " " x length($dirname), "+". $_;
    }

	my @dir_names = grep /^d/, @dir;
	my @dir_names_full = @dir_names[2..$#dir_names]; 
	my @dir_names_short = map { (split)[8]  } @dir_names_full;
	foreach(@dir_names_short){
		listing($ftp, $dirname."/".$_);
	}
    $ftp->cwd($dirname);
}

あれだよね、やっぱ @list = grep /^d/ @fromlist; とか say " " x $num ; っていいよね。phpでコレ書くのforで回すのかよって感じになるし。

まあ、こういうの使わないほうが、他人に渡す時(5年後の自分含む)読めるんだけどさ…

2011-10-17

mixiアプリで、IDの仕様が変わったから変換するって作業

すっごい放置してたんだけど、もう月末には締切だよ!って感じなので対応をしたね!


概要

どこにもズバリズバズバな説明が書いて無いからなかなか弱ったんだけど、


「opensocial_owner_idは前は数字やったけど、こんどは英数字13文字になるから*1変換してよ!一対一で差し替えるだけだから!」


というそれだけの話でしたね。いやそれだけの話なのに、HOWTOみたいなのが記載ないってどういうことや。(俺だけだったのかなあ…、一発で分からなかったの)


具体的な作業

とりあえずアプリ設定画面にいって、変換API叩くよーって申請すると新IDを引く為の用意ができるから

手元にあるであろうopensocial_owner_idをキーにして、すっごいガンガンAPI叩いて、対応する新IDを取得していく。

APIにアタック^H^H^Hクセスする速度は特に注意書きされてなかったので、ウェイトとかいれなかった!

そんで全部とれたら、既存のopensocial_owner_idと差し替えて、再度アプリ設定画面にいって、変換作業おわったよーってボタンを押す。

すると完了やで!


さらに具体的なツール(二度と使わないけど、メモ)

YAPC直後でPerl熱が高まっているので、Perlで書きましたね。

Prepared Statementで名前付きプレースホルダつかいたかってんけど、

普段使ってる ゆるふわPDOとはちょっと書き方ちがったので、面倒で?、?って感じですね。


ーー

DBのスキーマ的には

userという名前のテーブルがあって、

id <= 連番

owner_id <= opensocial_owner_idでとれるやつ

new_owner_id <= 新しく差し替えるopensocial_owner_idをALTERで追加しとく

という想定。

あ、new_owner_idは、後述しますけどひけない事が大量にあるので、Uniqueは後でかけたほうがいいでしょうね。

引けなかったデータを捨てるか、整合性の為にうめごろしするかは自由かなと。


ーー

#!/usr/bin/perl
use 5.0.8;
use strict;
use warnings;
use JSON::XS;
use OAuth::Lite;
use OAuth::Lite::Consumer;
use DBI;

#mixisetting
my %options = (
	site            => 'api.mixi-platform.com',
	consumer_key    => 'xxxxxxxxxxxxxxxxxxxxxx',
	consumer_secret => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
	requester_id    => '0000000000', #アプリオーナーのowner_id
);

my $consumer = OAuth::Lite::Consumer->new(
	consumer_key    => $options{consumer_key},
	consumer_secret => $options{consumer_secret}
);


my $db = DBI->connect('dbi:mysql:database=db_name', 'db_user', 'db_pass',{
	mysql_enable_utf8 => 1,
});


my $st = $db->prepare('SELECT * FROM user ORDER BY id');
$st->execute();
my $update_puid = $db->prepare('UPDATE user SET new_owner_id = ? WHERE id= ? ');

while(my $row = $st->fetchrow_hashref ){

	my $id = $row->{id};
	my $owner_id = $row->{owner_id};
	print "id-> $id, owner_id-> $owner_id, ";

	my $response = $consumer->request(
	    method  => 'GET',
	    url     => sprintf('http://%s/os/0.8/people/'.$owner_id.'/@self', $options{site}),
	    params  => {
			xoauth_requestor_id => $options{requester_id},
			fields=>'platformUserId',
	    });

	if ($response->is_success) {
		my $data = JSON::XS::decode_json($response->decoded_content);
		print "new_owner_id->".$data->{entry}->{platformUserId};
		$update_puid->execute($data->{entry}->{platformUserId}, $id);
	} else {
		warn $response->status_line;
		print "GET ERROR";
	}

	print "\n";
}
$db->disconnect;

この取得がおわったら、

・アプリを適当メンテ中表示にでもして(弱小アプリならしなくたってバレないだろうけど)

ALTERでnew_owner_idとowner_idいれかえて

管理画面で変換作業完了を押して

・アプリをメンテ中表示外す

(・古いowner_idは適当にのこしておくか、消す)

という感じですね。





作業自体は簡単だったんだけど

俺の設計で、速度とかの問題からowner_idをスーパークッキー代わりにしてるところがあったんだけど、それがこの仕様変更で死んだ、仕方ない…。


後、変換プログラムで引けないデータ結構大量にでてびっくりするけど、これはアプリを削除してるからってことらしい。

これの弊害として、通常アプリを再インストール(?)したとき、しれっと既存データもどせるけど、残念なことに今後は突き合わせできないから、この時点でアプリアンインストールしてる人はどうしてもロストということになるんだろうな。

*1:実際には、今後はIDをアプリ間で共有できないとか、色々仕様が

2011-09-07

延命centos5系

副題:いい加減CentOS6をそこらへんの業者がつかえるようになってほしい

yum remove php*

yum install php53*

(必要ならremove php*ではずれちゃった他の物を戻す)

yum install php-pear

pear upgrade --force Archive_Tar Console_Getopt PEAR XML_RPC

yum install pcre-devel

pecl install apc

どうでもいいけど、groongaのリポジトリをいれていると、mysql と MySQLがケンカして、php53にまで影響がでるのがちょっと困る。

(いや、groongaいれたら一回外せばいいだけなんだけど)


追記:

上のをやった後でVirtualminを入れるとコケる。

install.shのrhdepをいじるか、事が終わってからやった方が良い。

2011-06-14

phpのfile_get_contentsでHEAD Request

まあ、普通にできるんですけど、あんまりHeadでリクエストする人もいないのか、あんまり記事がなかったので、メモ

<?php

echo getAvaterImageUrl('fb', 'junichi.ishida');
echo '<br>';
echo getAvaterImageUrl('tw', 'uzulla');

function getAvaterImageUrl($mode, $id){
    $opts = array(
      'http'=>array(
        'method'=>"HEAD",
      )
    );

    $context = stream_context_create($opts);

    if($mode=='fb'){
        $file = file_get_contents('https://graph.facebook.com/'.$id.'/picture', false, $context);
    }else if($mode=='tw'){
        $file = file_get_contents('http://api.twitter.com/1/users/profile_image/'.$id, false, $context);
    }else{
        return null;
    }

    $location = array_merge(preg_grep('/^location:/i', $http_response_header));
    $url = preg_replace('/location:[ ]*/i', '', $location[0]);

    return $url;
}

見てわかる通り、TwitterとFacebookで、ユーザーのID、Screen_nameを元にアバターアイコンの実体URLを取得する方法ですね。


所で、FacebookはAPI経由でアイコンにアクセスさせて文句いわれないみたいですけど(特に文句をかいてないので)、Twitterの方はユーザーには出すなとかかいてあってこんな面倒なコードをかかないといけない。

最近のTwitterは微妙だからもっとがんばってほしいわー、ホントFBをみならってほしいわー。


http://dev.twitter.com/doc/get/users/profile_image/:screen_name

http://developers.facebook.com/docs/reference/api/