Rubyによるアメブロのペタ貼り自動化スクリプト(14) アメンバー限定記事の処理(4)

さて、間が開いてしまったのだが、アメンバー限定記事の処理のコードを載せる。
今までのコードと違うのは
ペタへのリンクとアメンバー申請へのリンクのどちらが先に出てくるか調べて、アメンバー申請が先に出てきた場合にはアメンバー限定記事と判断。
アメンバー限定記事の場合には、更にそのリンクを調べる。
アメンバー承認されている場合には、リンク先にペタリンクが出てくる。
ペタリンクが出てこない場合には、アメンバー承認されていないのでプロフィールからペタを貼る。

アメンバー承認されていない場合にはペタを貼らないという選択肢もあるけれど、依頼者の要望はプロフィールから貼ると言う事だ。
さて、従来の様にログファイルに、ペタを貼った時のurlを表す変数peta_e_pathをそのまま更新確認用のログファイルに使うと、プロファイルからペタを貼った場合に更新確認が出来なくなるので、新しい変数peta_e_logを作って、ログ用記事のペタurlと実際に貼ったurlを別にしている。

#アメンバー記事へのエントリーを抽出する正規表現を設定
amember_entry = Regexp.new("secret.ameba.jp/[A-Za-z0-9\\-]+/amemberentry-[0-9]+.html")


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


  list1.each do |line|
    print "Checking ", line ," blog... "
    #Get peta_entry url
    response = Net::HTTP.get('ameblo.jp','/'+line+'/')
    if peta_blog_e_get = peta_entry.match(response)
      entry_point = peta_blog_e_get.begin(0)
   peta_e_log = peta_blog_e_get.to_s #ブログトップページで見つけたペタへのリンクを最新記事へのログデータとして変数に保存する。
      #アメンバー申請へのリンクを探す
   #アメンバー申請へのリンクが見つかった場合には、ペタへのリンクとどちらが先か確認する
		if (amember_e_get = amember_entry.match(response)) and (entry_point > amember_e_get.begin(0))
			amember_path = amember_e_get.to_s
			amember_path.sub!(/secret.ameba.jp/,'')
			http2 = Net::HTTP.new('secret.ameba.jp')
			http2.start
			amember_response = http2.get(amember_path, header)
			http2.finish
			
			if peta_entry.match(amember_response.body)
				peta_e_path = peta_blog_e_get.to_s
				posting_text = "from blog entry(amenber).."
			else
				peta_e_path = '/p/addPeta.do?targetAmebaId='+line+'&service=profile'
				posting_text = "from Profile(not amenber)..."
			end
		else
			peta_e_path = peta_blog_e_get.to_s
			posting_text = "from blog entry..."
		end
     if (log.assoc(line) == nil) or (log.assoc(line)[1] != peta_e_log) then
        print "Posting Peta " + posting_text
        response = http.get(peta_e_path,header)

        if petaIDget =peta_url.match(response.body)
          peta_path =  petaIDget.to_s
          response = http.get(peta_path,header)

          if log.assoc(line) == nil then
            log << [line, peta_e_log]
          else
            log.assoc(line)[1] = peta_e_log
          end
          print "OK"
        else
          print "NG"
        end

     else
        print "not updated."
     end
    end
    print "\n"
  end

http.finish

Rubyによるアメブロのペタ貼り自動化スクリプト(13) アメンバー限定記事の処理(3)

ちょっと本業の仕事が忙しくて、バタバタしている間に更新が凄く遅くなってしまった。

前回予告した様に、アメンバー限定記事と判定された記事の処理について書く。

アメンバー限定記事の場合、リンク

http://secret.ameba.jp/********/amemberentry-???????????.html

に対し、ヘッダにcookieを設定してGETを送ると、

当然、アメンバー認証されていない場合にはペタへのリンクはない。
このデータに対してペタへのリンクを探せば、アメンバー認定をされているのか?否か?を判別出来る。

コードは次回。

Rubyによるアメブロのペタ貼り自動化スクリプト(12) アメンバー限定記事の処理(2)

僕のスクリプトだと、アメンバー限定記事で本来読めない記事でも、『記事から』ペタを貼ってしまう。
依頼者はそこら辺が困ると言う。
アメンバー限定記事がある時は、認定されている場合には『記事から』、認定されていない場合には『プロフから』でペタを貼って欲しい。
そういうリクエストだ。

アメンバー限定記事の仕組みを調べる。まずブログのトップページで、アメンバー限定記事の場合には、記事が直接表示されない。この表示状態はアメンバーの認定されていても、されていなくても同じだ。
記事のタイトルは表示されるが、記事本文の代わりに『アメンバーエントリーボタン』と『アメンバーエントリーテキスト』、『アメンバーエントリーリクエスト』が表示される。
ソースは以下の形になる。

<div class="contents">
<div class="subContents">
<div class="amemberEntry">
<p class="amemberEntryBtn">
<a href="http://secret.ameba.jp/********/amemberentry-???????????.html" class="btn002">
<img src="http://stat.ameba.jp/p_skin/cmn/img/amember/btn002.gif" alt="アメンバー限定記事を読む"></a></p>
<p class="amemberEntryTxt"><a href="http://helps.ameba.jp/qguide/amember/post_186.html" target="_blank">
アメンバー限定記事を読むには</a></p>
<p class="amemberEntryReq"><a href="http://amember.ameba.jp/amemberRequest.do?oAid=********">
アメンバーになる</a></p>
</div>

********にはブログのID、???????????にはブログのエントリーNoが入る。

この記事に対するペタ貼りのリンクは、記事より下に表示される。
だから、この『アメンバーエントリーボタン』がペタ貼りのリンクより前に来るかどうかで記事がアメンバー限定かどうかを判断出来る。

  1. 正規表現で『アメンバーエントリー』のリンクを検出する
  2. 正規表現のパターンマッチでの結果で得られるMatchDataオブジェクトをつかって、『アメンバーエントリー』とペタ貼りのリンクとどちらがより先頭に近いかで、アメンバー限定記事かどうかを判断する。

まず、アメンバーエントリーのリンクを検出する正規表現は、下記の通り。

amember_entry = Regexp.new("http://secret.ameba.jp/[A-Za-z0-9\\-]+/amemberentry-[0-9]+.html")

次に、アメンバー限定記事かどうかを判定するコードは以下の通り。

    response = Net::HTTP.get('ameblo.jp','/'+line+'/')
    if peta_blog_e_get = peta_entry.match(response) #ペタリンクの検出
      entry_point = peta_blog_e_get.begin(0) #テキスト先頭からペタリンクまでの文字数

      if (amenber_e_get = amember_entry.match(response)) and (entry_point > amenber_e_get.begin(0))
    #アメンバーエントリーのリンクを検出
    #検出されたらその位置までの文字数を、ペタリンクまでの文字数と比較する。

        #ここにアメンバー限定記事への処理を書く

      else
    #通常記事の処理
        peta_e_path = peta_blog_e_get.to_s
   end
  end

Rubyでは、正規表現のパターンマッチで(正規表現でなくても使える?)テキストから文字列を検出した結果に、MatchDataオブジェクトが返される。
このオブジェクトのメソッドのbegin(0)で、マッチした配列の先頭オフセットの値(この場合にはhtmlソースの先頭からの文字数)を得る事ができる。

これで、とりあえず、アメンバー限定記事と通常記事の場合分けは出来る様になったはず。次回以降で、アメンバー限定記事の処理について書きます。

Rubyによるアメブロのペタ貼り自動化スクリプト(11) アメンバー限定記事の処理

しばらく記事の書き込みが滞ってしまった。
仕事で海外に出張していて、ブログどころでは無かったせいだ。

その間、依頼者は、実際にペタ貼りスクリプトを使い始めたわけだが、改善点のリクエストを貰った。

アメブロにはアメンバーという仕組みがある。
アメンバーと言うのは自分が特別に許可した相手。
通常は誰でも読めるブログなのだけれど、少しプライベートな事が書いてあったりする場合には、記事をアメンバー限定という設定にして、読む人間を限定する事ができる。

僕が作ったスクリプトは、そのアメンバー限定記事でも関係無くペタ貼りをしてしまう。
依頼者はそれがとても困ると言う。
ネット原始人の僕などは、オープンなネット上でアメンバー限定などとややこしい事をするくらいならブログなど書かなければ良いのにと思うのだけれど、ネット上でもそう言う微妙な関係性を築けるのが最近の風潮の様だ。
依頼人の言う事には逆らえない立場だし、仕方ないから対応する事にする。

まず、アメンバー限定記事の仕組みを調べてみる…

Rubyによるアメブロのペタ貼り自動化スクリプト(10) パスワードの暗号化(2)

Rubyには、MD5を扱うライブラリがきちんとある。
その中のDigest::MD5.hexdigest関数を使って、以下の様にログインのpostデータを作って、そのデータがブラウザのJavaScriptで作られた文字列と同じかどうか確認してみる。

require 'digest/md5'

#IDとパスワードの入力コードはオーソドックスにこれ
print "Please input your password :"
blog_pwd = gets.chop

pwd_data = Digest::MD5.hexdigest(blog_pwd)

p pwd_data

大丈夫な様だから、これを本体のスクリプトに組み込む。

Rubyによるアメブロのペタ貼り自動化スクリプト(9) パスワードの暗号化(1)

9/11の記述までで、一通り動作するスクリプトが出来た。

さて、スクリプトを頼んできた知り合いに、実際に使って貰おうとして、問題になったのが、ログインしてパスワードを送信する件である。
このスクリプトでは以下の様に、スクリプト中にIDとパスワードを暗号化したものを埋め込んでしまっている。

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

で、実際にブラウザがアメブロに送信する時の暗号化されたパスワードを調べて、スクリプトの中に実際に埋め込んで貰わないといけない。
しかし、その知り合いは、プログラムとかHTTPの通信とかの仕組みを全く知らないため、そういう事を自分で調べる事が出来ない。

パスワードを教えてくれたら僕が調べると言ったのだが、教えるのは嫌だと言う。
百歩譲って教えても良いが、その後に変更すると言う。
それでは意味がない。(余程、信用ないんだな、オレ…(-_-;))

困った。
仕方ないので、IDとパスワードを入力して貰って、それをスクリプト内で暗号化して送る事にした。

アメブロでログインする時のhtmlファイルから、Javascriptで書かれたパスワードを暗号化する部分を探し出す。

function pwdEncode(A){
	rawPasswd=document.getElementById(A).value;
	if(trim(rawPasswd).length>0&&trim(rawPasswd).length!=32&&!isHttps()){
		document.getElementById(A).value=hex_md5(rawPasswd)
	}
}

function trim(A){return A.replace(/^\s+|\s+$/g,"")}

で、ここで出てくるhex_md5という関数は、別ファイルに、やはりJavaScriptでこうある。

var hexcase=0;
var b64pad="";
var chrsz=8;

function hex_md5(A){return binl2hex(core_md5(str2binl(A),A.length*chrsz))}
function b64_md5(A){return binl2b64(core_md5(str2binl(A),A.length*chrsz))}
function str_md5(A){return binl2str(core_md5(str2binl(A),A.length*chrsz))}
function hex_hmac_md5(A,B){return binl2hex(core_hmac_md5(A,B))}
function b64_hmac_md5(A,B){return binl2b64(core_hmac_md5(A,B))}
function str_hmac_md5(A,B){return binl2str(core_hmac_md5(A,B))}
function md5_vm_test(){return hex_md5("abc")=="900150983cd24fb0d6963f7d28e17f72"}
function core_md5(K,F){
	K[F>>5]|=128<<((F)%32);
	K[(((F+64)>>>9)<<4)+14]=F;
	var J=1732584193;
	var I=-271733879;
	var H=-1732584194;
	var G=271733878;
	for(var C=0;C<K.length;C+=16){
		var E=J;
		var D=I;
		var B=H;
		var A=G;
		J=md5_ff(J,I,H,G,K[C+0],7,-680876936);
		G=md5_ff(G,J,I,H,K[C+1],12,-389564586);
		H=md5_ff(H,G,J,I,K[C+2],17,606105819);
		I=md5_ff(I,H,G,J,K[C+3],22,-1044525330);
		J=md5_ff(J,I,H,G,K[C+4],7,-176418897);
		G=md5_ff(G,J,I,H,K[C+5],12,1200080426);
		H=md5_ff(H,G,J,I,K[C+6],17,-1473231341);
		I=md5_ff(I,H,G,J,K[C+7],22,-45705983);
		J=md5_ff(J,I,H,G,K[C+8],7,1770035416);
		G=md5_ff(G,J,I,H,K[C+9],12,-1958414417);
		H=md5_ff(H,G,J,I,K[C+10],17,-42063);
		I=md5_ff(I,H,G,J,K[C+11],22,-1990404162);
		J=md5_ff(J,I,H,G,K[C+12],7,1804603682);
		G=md5_ff(G,J,I,H,K[C+13],12,-40341101);
		H=md5_ff(H,G,J,I,K[C+14],17,-1502002290);
		I=md5_ff(I,H,G,J,K[C+15],22,1236535329);
		J=md5_gg(J,I,H,G,K[C+1],5,-165796510);
		G=md5_gg(G,J,I,H,K[C+6],9,-1069501632);
		H=md5_gg(H,G,J,I,K[C+11],14,643717713);
		I=md5_gg(I,H,G,J,K[C+0],20,-373897302);
		J=md5_gg(J,I,H,G,K[C+5],5,-701558691);
		G=md5_gg(G,J,I,H,K[C+10],9,38016083);
		H=md5_gg(H,G,J,I,K[C+15],14,-660478335);
		I=md5_gg(I,H,G,J,K[C+4],20,-405537848);
		J=md5_gg(J,I,H,G,K[C+9],5,568446438);
		G=md5_gg(G,J,I,H,K[C+14],9,-1019803690);
		H=md5_gg(H,G,J,I,K[C+3],14,-187363961);
		I=md5_gg(I,H,G,J,K[C+8],20,1163531501);
		J=md5_gg(J,I,H,G,K[C+13],5,-1444681467);
		G=md5_gg(G,J,I,H,K[C+2],9,-51403784);
		H=md5_gg(H,G,J,I,K[C+7],14,1735328473);
		I=md5_gg(I,H,G,J,K[C+12],20,-1926607734);
		J=md5_hh(J,I,H,G,K[C+5],4,-378558);
		G=md5_hh(G,J,I,H,K[C+8],11,-2022574463);
		H=md5_hh(H,G,J,I,K[C+11],16,1839030562);
		I=md5_hh(I,H,G,J,K[C+14],23,-35309556);
		J=md5_hh(J,I,H,G,K[C+1],4,-1530992060);
		G=md5_hh(G,J,I,H,K[C+4],11,1272893353);
		H=md5_hh(H,G,J,I,K[C+7],16,-155497632);
		I=md5_hh(I,H,G,J,K[C+10],23,-1094730640);
		J=md5_hh(J,I,H,G,K[C+13],4,681279174);
		G=md5_hh(G,J,I,H,K[C+0],11,-358537222);
		H=md5_hh(H,G,J,I,K[C+3],16,-722521979);
		I=md5_hh(I,H,G,J,K[C+6],23,76029189);
		J=md5_hh(J,I,H,G,K[C+9],4,-640364487);
		G=md5_hh(G,J,I,H,K[C+12],11,-421815835);
		H=md5_hh(H,G,J,I,K[C+15],16,530742520);
		I=md5_hh(I,H,G,J,K[C+2],23,-995338651);
		J=md5_ii(J,I,H,G,K[C+0],6,-198630844);
		G=md5_ii(G,J,I,H,K[C+7],10,1126891415);
		H=md5_ii(H,G,J,I,K[C+14],15,-1416354905);
		I=md5_ii(I,H,G,J,K[C+5],21,-57434055);
		J=md5_ii(J,I,H,G,K[C+12],6,1700485571);
		G=md5_ii(G,J,I,H,K[C+3],10,-1894986606);
		H=md5_ii(H,G,J,I,K[C+10],15,-1051523);
		I=md5_ii(I,H,G,J,K[C+1],21,-2054922799);
		J=md5_ii(J,I,H,G,K[C+8],6,1873313359);
		G=md5_ii(G,J,I,H,K[C+15],10,-30611744);
		H=md5_ii(H,G,J,I,K[C+6],15,-1560198380);
		I=md5_ii(I,H,G,J,K[C+13],21,1309151649);
		J=md5_ii(J,I,H,G,K[C+4],6,-145523070);
		G=md5_ii(G,J,I,H,K[C+11],10,-1120210379);
		H=md5_ii(H,G,J,I,K[C+2],15,718787259);
		I=md5_ii(I,H,G,J,K[C+9],21,-343485551);
		J=safe_add(J,E);
		I=safe_add(I,D);
		H=safe_add(H,B);
		G=safe_add(G,A)
	}
	return Array(J,I,H,G)
}

function md5_cmn(F,C,B,A,E,D){return safe_add(bit_rol(safe_add(safe_add(C,F),safe_add(A,D)),E),B)}
function md5_ff(C,B,G,F,A,E,D){return md5_cmn((B&G)|((~B)&F),C,B,A,E,D)}
function md5_gg(C,B,G,F,A,E,D){return md5_cmn((B&F)|(G&(~F)),C,B,A,E,D)}
function md5_hh(C,B,G,F,A,E,D){return md5_cmn(B^G^F,C,B,A,E,D)}
function md5_ii(C,B,G,F,A,E,D){return md5_cmn(G^(B|(~F)),C,B,A,E,D)}

function core_hmac_md5(C,F){
	var E=str2binl(C);
	if(E.length>16){E=core_md5(E,C.length*chrsz)}
	var A=Array(16),D=Array(16);
	for(var B=0;B<16;B++){
		A[B]=E[B]^909522486;
		D[B]=E[B]^1549556828
	}
	var G=core_md5(A.concat(str2binl(F)),512+F.length*chrsz);
	return core_md5(D.concat(G),512+128)
}

function safe_add(A,D){
	var C=(A&65535)+(D&65535);
	var B=(A>>16)+(D>>16)+(C>>16);
	return(B<<16)|(C&65535)
}

function bit_rol(A,B){return(A<<B)|(A>>>(32-B))}
function str2binl(D){var C=Array();var A=(1<<chrsz)-1;for(var B=0;B<D.length*chrsz;B+=chrsz){C[B>>5]|=(D.charCodeAt(B/chrsz)&A)<<(B%32)}return C}
function binl2str(C){var D="";var A=(1<<chrsz)-1;for(var B=0;B<C.length*32;B+=chrsz){D+=String.fromCharCode((C[B>>5]>>>(B%32))&A)}return D}

function binl2hex(C){
	var B=hexcase?"0123456789ABCDEF":"0123456789abcdef";
	var D="";
	for(var A=0;A<C.length*4;A++){
		D+=B.charAt((C[A>>2]>>((A%4)*8+4))&15)+B.charAt((C[A>>2]>>((A%4)*8))&15)
	}
	return D
}
function binl2b64(D){
	var C="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	var F="";
	for(var B=0;B<D.length*4;B+=3){
		var E=(((D[B>>2]>>8*(B%4))&255)<<16)|(((D[B+1>>2]>>8*((B+1)%4))&255)<<8)|((D[B+2>>2]>>8*((B+2)%4))&255);
		for(var A=0;A<4;A++){
			if(B*8+A*6>D.length*32)
				{F+=b64pad}
			else
				{F+=C.charAt((E>>6*(3-A))&63)}
		}
	}
	return F
}

これをそのままrubyに移植しようかと思ったが…
ちょっと待てよ。
これ、関数の名前のまんまにハッシュ関数MD5で処理にすれば良いのではなかろうか… と思い至る。<続く>

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

更に、もっとペタを貼る相手を探す方法として、アメブロ内のランキングを利用する。
アメブロでは、ジャンル毎にランキングが公開されている。(元々このペタ貼りの自動化ソフトも、そのランキングを上げるために楽にペタを貼りたいという要望を受けて作り始めたモノなのだが…)
そのジャンル毎のランキングを使って、上限の500件までペタを貼る為のターゲットのブログを抽出する。
アメブロのランキングurlは以下の通り
http://ranking.ameba.jp/genre/detail?genreCode=****&page=n
ここで、****はブログのジャンル、nはランキングのページ数。各ジャンル毎に500位まで公開。各ページに50位毎に表示されるから、n=1〜10までのページを読み込めば、公開されているランキングを全部得る事が出来る。
ランキングのブログIDは、昨日作成した正規表現で抽出して、昨日同様、配列listに追加する。
僕は本と映画とマンガのランキングに参加しているので、そのジャンルのランキングのデータを収集する。

#自分のつけたい分野のランキングを確認(1)。本、読書ランキング上位のブログ
for i in 1..10
  res = Net::HTTP.get('ranking.ameba.jp','/genre/detail?genreCode=book&page='+i.to_s)
  res.to_s.scan(ameba_url) do |a|
    list.push(a[0])
    #p a
  end
end

#自分のつけたい分野のランキングを確認(2)。映画ランキング上位のブログ
for i in 1..10
  res = Net::HTTP.get('ranking.ameba.jp','/genre/detail?genreCode=cinema&page='+i.to_s)
  res.to_s.scan(ameba_url) do |a|
    list.push(a[0])
    #p a
  end
end

#自分のつけたい分野のランキングを確認(3)。マンガランキング上位のブログ
for i in 1..10
  res = Net::HTTP.get('ranking.ameba.jp','/genre/detail?genreCode=manga&page='+i.to_s)
  res.to_s.scan(ameba_url) do |a|
    list.push(a[0])
    #p a
  end
end

もちろん、昨日同様、最後に不要なID、重複したIDを外す。

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