Rubyによるアメブロのペタ貼り自動化スクリプト(7) ペタ貼り対象の自動抽出

次に、以下の不満点を解消する。

  • 一日に貼れるペタ数は500。500貼ろうとすると対象となるどうやって選び出すかが問題。"peta_target.txt"で設定されているものだけでは、到底数が足りない。
  • ペタを貼って貰った相手にお返しをしないといけないのだが、今の仕組みだと、"peta_target.txt"に一々登録しないといけない。

まず、ペタを貼って貰った相手の抽出をする。

ペタを貼って貰った状況は、ペタを貼りに行くアドレスにアクセスすれば確認できる。通常、そのURLは、
http://peta.ameba.jp/p/addPeta.do?petaId=&targetAmebaId=*****&pageNo=n
*****の部分に自分のID、nの部分にペタ貼りのページNoがはいる。
ペタ貼りのページに示されるペタの数は15件、全部で500件のペタ情報を集める事を目安として、1〜33ページまでアクセスして、貼られたペタの情報を集める。


ペタを貼ってくれた相手の情報は、下記の正規表現

ameba_url = Regexp.new("http:\/\/ameblo.jp\/([A-Za-z0-9]*)\/")

使って相手のブログのurlを抽出し、()で囲われた部分でバックリファレンスの機能を使って、更にそのurlから相手のブログIDを抽出する。抽出した相手IDは、"peta_target.txt"から読み出したペタを貼りたいIDの配列に追加する。


実際のコードは

for i in 1..33
  res = Net::HTTP.get('peta.ameba.jp','/p/addPeta.do?petaId=&targetAmebaId=romrus&pageNo='+i.to_s)
  res.to_s.scan(ameba_url) do |a|
    list.push(a[0])
    #p a
  end
end


実際にIDを検出させると、ペタを貼ってくれたブログのIDとは別に

  • 自分のID
  • 人気ブログへのリンク
  • プレミアムブログへのリンク

が検出される。更に

  • 元々のlistに含まれている"peta_target.txt"の中にあったID

も重複して検出されるので、listに対して、以下の処理を行って、配列list1を得る。

list.delete("*****")#自分のブログを外す
list.delete("famousblog")#人気ブログへのリンクを外す
list.delete("content")#プレミアムブログへのリンクを外す
list1 = list.uniq


最後の行を

list.uniq!

としなかったのは、抽出したIDのリストに重複が無かった場合に、listがnilになる為。
まさかそんな偶然があるとは思わないけれど…

Rubyによるアメブロのペタ貼り自動化スクリプト(6) ログファイルの作成

昨日は、ペタを貼るリンクの検出の正規表現を変更し、最新の記事IDが入ったリンクだけを検出する様になった。
で、次は更新されていないブログにペタ貼りをしない様にスクリプトを変更する。

そのために、まず、ペタを貼った後にそのブログのIDと、ペタ貼りのリンクを、ログに保存する。
最新記事のIDの入ったリンクだけを検出する事が可能になったので、それをログに残しておいて、次にペタ貼りをする時に、ペタ貼りのリンクが同じかどうかを確認すれば、記事が更新されたかどうか判断できる。
ログファイルの形式にはcsvを、スクリプト内でのデータ形式は配列の配列を使う。
ログの配列の1番目には、ブログのIDを入れて、配列の2番目にはペタ貼りへのリンクを入れる。
Rubycsvライブラリでは、配列の配列をcsvファイルに簡単に書き込んだり、読み出したりできるので、その機能を以下の様にそのまま使わせて貰う。

#ログデータのlogへの読み込み
log = Array.new

log = CSV.readlines('logdata.csv')

#ログデータの書き込み
CSV.open("logdata.csv", 'w') do |writer|
  log.each do |line|
    writer << line
  end
end


ログの配列をlogとして、ブログのIDを基にログを検索し、その配列の二番目のデータを、ネットから読み込んだペタ貼りのページへのリンクと比較して、動作を場合分けしてる。

#各ペタページにアクセスしてペタ貼りをする
http = Net::HTTP.new('peta.ameba.jp',80)
http.start
header = Hash::new
header['Connection'] = 'Keep-Alive'
header['Cookie'] = cookie_str(cookie)

File.open("peta_target.txt").each do |file|
  file.each_line do |line|
  p line.chomp!
    #Get peta_entry url
    response = Net::HTTP.get('ameblo.jp','/'+line+'/')
    if peta_blog_e_get = peta_entry.match(response)
      peta_e_path = peta_blog_e_get.to_s
      
      # p peta_e_path
      if (log.assoc(line) == nil) or (log.assoc(line)[1] != peta_e_path) then
      #ログの配列の中に該当するブログIDがない場合、またはペタへのリンクが異なっている場合だけ次の処理へ  
        response = http.get(peta_e_path,header)
        
        # p response
        
        if petaIDget =peta_url.match(response.body)
          peta_path =  petaIDget.to_s
          
          p peta_path
          
          response = http.get(peta_path,header)
          
          p response
          #ペタを貼る事に成功した場合のみ、ログの配列を更新する。
          if log.assoc(line) == nil then #ログの中に該当するブログIDがない場合
            log << [line, peta_e_path]#ログにブログIDとペタへのリンクの配列を追加する。
          else #ペタへのリンクがログと異なっていた場合
            log.assoc(line)[1] = peta_e_path #ログの配列のペタへのリンクを新しい物に書き替える
          end
        end
      end
    end
  end
end

http.finish

Rubyによるアメブロのペタ貼り自動化スクリプト(5) ペタ貼りのリンク検出正規表現の変更

アメブロへの自動ペタ貼りソフト、作成して、10日ばかり運用したのだが、何点か気になる所が出てきた。
気になる点は以下の通り

  • ペタを貼るリンクの検索で記事IDの入ってないペタ貼りリンクも検出してしまう。
  • 毎日ソフトを走らせると、特に更新されていない記事にもペタを貼る。更新されてない記事にペタを貼るのはどうも自動スクリプトっぽくていけない。
  • 一日に貼れるペタ数は500。500貼ろうとすると対象となるどうやって選び出すかが問題。"peta_target.txt"で設定されているものだけでは、到底数が足りない。
  • ペタを貼って貰った相手にお返しをしないといけないのだが、今の仕組みだと、"peta_target.txt"に一々登録しないといけない。

順にスクリプトを直していく。

まず、ペタ貼りのリンクを検出するための正規表現を変更する。
元々の正規表現

peta_entry = Regexp.new
("(?-mix:\\/p\\/addPeta.do(?:\\?(?:[;\\/?:@&=+$,A-Za-z0-9\\-_.!~*'()]|%[0-9a-fA-F][0-9a-fA-F])*))")

で設定されている。
スクリプトでは、HTMLを読み込んで、最初にこの正規表現にマッチしたリンクを使いその後の処理を続けるのだが、この正規表現ではブログ記事のIDが入ってないペタ用のリンクが先に出てきた場合にもマッチしてしまい、それで処理を続けてしまう。
具体的に問題になるのは、
>||/p/addPeta.do?targetAmebaId=ブログのID&service=blog|

Rubyによるアメブロのペタ貼り自動化スクリプト(4)

昨日までのスクリプトで、ペタ貼りに必要なcookieは全部入手できたので、いよいよ目的のブログにペタを貼るスクリプトを書く。
まず、以下の二つの正規表現を準備する。

  • ペタを貼るためのペタ貼り用のページのurlを見つけるための正規表現
  • 実際にペタを貼るためurlを見つけるための正規表現
peta_url = Regexp.new("(?-mix:\\/p\\/addPetaComplete.do(?:\\?(?:[;\\/?:@&=+$,A-Za-z0-9\\-_.!~*'()]|%[0-9a-fA-F][0-9a-fA-F])*))")
peta_entry = Regexp.new("(?-mix:\\/p\\/addPeta.do(?:\\?(?:[;\\/?:@&=+$,A-Za-z0-9\\-_.!~*'()]|%[0-9a-fA-F][0-9a-fA-F])*))")

次に、ペタを貼るブログのトップページにアクセスして、ぺタ貼り用のページのurlを取得する。
ペタを貼るブログのidは、予め'peta_target.txt'というファイルに一行ずつ改行して格納しておく。
アメブロのトップページのurlは'http:://ameblo.jp/ユーザid/'という形式なので、'peta_target.txt'から読み込んだidに対して一行ずつ処理を進める。
response = Net::HTTP.get('ameblo.jp','/'+line+'/')
として、各ブログのトップページからの応答を得る。
この応答の中から、if peta_blog_e_get = peta_entry.match(response) で、ペタを貼り用のページのurlを取得する。if文を使っているのは、ブログによってはペタを拒否している場合もあるので、その時に処理をスキップして次のブログへ処理を進める為。

#各ペタページにアクセスしてペタ貼りをする
http = Net::HTTP.new('peta.ameba.jp',80)
http.start

File.open("peta_target.txt").each do |file|
  file.each_line do |line|
  p line.chomp!
    #Get peta_entry url
    response = Net::HTTP.get('ameblo.jp','/'+line+'/')
    if peta_blog_e_get = peta_entry.match(response)#ペタ貼り用のページのurlを見つける
      peta_e_path = peta_blog_e_get.to_s
      p peta_e_path

実は、ペタ貼り用のページのurlは'http://peta.ameba.jp/p/addPeta.do?targetAmebaId=ユーザid'で良い。しかし、アメブロではどこからペタを貼られたのかが判る様に、

  • service
  • key
  • sig

の三つの変数をurlの中に含める様、最近仕様変更が行われた。
'service'は、空白、'blog'、'blog_e'、'profile'の4種類の定義がある。これはそれぞれ、ペタ帳に'表示なし(その他)'、'ブログから'、'ブログ記事から'、'プロフィールから'と表示されるのに対応する。'key'と'sig'は'service=blog_e'の時だけ意味があって、ブログのどの記事のペタリンクからペタが貼られたか判る様になっている。
自動でペタを貼る場合、横着をして'http://peta.ameba.jp/p/addPeta.do?targetAmebaId=ユーザid'だけで、ペタ貼りの処理をすると、どこからペタを貼ったのかが表示されず、相手に記事を読まずにペタを貼っているのがバレてしまう。実際にその通りなのだからバレても仕方ないのだが、自動ペタ貼りをするのは、相手にペタを返すのが礼儀なのだけれど時間がないから自動化したい。という所が出発点なので、ここは礼儀を失しない範囲で騙れるのならば騙っておきたい。年賀状を印刷で済ませる様なモノだと割り切る。
ブログ記事を読んでペタを貼る場合には、通常は'service=blog_e'で表示は『ブログ記事から』
プロフからペタを貼る場合には'service=profile'で表示は『プロフィールから』
おそらくちゃんとブログを読んでペタを貼ってますよと騙れる許容範囲はここまでだろう。
'service=blog'は『ペタを貼ってね』という画像を表示させている場合にのみ出てくる。そういう画像を貼っていない場合に表示で『ブログから』となった場合には、自動ペタ貼りソフトである事を白状した様なモノだ。
で、仕方ないから、ブログのトップページから実際のブログ記事のペタ貼り用のページurlを見つける仕様にしてある。

次に、ペタ貼り用のページを読み込んで、その中から実際にペタを貼る為のurlを先に定義しておいた正規表現'peta_url'で見つける。
で最後に、見つけたペタを貼る為のurlを送って、ペタ貼りを行う。

      #Get petaId & targetAmebaId
      req = Net::HTTP::Get.new(peta_e_path)#ペタ貼り用のページのリクエスト
      req['Connection'] = 'Keep-Alive'
      req['Cookie'] = cookie_str(cookie)
      response  = http.request(req)
    
      p response
    
      if petaIDget =peta_url.match(response.body)#実際にペタをはるurlを見つける
        peta_path =  petaIDget.to_s
        
        p peta_path
        
        req = Net::HTTP::Get.new(peta_path)
        req['Connection'] = 'Keep-Alive'
        req['Cookie'] = cookie_str(cookie)
        response = http.request(req)
      
        p response
      end
    end
  end
end

http.finish

ペタ貼り用のurlを見つける辺りのリクエストを、都度、newして'Connection'と'Cookie'を設定しているのは効率が悪い様な気がするので、それはこれからの宿題。

Rubyによるアメブロのペタ貼り自動化スクリプト(3)

昨日のスクリプトで、アメブロにログインして色々な事をするためのcookie'JSESSIONID'を入手したので、そのままアメブロのIDとパスワードをPOSTして、次に必要なcookie'I','N'を入手する。(送信するのはGETではなくてPOSTなので注意)
下記のスクリプトの中の***の部分にアメブロのID、****の部分にパスワードが入る。
パスワードは、先に読み込んだbodyの中に暗号化するためのJavaScriptが埋め込まれていて暗号化される。暗号化された文字列は結構長いもので4文字ではないので念の為。そのJavaScriptを解読して暗号化する機能までを下のスクリプトに埋め込むのは面倒くさいしバグを作り込みそう。なので、通常のブラウザでログインする時に、ブラウザがPOSTするデータを調べてそのまま使った。

#ID、パスワードを送信して、ログインする
path = '/login.do'
post_data = 'amebaId=***&password=****&Submit.x=0&Submit.y=0'
#***の部分にID、*****の部分にパスワードを暗号化した文字が入る

req = Net::HTTP::Post.new(path)
req['Connection'] = 'Keep-Alive'
req['Cookie'] = cookie_str(cookie)

print_key_and_val req

response = http.request(req,post_data)
p response.code
print_key_and_val response
set_cookie_from_res(response,cookie)#入手したクッキーからpetaに必要なクッキーをハッシュに設定する

p cookie #念の為にクッキーを出力してみる

http.finish

この状態でログインが上手く行くと、サーバーからは、cookie'I','N'と同時に、302リダイレクトの応答が返ってくる。ブラウザの場合には、各人のマイページを読み込むのだけれど、ペタを貼るだけの目的の場合、マイページの情報は必要ないので、リダイレクトには対応していない。
setcookieで送られて来たcookie'I','N'をハッシュ変数cookieに格納して、一旦セッションを切る。<続く>

Rubyによるアメブロのペタ貼り自動化スクリプト(2)

まず、ライブラリの読み込みと、変数・関数の準備する。

ライブラリは、Rubyに標準添付されているHTTPクライアントライブラリの'net/http'と、URIサポートライブラリ'uri'を読み込み。
(実は、今のところ'uri'は使っていないのだけれど…)

require 'uri'
require 'net/http'

Net::HTTP.version_1_2  #おまじない

ここでNet::HTTP.version_1_2は、Rubyのnet/httpライブラリのバージョンを指定するためのおまじない。
このライブラリは前バーションとの差異が大きいので、どちらも使える様に移行期間中らしい。

次にcookieを格納するためのハッシュを準備。
それからプログラムを簡単にするため、内部で繰り返し使う機能を関数にする。

cookie = Hash.new

# 要求・応答ヘッダ出力
def print_key_and_val(hash)
  puts "-- #{hash.class} --"
  hash.each do|k,v| puts "#{k} => \t#{v}" end
  puts ''
end

#Set-Cookie→クッキーの設定
def set_cookie_from_res(res,cook)
res.get_fields('Set-cookie').each{|str|
  k,v = str[0...str.index(';')].split('=')
  cook[k] = v
}
end

#ハッシュcookieから、
#HTTPのリクエストヘッダに挿入する文字列を生成する
def cookie_str(cook)
cook.map{|k,v|
      "#{k}=#{v}"
    }.join(';')
end

準備が出来たら、いよいよアメブロにアクセスする。
まず最初に人間がブラウザでログインするページ'http://www.ameba.jp/'にアクセスして、cookie'JSESSIONID'を貰う。
ここで大事なのはセッションを切らない事なので、リクエストヘッダの'Connection'に'Keep-Alive'をセットする。
同時にNet::HTTP.startするのだけれど、ブロックを使わない様にしている。
(これは、ブロックを使えばセッションを確実に終わらせられるという、Rubyお奨めの書き方からちょっと外れた書き方)
リクエストを投げて、返ってきたヘッダの中の'set-cookie'の項目をハッシュ変数cookieに格納する。

#
http = Net::HTTP.new("www.ameba.jp",80)

# http.set_debug_output $stderr

#まずwww.ameba.jp/に接続してセッションIDを入手する
path = '/'
req=Net::HTTP::Get.new(path)
req['Connection'] = 'Keep-Alive'
req['Cookie'] = cookie_str(cookie)

http.start

print_key_and_val req
response = http.request(req)
p response.code
print_key_and_val response

#入手したクッキーからセッションID等をハッシュに設定する
set_cookie_from_res(response,cookie)

この状態では、まだセッションは繋がっている事に注意!<続く>

Rubyによるアメブロのペタ貼り自動化スクリプト(1)

国内最大のユーザを抱えるブログサイトがアメブロ
僕もアメブロでお気楽ブログを書いている。
アメブロには独特の仕組みがある。
ペタとかアメーバピグとかである。

特に『ペタ』という奴がやっかいである。
どうもランキングに関係しているみたいなので、アメブロでブログの記事を読んだ後は、ペタ貼りをするのが礼儀らしい。
そんなモノは放っておけば良いのだけれど、知り合いから自動で『ペタ貼り』を出来るプログラムを作って欲しいとリクエストがあった。

そんな仕組みは放っておけよ。と言いたい所だが、彼の知り合いにとってはランキングとかペタは非常に大事らしい。

そういうわけでペタの仕組みを調べてみた。
ペタ貼りをするまでのHTTPクライアント(ブラウザ)とサーバーのやりとりはざっとこんな感じだ。

  1. まずログインのページをGETして、cookie('JSESSIONID')を取得する。
  2. ユーザIDとパスワードをPOSTして、cookie('N','I')を取得する。
  3. ペタを貼りたいブログをGETしての中からペタ貼り用ページのurlを取得する。(実はこのurlは、わざわざGETしなくてもブログのIDから作成する事も可能なのだが、後述する要因から一手間をかけて読み込む事にする。)
  4. 上で取得したペタ貼り用ページurlからデータをGETして、実際にペタを貼るためのurlを取得する。
  5. ペタを貼るためのurlをGETする。

ざっと、こんな流れだ。
最初の2項目で入手するcookieは、一度ログインして取得すれば、ログオフするまでそのまま使えるので、複数のブログに『ペタ貼り』する場合には、後の3項目だけを繰り返せば良い。

プログラム(スクリプト)はRuby1.9.1で書いた。
こんな感じ

require 'uri'
require 'net/http'

Net::HTTP.version_1_2  #おまじない

cookie = Hash.new

# 要求・応答ヘッダ出力
def print_key_and_val(hash)
  puts "-- #{hash.class} --"
  hash.each do|k,v| puts "#{k} => \t#{v}" end
  puts ''
end

#Set-Cookie→クッキーの設定
def set_cookie_from_res(res,cook)
res.get_fields('Set-cookie').each{|str|
  k,v = str[0...str.index(';')].split('=')
  cook[k] = v
}
end

def cookie_str(cook)
cook.map{|k,v|
      "#{k}=#{v}"
    }.join(';')
end

http = Net::HTTP.new("www.ameba.jp",80)

# http.set_debug_output $stderr

#まずwww.ameba.jp/に接続してセッションIDを入手する
path = '/'
req=Net::HTTP::Get.new(path)
req['Connection'] = 'Keep-Alive'
req['Cookie'] = cookie_str(cookie)

http.start

print_key_and_val req
response = http.request(req)
p response.code
print_key_and_val response

set_cookie_from_res(response,cookie)#入手したクッキーからセッションID等をハッシュに設定する


#ID、パスワードを送信して、ログインする
path = '/login.do'
post_data = 'amebaId=******&password=********************************&Submit.x=0&Submit.y=0'

req = Net::HTTP::Post.new(path)
req['Connection'] = 'Keep-Alive'
req['Cookie'] = cookie_str(cookie)

print_key_and_val req

response = http.request(req,post_data)
p response.code
print_key_and_val response
set_cookie_from_res(response,cookie)#入手したクッキーからpetaに必要なクッキーをハッシュに設定する

p cookie

http.finish

peta_url = Regexp.new("(?-mix:\\/p\\/addPetaComplete.do(?:\\?(?:[;\\/?:@&=+$,A-Za-z0-9\\-_.!~*'()]|%[0-9a-fA-F][0-9a-fA-F])*))")
peta_entry = Regexp.new("(?-mix:\\/p\\/addPeta.do(?:\\?(?:[;\\/?:@&=+$,A-Za-z0-9\\-_.!~*'()]|%[0-9a-fA-F][0-9a-fA-F])*))")



#各ペタページにアクセスしてペタ貼りをする
http = Net::HTTP.new('peta.ameba.jp',80)
http.start

File.open("peta_target.txt").each do |file|
  file.each_line do |line|
  p line.chomp!
    #Get peta_entry url
    response = Net::HTTP.get('ameblo.jp','/'+line+'/')
    if peta_blog_e_get = peta_entry.match(response)
      peta_e_path = peta_blog_e_get.to_s
      p peta_e_path
      #Get petaId & targetAmebaId
      req = Net::HTTP::Get.new(peta_e_path)
      req['Connection'] = 'Keep-Alive'
      req['Cookie'] = cookie_str(cookie)
      response  = http.request(req)
    
      p response
    
      if petaIDget =peta_url.match(response.body)
        peta_path =  petaIDget.to_s
        
        p peta_path
        
        req = Net::HTTP::Get.new(peta_path)
        req['Connection'] = 'Keep-Alive'
        req['Cookie'] = cookie_str(cookie)
        response = http.request(req)
      
        p response
      end
    end
  end
end

http.finish

詳細内容については、次回以降説明