twitterを掘る

通常twitterでは3200くらいしか過去のツイートを遡れません。とはいえ全ツイートを保存したい。

そんなときは、"from:XXXX since:YYYY until:ZZZZ" でツイッターを検索すると好きな期間のツイートを取得できます。

例えばこんな感じ。

https://twitter.com/search?f=tweets&q=from%3AMilkShake_yuuna%20since%3A2013-12-01%20until%3A2013-12-31

これでツイッター開始から1ヶ月ずつ(ツイ廃の子なら1週間くらい?)検索していけば、全ツイートを保存することができます。

ツイッター開始日はAPIで取得できますので、検索するといくらでもその手のサービスが見つかるでしょう。

https://api.twitter.com/1.1/users/show.json?screen_name=MilkShake_yuuna

{
  "id": 2238776738,
  "id_str": "2238776738",
  "name": "にょろ@ミルクセーキ",
  "screen_name": "MilkShake_yuuna",
  "location": "10/22(土) 長崎",
  "profile_location": null,
  "description": "ミルキーナンバー2番、にょろこと河合ゆうなです。メロンソーダがすきです。10月は1と2と9と10と15と22(卒業)出演しますよろしくお願いします。わは。#にょろさん @MilkShake_NGS",
  "url": "https://t.co/SrXI42d5mY",
  "entities":  {},
  "protected": false,
  "followers_count": 3684,
  "friends_count": 210,
  "listed_count": 149,
  "created_at": "Tue Dec 10 07:45:32 +0000 2013",
  "favourites_count": 208284,
  "utc_offset": null,
  "time_zone": null,
  "geo_enabled": false,
  "verified": false,
  "statuses_count": 10450,
  "lang": "ja",
  "status":  {},
  "contributors_enabled": false,
  "is_translator": false,
  "is_translation_enabled": false,
  "profile_background_color": "C0DEED",
  "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
  "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
  "profile_background_tile": false,
  "profile_image_url": "http://pbs.twimg.com/profile_images/755076601574862849/1X_rswi__normal.jpg",
  "profile_image_url_https": "https://pbs.twimg.com/profile_images/755076601574862849/1X_rswi__normal.jpg",
  "profile_banner_url": "https://pbs.twimg.com/profile_banners/2238776738/1474279384",
  "profile_link_color": "0084B4",
  "profile_sidebar_border_color": "C0DEED",
  "profile_sidebar_fill_color": "DDEEF6",
  "profile_text_color": "333333",
  "profile_use_background_image": true,
  "has_extended_profile": true,
  "default_profile": true,
  "default_profile_image": false,
  "following": true,
  "follow_request_sent": false,
  "notifications": true,
  "translator_type": "none"
}

追記

それにしても、昔は「ニョロ姫」とか呼ばれてたりツイッターのリプは釣るタイプだったり、なるほど昔は結構キャラが違ったんだな・・・と思った。アイドルも色々だな。

Eventernoteのお気に入り声優/アーティストにイベント参加回数を表示させるブックマークレット

誰かが作ってるだろうけど。なんとなく仕事をする気がなかったので作ってみた。

javascript:(function(){var v=document.getElementsByClassName('gb_actors_list')[0].getElementsByTagName('li'); for(var i=0;i<v.length;i++){n=v[i].className.replace(/.*c(\d+).*/,'$1'); v[i].getElementsByTagName('a')[0].textContent+='('+n+')'}}())

ThunderbirdでドコモメールをGmailに自動転送する

方法を色々と調べてみたのだけど、結論としてはIMAPフォルダーに直接コピーする方法が一番よさそう。

IMAPからみるとGmailのラベルはフォルダーになっているようなので、Inboxと好きなラベル(Mobame)に2つコピーをしている。あとは念のためローカルにもコピー。
ドコモが自動転送をサポートすれば何の問題もないのだけど。あるいはモバメが好きなメールアドレスを使わせてくれれば。

補足

Thunderbirdから転送をすると、Fwd:(件名)みたいなsubjectになったり、差出人が自分の名前になったりしてイマイチ。

公式の拡張機能としてヘッダーをそのままリダイレクトするものもあるけど、どうにも癖があって期待通り動かなかったりする。たぶんSMTPサーバーの動きによるものだと思う。

dropbox-api-commandをCygwinで使うメモ

Perlで実装されたdropboxAPIを使ってUNIX likeなコマンドラインインターフェイス。でいいかな。これは便利。

インストールはReadme通りに

wget https://raw.github.com/miyagawa/cpanminus/master/cpanm
perl cpanm App::dropboxapi

でよい。

Cygwinで使うときはLANGをUTF-8にしておくと日本語ファイルが文字化けしなくて便利。

$ export LANG=ja_JP.UTF-8

あと、syncの挙動を変えたかったのでちょっと中身をいじった。

$ diff /usr/local/bin/dropbox-api /usr/local/bin/dropbox-api.org
34d33
< unless($command){help('help'); exit(1)};
579c578
<                 (($remote_size != $local_size) and ($remote_epoch > $local_epoch)) or
---
>                 ($remote_size != $local_size) or
704c703
<                 if ((($remote_size != $local_size) and ($remote_epoch < $local_epoch)) or
---
>                 if (($remote_size != $local_size) or
  • 引数無しで実行した時にはhelpを出して終了
  • syncの際にファイルを上書きする条件を変更(サイズが違う&更新時間が新しいときにする)

ファイルの変更を行う時の条件はもっときめ細かくオプションで指定できると便利かなー。まあ適宜調節していけばよいか。

追記 2016/11/11

最新版では、こんな感じになってスッキリしていた。ほんとは != じゃなくて大小も判定したいけど、まあ実用上不都合もなさそうだからこのままでいいか。

   1114 sub has_change ($$) {
   1115     my ($local_path, $content) = @_;
   1116
   1117     my $remote_epoch = $strp->parse_datetime($content->{client_modified})->epoch;
   1118     my $local_epoch = $local_path->stat->mtime;
   1119     my $remote_size = $content->{size};
   1120     my $local_size = $local_path->stat->size;
   1121
   1122     if ($debug) {
   1123         printf "remote: %10s %10s %s\n", $remote_epoch, $remote_size, $content->{path_display};
   1124         printf "local:  %10s %10s %s\n", $local_epoch, $local_size, decode('locale_fs', $local_path);
   1125     }
   1126
   1127     if (($remote_size != $local_size) || ($remote_epoch != $local_epoch)) {
   1128         return 1;
   1129     }
   1130
   1131     return;
   1132 }
   1133

Cygwinでcronを使うときのメモ その2

cygwinでcron使おうとすると毎回何かしらはまってる気がする。

Cygwinでcronを使うときのメモ - XXXannex

今回はこんな感じのエラーが出た。

$ cronevents | tail
..
2015/07/17 12:14:01 [SYSTEM] /usr/sbin/cron: PID 5676: (k) CMD (/usr/bin/echo aa)
2015/07/17 12:14:01 [SYSTEM] /usr/sbin/cron: PID 5676: (CRON) error (can't switch user context)

詳細はよく分からないけど、passwd -R でパスワードをレジストリに保存しておいて、"Do you want the cron daemon to run as yourself?" の質問は "no" で答えると良いみたい。

$ passwd -R
This functionality stores a password in the registry for usage by services
which need to change the user context and require network access.  Typical
applications are interactive remote logons using sshd, cron task, etc.
This password will always tried first when any privileged application is
about to switch the user context.

Note that storing even obfuscated passwords in the registry is not overly
secure.  Use this feature only if the machine is adequately locked down.
Don't use this feature if you don't need network access within a remote
session.

You can delete the stored password by specifying an empty password.

Enter your current password:
Re-enter your current password:
$ cron-config
The cron daemon can run as a service or as a job. The latter is not recommended.
Cron is already installed as a service under account LocalSystem.
Do you want to remove or reinstall it? (yes/no) yes
OK. The cron service was removed.

Do you want to install the cron daemon as a service? (yes/no) yes
Enter the value of CYGWIN for the daemon: [ ]

You must decide under what account the cron daemon will run.
If you are the only user on this machine, the daemon can run as yourself.
   This gives access to all network drives but only allows you as user.
To run multiple users, cron must change user context without knowing
  the passwords. There are three methods to do that, as explained in
  http://cygwin.com/cygwin-ug-net/ntsec.html#ntsec-nopasswd1
If all the cron users have executed "passwd -R" (see man passwd),
  which provides access to network drives, or if you are using the
  cyglsa package, then cron should run under the local system account.
Otherwise you need to have or to create a privileged account.
  This script will help you do so.
Do you want the cron daemon to run as yourself? (yes/no) no

Were the passwords of all cron users saved with "passwd -R", or
are you using the cyglsa package ? (yes/no) yes
The cron daemon will run as SYSTEM.

Running cron_diagnose ...
... no problem found.

Do you want to start the cron daemon as a service now? (yes/no) yes
OK. The cron daemon is now running.
(以下略)

怪文書

はてなダイアリーの下書きを整理してたら謎の怪文書が。消すには惜しいし、残しておくには怪文書すぎるし、と思ってここに貼っておく。2009-11-10 23:32:23 更新らしい。昔すぎて何のことやらですな。
いや、何の話をしてるかは分かるけど、やっぱり怪文書としかいいようのない不気味さを感じる。

逆算を重ねた計算通りでした。目的を達成するためには手段は選ばない

一番怖いのは「別に困ってない」「繋がりたきゃmixiでやれ」という意見。具体的に言えばXX

有形無形の無形とはそう言う意味。

1アクセスの多い場所で騒ぐ
2有名ブロガーが取り上げる
3話題になった頃を見てメール

いかにして釣るか…という話。
アクセスを稼げる記事。2chか増田だが…まず2chは却下、増田も微妙。となれば、自サイトでアクセスの見込める記事と言えば…

第一の関門は突破できた。では次の関門として、いかに釣るか。「不便」ではちと弱い、「アクセスが減る」も釣り餌としては取り上げにくかろう。では、それらによって、失われる本質的なものは何だ?と考えた挙げ句思い付いたのが「文化」。
これはすごいキャッチーじゃないか!ブログサービスの一機能が「文化」まで昇華し、それが今失われようとしてるんだよ。他のバグで「文化」まで失われることが果たしてあるだろうか?そう、これは単なるバグ報告ではない、我々の文化を取り戻す戦いなのだ!
このフレーズが思い浮かんだ瞬間、全てのシナリオが繋がりました。気分はすっかりブリタイ閣下、テンション高めで一気に書き上げました。
もちろん、キーワード調査に相応しく定量的な調査結果も付けて…。

その後の経過
身内(勝手に…)

http://d.hatena.ne.jp/kkobayashi_a/20090719/p2

自己実現理論 - Wikipedia
http://ja.wikipedia.org/wiki/%E8%87%AA%E5%B7%B1%E5%AE%9F%E7%8F%BE%E7%90%86%E8%AB%96

この辺を踏まえつつ、「帰属」欲求をくすぐる感じで。

-具体的な効果を
-定量的に
-最後は感情に訴える

危惧していたのが「売名」的なアレ。うまい隠れ蓑があってよかった。

ESDちゃんのRT & Fav

冬コミの原稿でRを全く触らなかった(グラフはRで書いたけど)のでリハビリも兼ねてESDちゃんのtwitterからメンバーごとのRTとFavの統計を求めることにした。

https://twitter.com/earthstar_dream

今現在350くらいしかpostしてないので簡単に全部のログを取ってこれる。

とりあえずこんな感じの入力ファイルを作る。

$ cut -c -70 esd_rtfav.txt | head -5
name    rt      fav     id      text
谷尻まりあ      3       5       555225418421112833      こんにちは。青の谷尻まりあです。「声
中島由貴        3       12      555129738151219200      アーススタードリーム中島由貴です?おは
曽我部英理      2       8       555025061426368512      こんばんは!オレンジの曽我部英理です
小出ひかる      2       12      555021299517374467      こんばんは?(*∩ω∩*)黄色の小出ひか

データはgithubにアップロードしようかと思ったけどgoogleでいいわ。

https://docs.google.com/spreadsheets/d/1b6mMbliVOAwspOO5dtCwdrWyzwknD8nCN5WI_x9GlDk/export?format=tsv

で、ggplot2でグラフにする。

library(ggplot2)
library(reshape2)
library(plyr)

# データ読み込み
d <- read.delim("esd_rtfav.txt", colClasses=c("character", "numeric", "numeric", "character", "character"))

# その他(運営とか)のpostを除外
d2 <- d[d[,1] != "その他",]

# ggplot2用にデータを加工
d.m <- melt(d2[,1:3])

# favのmedian順でfactorをソート
d.m[,1] <- factor(d.m[,1], levels=names(sort(tapply(d2[,3], factor(d2[,1]), median), dec=T)), ordered=T)

# plot
p <- ggplot(data=d.m, aes(x=name, y=value)) + geom_boxplot(aes(colour=variable), outlier.shape=NA) + theme(axis.text.x=element_text(angle=90)) + scale_y_continuous(limits=quantile(d.m$value, c(0, 0.9)))
ggsave(file="esd.png", plot=p)

# 参考1:最大RT/fav
ldply(by(d2, factor(d2[,1]), function(x){x[which.max(x$rt),]}))
ldply(by(d2, factor(d2[,1]), function(x){x[which.max(x$fav),]}))

# 参考2:post数
data.frame(count=sort(tapply(d2[,1], factor(d2[,1]), length), dec=T))

こんな短いコードで書けて便利だね!でもコードの書き方調べるのに5時間くらい掛かってるからExcelでやったほうが早いね!

fav数の中央値でx軸ソートしたけど、これを見るとゆっきーさおりん辺りが人気なのかなあ。さおりんは自撮りカワイイからね!

いづみんこと新井田いづみちゃん、favはそれほどでも無いけどRTが多い。スパルタンMXに出ていたようなので、その辺の身内関連なのかも。調べてないけど。

とは言え、こうして見ると多少の上下はあるものの、それほどRTやfavに格差が出てるわけでは無さそう。今のところは中島由貴ちゃんが比較的推されてる感あるけど、まだまだ横並びな感じですね。

個別のtwitterアカウントができればもう少し格差が広がっていくことになるのかなあ。社長の方針としては、まだ個別アカウントは作らないみたいなので、もうしばらく様子を見守ろう。

post数の統計

名前 post数
中島由貴 51
小出ひかる 38
斎藤愛永 33
谷尻まりあ 33
高尾奏音 32
愛原ありさ 30
村北沙織 27
新井田いづみ 26
曽我部英理 21

なるほど。

最大RT/favを達成したpostも調べたけど、「はじめまして」postがほとんどであんまり面白くない。

RT

愛原ありさ (30 RT)

高尾奏音 (25 RT) 斎藤愛永 (18 RT) 小出ひかる (35 RT)新井田いづみ (19 RT) 曽我部英理 (17 RT) 村北沙織 (14 RT) 谷尻まりあ (20 RT) 中島由貴 (32 RT)

fav

愛原ありさ (42 fav)

高尾奏音 (24 fav) 斎藤愛永 (24 fav) 小出ひかる (35 fav)新井田いづみ (23 fav) 曽我部英理 (33 fav) 村北沙織 (26 fav) 谷尻まりあ (24 fav) 中島由貴 (38 fav)

データ取得用コード

本文に名前か担当色が入っているかどうかで名前の判別をしている。

一番先にキーワードが出てきた人が投稿者だろうと仮定しているけど、ときどき誤検知をするので最終的には手動でチェックしないといかん。

use strict;
use warnings;
use Net::Twitter;
use Scalar::Util qw/blessed/;
use Getopt::Std;
use Encode;
use List::Util qw/reduce/;
use utf8;
binmode STDIN,  ":utf8";
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8";

my $opt = {};
getopts("n:p:c:", $opt);

my $name_table = {
  中島由貴     => '中島由貴|ゆっきー|白',
  高尾奏音     => '高尾奏音|のんのん|ピンク',
  小出ひかる   => '小出ひかる|ひかるん|黄色',
  斎藤愛永     => '斎藤愛永|ぴぃちゃん|水色',
  愛原ありさ   => '愛原ありさ|あーりぃ|赤',
  曽我部英理   => '曽我部英理|えりりん|オレンジ',
  谷尻まりあ   => '谷尻まりあ|まりあんぬ|青',
  新井田いづみ => '新井田いづみ|いづみん|黄緑',
  村北沙織     => '村北沙織|紫',
};

my $screen_name = $opt->{n} || 'earthstar_dream';
my $page        = $opt->{p} || 1;
my $count       = $opt->{c} || 20;

my $nt = Net::Twitter->new(
  traits   => [qw/API::RESTv1_1/],
  consumer_key        => $consumer_key       ,
  consumer_secret     => $consumer_secret    ,
  access_token        => $access_token       ,
  access_token_secret => $access_token_secret,
);

my $statuses = [];

while(1){
  print STDERR "getting page $page..\n";
  
  eval{
    $statuses = $nt->user_timeline({screen_name=>$screen_name, page=>$page, count=>$count});
    ### $statuses
  };
  if(my $err = $@){
    ### $err
    if(blessed $err && $err->isa('Net::Twitter::Error')){
      warn $err->error;
    }
    else {
      # something bad happened!
      die $err;
    }
  }
  last unless @$statuses;
  print_status($statuses);
  $page++;
  sleep 1;
}

sub print_status{
  my $st = shift;
  foreach my $e (@$st){
    # determine a name : check the first matched position, and choose the leftmost person
    my $text = $e->{text}; $text =~ tr/\x0A\x0D//d;
    my %l = map { $text =~ /$name_table->{$_}/; (@- ? ($_ => $-[0]) : ()) } keys %$name_table;
    my $n = (%l ? (reduce { $l{$a} < $l{$b} ? $a : $b } keys %l) : 'その他');
    printf("%s\t%d\t%d\t%s\t%s\n", $n, $e->{retweet_count}, $e->{favorite_count}, $e->{id}, $text);
  }
}