マックではSサイズを買え!
ゴールデンウィークまっただ中ということで、マクドナルドで食事をする人も多いと思いますが、そんな人に耳寄りな情報をご紹介。
ここ数日はとても暖かいのでついついマックシェイクでも頼んでみたくなりますよね?
そんなときにはMサイズ(220円)を1つ注文するのなら、Sサイズ(100円)を2つ注文した方がお得なのです。
通常はそのような価格設定はしないように思うのですが、この前重さを量ってみたら下のようになりました。
Sサイズ×2: 約392g(容器込みの2つ分の重さ) - 約20g(容器2つ分の重さ) = 372g
Mサイズ×1: 約315g(容器込みの1つ分の重さ) - 約20g(容器1つ分の重さ) = 295g
なんとSサイズ2つのほうが、Mサイズ1つより約80gも多いのです。
もちろん個体差はあると思うのですが、たぶんほとんどの場合Sサイズを2つ買った方が量は多いと思います。
それにSサイズを2つ買えば2つの味が楽しめるのもgood.
ということでマックシェイクを買うのならSサイズを2つ買うのが断然おすすめです。
Happy Golden Week!
比較優位の考えが参考になった
価格の仕組みが分かります
多くの人に読んでもらいたいです
身近なところから実感できる経済学
新しいfollowerやremoveした人のBioやFollowing/Followersを表示する
以前新しいfollowerやremoveした人を表示するRubyスクリプトというものを書いた。
しかし最近よくわからない外人のfollowerが増えてきていることもあり、名前だけ表示するだけではなくBioやFollowing, Followers数などがわかれば、いちいちその人のページを確認する必要もないよね、ということでBioとFollwing, Followers数も表示するRubyスクリプトを書いた。
follotter.rb
require 'rubygems' require 'mechanize' require 'kconv' class Follotter def initialize(user, pass) @agent = WWW::Mechanize.new @agent.user_agent_alias = 'Mac Safari' @agent.max_history = 1 login_form = @agent.get('http://twitter.com/').forms.action('https://twitter.com/sessions').first login_form['username_or_email'] = user login_form['password'] = pass @agent.submit(login_form) end def get_followers(command = 'check') case command when 'update' File.open("followers.txt", "w") do |io| (1..lastpage_index).each do |n| io.puts((@agent.get("http://twitter.com/followers?page=#{n}")/"a.url").map {|i| i.inner_text}) end end when 'check' followers = (1..lastpage_index).inject([]) {|memo, n| memo + (@agent.get("http://twitter.com/followers?page=#{n}")/"a.url").map {|fr| fr.inner_text} } older_followers = File.open("followers.txt", "r") {|io| io.readlines}.map {|l| l.chomp} (older_followers - followers).each do |i| info = get_followers_info(i) puts "-----------------------------------------" puts "#{i} has removed you." puts "Bio: #{info[0]}" #sjisでしか表示できない場合は info[0].tosjis のようにする puts "Following : Followers = #{info[1]} : #{info[2]}" end (followers - older_followers).each do |i| info = get_followers_info(i) puts "-----------------------------------------" puts "#{i} has started to follow you." puts "Bio: #{info[0]}" #sjisでしか表示できない場合は info[0].tosjis のようにする puts "Following : Followers = #{info[1]} : #{info[2]}" end else raise 'invalid arguments' end end private def lastpage_index num = (@agent.get('http://twitter.com/followers')/:h2).inner_text.match(/\d+/).to_s.to_i puts "Your #{num} Followers" (num%20 != 0) ? (num/20 + 1) : (num/20) end def get_followers_info(follower_name) user_page = @agent.get("http://twitter.com/#{follower_name}") bio = (user_page/"span.bio").inner_text following = (user_page/"ul.stats"/"li[1]/span").inner_text followers = (user_page/"ul.stats"/"li[2]/span").inner_text [bio, following, followers] end end if __FILE__ == $0 user = 'username' #自分のusernameに変更 pass = 'password' #自分のpasswordに変更 Follotter.new(user, pass).get_followers(*ARGV) end
基本的には前回自分が書いたものを、ujihisaが添削してくれた記事を大いに参考にしながら修正するようにした。
今まであまりArray#mapやArray#injectを使ってこなかったのだが、このように使えるんだと大変勉強になった。
あともう一つ気をつけたところは要素を指定する際にXPathをあまり使わないようにしたこと。
XPathは簡単に要素指定できるのですごく便利である一方、ページ構造に強く依存しているためページ構造の変化に弱いという欠点がある。そのあたりをタグ名やクラス名を指定するようにしてみた。
formも単純に何番目のフォームとするのではなくて、action名を元に決めることにした。(前回からログインフォームの位置が変わっていたりしたので)
Bioを表示する際に、Windowsのコマンドプロンプトを使っている人は、日本語を表示するために出力する文字の文字コードをsjisにしなくてはならない点に注意してください。
UTF-8で表示できる場合はプログラム冒頭の require 'kconv' は不要。
実行方法は前回と同じで
ruby follotter.rb update
で、比較対象となるfollowerをfollowers.txtに書き込み、
ruby follotter.rb
で、followers.txtとの差分を出力する。実行結果はたとえば下のようになる。
実行日時ごとにfollowersファイルを作成して、follower履歴みたいのをわかるようにした方がいいかなあ、と思いつつもそこまではやれてないです。
twitterの発言漏れをチェックするRubyスクリプト
ここ数日twitterでの発言漏れがひどいらしくて、自分の発言が特定のfollowerのtimelineに表示されないことがしばしば起こっている。
じゃあ一体誰のところで表示されていて、誰のところで表示されていないんだろうと思って、それを知るためのRubyスクリプトを書いた。あくまでも簡易的なものですが。要mechanize.
usernameとpasswordを変更するのを忘れずに。
4/22 3:15追記 重要
mechanizeの履歴を制限するのをすっかり忘れていました。
具体的には特に履歴が必要なければ @agent.max_history = 1 などのようにします。この設定をしないと(おそらく)すべてのページの履歴(Mechanize::Page)を記憶するので膨大なメモリを食います。
実際、僕の環境では最初のコードでは最大時で200MB以上のメモリを消費していました。上記のようにしただけで使用メモリは最大でも15MB以下に抑えられました。
ついついこの設定って忘れちゃんですよね。。。
4/22 3:55さらに追記
深夜に約200人をチェックした僕の場合で6分弱かかりました。かかる時間はチェックする人数に応じて大体これに比例すると考えられそうです。
require 'rubygems' require 'mechanize' class Chekker def initialize @agent = WWW::Mechanize.new @agent.user_agent_alias = 'Mac Safari' @agent.max_history = 1 #追記 重要! @username = 'username' #自分のusernameに変更 @password = 'password' #自分のパスワードに変更 login_form = @agent.get('http://twitter.com/').forms.first login_form['username_or_email'] = @username login_form['password'] = @password @agent.submit(login_form) end def check_post error_user = [] product_set.each do |fl| begin friends_with = [] friends_with = (@agent.get("http://twitter.com/#{fl}/with_friends")/"td.content"/:strong).map {|t| t.inner_text} error_user << fl if !friends_with.include?(@username) rescue WWW::Mechanize::ResponseCodeError => e case e.response_code when '404' next when '502' sleep 5 retry end rescue Timeout::Error puts "WRYYY" sleep 5 retry end end puts error_user if ARGV[0] == '-p' puts error_user.map {|u| '@' + u}.join(' ') end end private def lastpage_index(type) num = (@agent.get("http://twitter.com/#{type}")/:h2).inner_text.match(/\d+/).to_s.to_i (num%20) ? num/20 : num/20 + 1 end def get_username(type) (1..lastpage_index(type)).inject([]) {|memo, i| memo + (@agent.get("http://twitter.com/#{type}?page=#{i}")/"a.url").map {|a| a.inner_text}} end def product_set get_username('followers') & get_username('friends') end end if __FILE__ == $0 Chekker.new.check_post end
やっていることは単純で、自分のfollowerかつ自分のfollowing(friend)であるユーザを求めて、彼らのページを個別に訪れ、with_othersに自分の発言が含まれているかをチェックしているだけ。つまり、with_othersには20件しか表示されない仕様なので、timelineの流れの速いfollowing(ものすごくたくさんの人をfollowしている人)のwith_othersには発言直後でも自分の発言が含まれないことがあることに注意してください。
自分の発言の30秒から1分後ぐらいにまわすのがいいかと。ただし、twitterのサイト状況によっては結構時間がかかったり、開始まもなくエラーが出るので気長にどうぞ。(早くても3分はかかるかも)
バグっているところや、コードに関するつっこみがあればよろしくお願いします。
使い方は
ruby chekker.rb -p
などとすればOK. -pオプションをつけると下のように、自分の発言が表示されていなかったユーザ一覧の最後に@つき空白区切りでユーザを並べます。-pオプションをつけなければ1行に1ユーザが表示されるだけです。
自分の発言に@をつけるとみんなに見えやすくなるというのも聞くので、twitterの中身はどうなっているか興味津々です。
日記のデザインを変えてみた
春だよ、新学期だよ!ということで日記のデザインをid:gomi-box女史の作ったものに変更してみた。
こういうデザインセンスって本当に素敵だよなあ。
自分が解決したいという問題を設定するのって難しい。研究テーマとか決まるのだろうか。
春ですよ、春。
ニコニコ動画の新着投稿動画をチェックし続けるRubyスクリプトVer2
こちらはログイン不要!
おととい書いた日記でsanadanさんから
非公式ですけど、
http://www.nicovideo.jp/newarrival?rss=atom
ならログインしなくても新着情報がとれるので、そっちを使った方がすっきりしますし、ニコニコ動画のサーバー負荷も少ないんじゃないかと思いますけどどうでしょう?
というコメントを頂いたので、そっちで書き直してみました。ログインが不要なのでニコニコ動画アカウントを持ってなくても利用できます!
nico_newarrival2.rb
require 'rubygems' require 'hpricot' require 'open-uri' require 'date' require 'kconv' str = [] f = open(ARGV[0], "r") while l = f.gets do str << l.chomp.toutf8 end while true do targets = [] newarrivals = Hpricot(open('http://www.nicovideo.jp/newarrival?rss=atom')) (newarrivals/:entry).each do |d| title = (d/:title).inner_text link = (d/:link)[0].attributes['href'] str.each do |s| if Regexp.new(s) =~ title detail = Hpricot(open("http://www.nicovideo.jp/api/getthumbinfo/sm#{link.match(/\d+/).to_s}")) if (detail/:nicovideo_thumb_response)[0].attributes['status'] == "ok" status = "ok" first_retrieve = DateTime.strptime((detail/:first_retrieve).inner_text) length = (detail/:length).inner_text view_counter = (detail/:view_counter).inner_text comment_num = (detail/:comment_num).inner_text else status = "fail" first_retrieve = nil length = nil view_counter = nil comment_num = nil end targets << [link, status, title, first_retrieve, length, view_counter, comment_num] end end end if !targets.empty? targets.each do |t| if t[1] == "ok" puts("#{t[2]}\n #{t[3].strftime("%m/%d %H:%M:%S")} length: #{t[4]} view: #{t[5]} comment: #{t[6]}\n #{t[0]}") else puts("#{t[2]} deleted!") end puts "\n" end puts "**************************************************************************" end sleep 30 #下の追記に書いた通り頻繁にアクセスする必要はありませんでした end
3/5 2:00追記: 新着投稿動画一覧は10分ごとの更新のようなので、たまたま自分が見た30秒後に更新されることはありますが、頻繁にチェックする必要がまったくないようです。そのあたりは適当に調整してください。
koizukaさん、ご指摘ありがとうございました。
やはりログイン不要な分こちらの方がいいですね。link変数や30秒ごとにアクセスするなど、中身も微妙に変えてあります。Hpricotを入れてない人は gem install hpricot で。mechanizeを入れてあれば自動的に入っています。
実行方法などは前回と同様に、
ruby nico_newarrival2.rb hogelog.txt
でOKです。
クラスとか作らずにどぱーと書いてしまいました。相変わらずコードは汚いですね。。。
調べてみると、今回利用したような新着投稿動画情報をRSSフィードからとって来られるようなことは開発者の方のブログに書いてありました。知らなかったです。
新着動画のRSSを出してみた - こたにき
最近知りましたが、ニコニコ動画まとめwikiというのもあります。
http://nicowiki.com/
バグっているところや変なところがあれば教えてください。
ニコニコ動画の新着投稿動画をチェックし続けるRubyスクリプト
ニコニコ動画にアップされた最新の動画は新着投稿動画(http://www.nicovideo.jp/newarrival)でチェックできるわけだけど、自分で常にチェックするのは面倒だし、しかし目当ての動画がアップされたら諸事情で早急に見たい、ということがあるかと思います。
ということで、1分毎に目当ての新着投稿動画を動画タイトルをもとにチェックし、見つけたらコマンドライン上で知らせるRubyスクリプトを書いた。
3/5 2:00追記: ログイン不要なものをこちらに書きました。また新着投稿動画は10分毎に更新のようなので頻繁にアクセスする必要はまったくありませんでした。
mechanizeを入れていない場合は、gem install mechanize で。
nico_newarrival.rb
require 'rubygems' require 'mechanize' require 'open-uri' require 'kconv' require 'date' class NicoNewarrival def initialize @agent = WWW::Mechanize.new @agent.user_agent_alias = 'Mac Safari' login_page = @agent.get('http://www.nicovideo.jp/').forms[0] login_page['mail'] = 'hoge@foo.com' #ここに自分のメールアドレスを入力 login_page['password'] = 'password' #ここにパスワードを入力 @agent.submit(login_page) @agent end def check_newarrival str = [] #コマンドラインで指定したファイルを読み込む f = open(ARGV[0], "r") while l = f.gets do str << l.chomp.toutf8 end #新着投稿動画をチェックし続ける while true do targets = [] newarrival = @agent.get('http://www.nicovideo.jp/newarrival') (newarrival/"/html/body/div[3]/div/table[2]/tr").each do |r| (r/:td).each do |d| title = (d/"/div/div[2]/p/a").inner_text sm = (d/"/div/div[2]/p/a")[0].attributes['href'].match(/\d+/).to_s str.each do |s| if Regexp.new(s) =~ title doc = Hpricot(open("http://www.nicovideo.jp/api/getthumbinfo/sm#{sm}")) if (doc/:nicovideo_thumb_response)[0].attributes['status'] == "ok" status = "ok" first_retrieve = DateTime.strptime((doc/:first_retrieve).inner_text) length = (doc/:length).inner_text view_counter = (doc/:view_counter).inner_text comment_num = (doc/:comment_num).inner_text else status = "fail" first_retrieve = nil length = nil view_counter = nil comment_num = nil end targets << [sm, status, title, first_retrieve, length, view_counter, comment_num] end end end end #目的の動画が見つかったら表示 if !targets.empty? targets.each do |t| if t[1] == "ok" puts("#{t[2]}\n #{t[3].strftime("%m/%d %H:%M:%S")} length: #{t[4]} view: #{t[5]} comment: #{t[6]}\n http://www.nicovideo.jp/watch/sm#{t[0]}\n") else puts("#{t[2]} deleted!") end puts "\n" end puts "**************************************************************************" end sleep 60 end end end if __FILE__ == $0 NicoNewarrival.new.check_newarrival end
Windowsのコマンドプロンプトはsjisしか表示できないので、タイトルを表示する際に文字コードの変換が必要。具体的にはタイトルを表示している2カ所を下のようにしておくことに注意。
puts("#{t[2].tosjis} ......)
使い方はまず適当なテキストファイルに、目当ての動画のタイトルの一部分など以下のように書く。
nicotitles.txt
画
(ス|す).*(マ|ま).*(ブ|ぶ).*(ラ|ら)
改行で区切る。正規表現が使えるので上みたいに書けば多少の表記揺れなどは吸収できるのがポイント。
ruby nico_newarrival.rb nicotitles.txt
のように引数に目当てのタイトルを書いたテキストファイルを指定して実行する。
自分のidとパスワードに変更するのを忘れずに。
あとニコニコ動画の認証の性質上、同一アカウントで複数の場所からログインができないので、このスクリプトを走らせつつニコニコ動画を見る人はもう一つ別のアカウントがあったほうが便利です。
これを実行するとCtrl-cなどでプログラムを強制的に止めるまで、下のような画面が延々と続く。
まだ十分にテストとかしていないのでバグとかもあると思いますが、見つけしだい修正したいと思います。