スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

【同じタグを付けた記事の一覧】

FC2 ブログをメモ帳と JavaScript で遊ぶ

2010年08月31日(火)23時08分

概要

ここしばらくはずっと過去記事の見直し作業をやっていたわけですが、ある程度定型的な作業が数十件~数百件、あるいはそれ以上ある場合、何とかして手抜き(効率化とか自動化ともいう)ができないものかと考えてみるわけです。
そこで目を付けたのが XML-RPC というブログ情報を取得したり記事を投稿したりできるプロトコル(手続き)で、これは Windows Live Writer でも使用されているものです。
ただ、調べてみたところ、これでできることというのが、「かゆいところに微妙に手が届かない」感じで、結果的に今回の目的には全く使いはしなかったのですが、JavaScript からでも利用でき、その JavaScript は Windows であれば「メモ帳」に書いて「.js」の拡張子で保存し、ダブルクリックすれば使えるという手軽さですので、まあそのうちまた使うこともあるかもしれない、ということで、調べたことをまとめてみました。
そういう経緯で、この記事では、Movable Type 互換の XML-RPC 通信をサポートしているブログ・システムに対して JavaScript を用いてアクセスし、情報を取得したり送信したりする方法について説明しています。

今回の記事ですること・できること

テスト目的でしたので、さわりだけ、そして失敗すると痛そうな投稿関係は試さず、データの取得が中心です。
というわけで、ブログからこれまでに投稿した記事のタイトル一覧を取得するまでの解説を作ってみました。
とはいえ、ほかの操作も基本は変わりませんので、そこまでが理解できれば十分使えるようになるのではないかと思います。

おことわり

タイトルに「JavaScript で」とありますが、ここでいう JavaScript は Windows に搭載されているスクリプト・エンジンである「JScript」のことで、また、Windows に存在する各種オブジェクト(COM コンポーネント)が、セキュリティ設定などの制限を受けずに生成・利用できることが前提となっています。
そういうことですので、ここに書かれている内容は Windows 用で、ローカルに保存された拡張子「.js」のテキストファイルを実行することで利用するもの、ということになります。

Movable TypeFC2 ブログ

まずはじめに、XML-RPC というのは、どうも送信・取得する情報の基本的な書式が規定されているだけで、実際に「こういう命令を受信したら、こういう情報を返す」というコマンド群は、別に規定するもののようです。
そして、FC2 ブログではこのコマンド群として、Movable Type という非常に有名なブログ構築システムと互換のものを使用しているそうです。
ですので、ここに書かれた内容は、Movable Type(あるいはその互換システム)で構築された他のブログでも大体使えるのではないかと思いますが、基本的には FC2 ブログで使用することを念頭に書かれています。
特に、Movable Type では「カテゴリ」と「キーワード」というものがあり、FC2 ブログにも「ジャンル」「カテゴリ」「ユーザータグ」がありますが、これらは別の存在らしく、FC2 ブログではかろうじて「カテゴリ」の取得は XML-RPC から行えますが、「ジャンル」「ユーザータグ」に関しては設定・変更はもちろん、取得もできないようです(Windows Live Writer がこれらを設定できないのもここに原因があると思われます)。

Microsoft.XMLHTTP と Microsoft.XmlDom

本編に入る前に、XML-RPC とは直接の関係はありませんが、しかし使うためには必須(または知っていた方が便利)な事柄についての説明がいくつか必要になります。
そのうちの最初の二つが「XMLHTTP」と「XmlDom」です。
「XMLHTTP」は通信をするためのオブジェクトですから今回の目的には必須、「XmlDom」は XML 文書を操作するためのオブジェクトで、XML-RPC は XML ですから、使うと便利になります。
これら二つのオブジェクトは、Windows では古くから存在するものだけに、互換性等の問題からバージョン管理が複雑になっており、「この ProgID を指定しておけば、システムに存在するうちで最新のバージョンが使える」という風にはなっておりません。
ということで、使う際には「最新のバージョンから一つずつ探していき、最初に見つかったものを使う」という作業が必要になります。
まずはそれを関数化しておきます。

//XmlHttpオブジェクトを生成する。
function CreateXmlHttp()
{
	var ReturnObject=null;
	var CreateNames=
		[
			//安全・独立だがWin2k以降。
			"WinHttp.WinHttpRequest.5.1",
			"WinHttp.WinHttpRequest.5",
			"WinHttp.WinHttpRequest",
			//安全・独立だがWin2k以降のサーバ系OS。
			"Msxml2.ServerXMLHTTP.6.0",
			"Msxml2.ServerXMLHTTP.5.0",
			"Msxml2.ServerXMLHTTP.4.0",
			"Msxml2.ServerXMLHTTP.3.0",
			"Msxml2.ServerXMLHTTP",
			"Microsoft.ServerXMLHTTP",
			//インターネット・オプションの影響を受ける。がWin9x系ではこれになる。
			"Msxml2.XMLHTTP.6.0",
			"Msxml2.XMLHTTP.5.0",
			"Msxml2.XMLHTTP.4.0",
			"Msxml2.XMLHTTP.3.0",
			"Msxml2.XMLHTTP.2.6",
			"Msxml2.XMLHTTP",
			"Microsoft.XMLHTTP"
		];

	for(var Cnt=0;Cnt<CreateNames.length;Cnt++)
	{
		try{ReturnObject=WScript.CreateObject(CreateNames[Cnt]);}catch(exception){ReturnObject=null;}
		if(ReturnObject)break;
	}

	return ReturnObject;
}

//XmlDomオブジェクトを生成する。
function CreateXmlDom()
{
	var ReturnObject=null;
	var CreateNames=
		[
			"Msxml2.DOMDocument.5.0",
			"Msxml2.DOMDocument.4.0",
			"MSXML2.DOMDocument.3.0",
			"MSXML2.DOMDocument",
			"MSXML.DOMDocument",
			"Microsoft.XmlDom"
		];

	for(var Cnt=0;Cnt<CreateNames.length;Cnt++)
	{
		try{ReturnObject=WScript.CreateObject(CreateNames[Cnt]);}catch(exception){ReturnObject=null;}
		if(ReturnObject)break;
	}

	return ReturnObject;
}

UTF-8 → UTF-16

「直接は関係ない」二番目の項目は文字コード変換です。
他はわかりませんが、少なくとも FC2 ブログにおいては、返送されてくる XML 文書の文字コードは UTF-8 になっています。
JavaScript(JScript)では文字コードを UTF-16 か ASCII(Shift-JIS)として扱いますので、返ってきた内容(XmlHttp オブジェクトの responseText)を文字列としてそのまま使うと日本語が化けます。
そこで、文字列として使う場合には 内容(UTF-8)がバイナリのままで格納されている XmlHttp オブジェクトの responseBody を UTF-16 に変換して使うことになります。
といっても、今回はこのあたりの処理を内部で自動でやってくれているらしい XmlDom オブジェクトを使いますので、「直接は関係ない」どころか間接的にも関係はないのですが、「XML も文字列として使った方が楽」という場合に備えて、「responseBody(UTF-8) → String(UTF-16)」の処理を関数化しておきます。
なお、XmlHttp オブジェクトには responseXML というものも存在し、ここに XmlDom オブジェクト化された内容でも入っていれば便利そうですが、なぜか null で返ってきます。

//XmlHttpオブジェクトのresponseBodyを、CharSetで指定された文字コードのバイナリとみなし、UTF-16に変換して返す。
function ResponseBodyToUnicode(ResponseBody,CharSet)
{
	var ReturnText="";
	if(!CharSet)CharSet="utf-8";

	try
	{
		//ResponseBodyを指定された文字コードのバイナリとしたオブジェクトを生成。
		var ResBodyStream=WScript.CreateObject("ADODB.Stream");
		ResBodyStream.Type=1;
		ResBodyStream.Open();
		ResBodyStream.Write(ResponseBody);
		ResBodyStream.Position=0;
		ResBodyStream.Type=2;
		ResBodyStream.Charset=CharSet;
		//内部UNICODE扱い設定のオブジェクトを生成。
		var UnicodeStream=WScript.CreateObject("ADODB.Stream");
		UnicodeStream.Charset="unicode";
		UnicodeStream.Open();
		//ResponseBodyオブジェクトの内容をUNICODEオブジェクトに流し込むと自動変換される。
		ResBodyStream.CopyTo(UnicodeStream);
		ResBodyStream.Close();
		ResBodyStream=null;
		//変換されたUNICODE文字列を取得。
		UnicodeStream.Position=0;
		ReturnText=UnicodeStream.ReadText();
		UnicodeStream.Close();
		UnicodeStream=null;
	}
	catch(exception)
	{
		ReturnText="";
	}

	return ReturnText;
}

この後のスクリプトの流れ

XML-RPC での通信作業は基本的に

  1. XmlHttp オブジェクトを ブログが公開している XMP-RPC の窓口の URL で POST 指定を使って開く
  2. 目的のコマンドを含んだ XML 文書を sendする
  3. 返送されてきた XML 文書を解析してして処理する

を繰り返していくことになります。
送りつける XML には「コマンド文字列」が必ず含まれ、さらに「ユーザー ID」「パスワード」の2つがほぼ必ず含まれており、あとはコマンドに応じて必要な情報(記事本文や記事数など)が追加されます。
ということで、とりあえず「XML-RPC の窓口の URL」「ユーザー ID」「パスワード」の3つは、自分で設定しなければならない必須の情報になりますが、それ以外の情報に関しては、この3つの情報をもとに XML-RPC から引き出すことができます。
ということを踏まえ、この後の流れとして、まずグローバル変数として「Blog」というオブジェクトと「Posts」という配列を作り、「Blog」オブジェクトのプロパティにブログ固有の情報(ブログ名や ID)を取得・設定し、「Posts」配列に各記事の情報(記事タイトルやURL)を格納していこうと思います。

Blog オブジェクトのプロパティ
Rpc XML-RPC の窓口の URL
App AppKey(FC2 ブログでは不要)
User ユーザー ID
Pass パスワード
Num 複数のブログが存在する場合の取得対象番号(FC2 ブログでは常に0)
Max 取得する最大記事数(記事総数を取得する方法がないため、多めに指定し上限まで取得する)
ID ブログ ID
URL ブログの URL
Name ブログのタイトル
Err エラー発生時の内容

Posts 配列の各要素のプロパティ
Date 投稿日時
ID 投稿 ID
URL URL
Name タイトル

ブログの基本情報を得る

まず、「Blog」オブジェクトを生成し、そのプロパティにブログの基本情報(ブログ・タイトルや URL など)を設定していきます。
そのためには、XML-RPC の通信窓口の URL に対して、以下のような XML 文書を送り付けます。

<?xml version="1.0"?>
<methodCall>
	<methodName>blogger.getUsersBlogs</methodName>
	<params>
		<param><value><string>「AppKey」</string></value></param>
		<param><value><string>「ユーザーID」</string></value></param>
		<param><value><string>「パスワード」</string></value></param>
	</params>
</methodCall>

文書中の「AppKey」とある部分は、少なくとも FC2 ブログにおいては必要がない(空文でいい)項目です。
成功すると以下のような XML 文書が返送されてきます。

<?xml version="1.0" ?>
<methodResponse>
	<params>
		<param>
			<value>
				<array>
					<data>
						<value>
							<struct>
								<member>
									<name>blogid</name>
									<value><string>「ブログID」</string></value>
								</member>
								<member>
									<name>url</name>
									<value><string>「ブログURL」</string></value>
								</member>
								<member>
									<name>blogName</name>
									<value><string>「ブログ・タイトル」</string></value>
								</member>
							</struct>
						</value>
					</data>
				</array>
			</value>
		</param>
	</params>
</methodResponse>

ここから読み取れる情報は「ブログ ID」「ブログ URL」「ブログ・タイトル」の3つです。
そして重要な点はそれら3つの情報が「array」タグで囲まれているということで、「array」とは配列のことです。
FC2 ブログに限れば、一つのユーザー ID には一つのブログしか存在しないわけですが、ブログ・サービス(システム)によっては一つのユーザー ID で複数のブログを管理することができ、その場合は「ブログ ID」「ブログ URL」「ブログ・タイトル」が複数含まれることになります。
今回は複数ある場合も、そのうち一つを対象として操作するため、事前に何番目のブログを対象とするかを指定しておきます(FC2 ブログの場合は0のみ)。
ということで、「Blog」オブジェクトに「XML-RPC の窓口の URL」「ユーザー ID」「パスワード」「対象ブログ番号」を設定して渡すと、残りの情報を取得・設定して返す関数を作ります。

var Blog={"Rpc":"","App":"","User":"","Pass":"","Num":0,"Max":32767,"ID":"","URL":"","Name":"","Err":""};

//XML-RPCのURL・ユーザーID・パスワードを使いブログ情報を取得します。
function GetBlogData(BlogData)
{
	var XmlHttp=CreateXmlHttp();
	XmlHttp.open("POST",BlogData.Rpc,false);
	XmlHttp.setRequestHeader("Content-Type","text/xml;");
	var SendXml='<?xml version="1.0"?>';
	SendXml+='<methodCall>';
	SendXml+='<methodName>blogger.getUsersBlogs</methodName>';
	SendXml+='<params>';
	SendXml+='<param><value><string>'+BlogData.App+'</string></value></param>';
	SendXml+='<param><value><string>'+BlogData.User+'</string></value></param>';
	SendXml+='<param><value><string>'+BlogData.Pass+'</string></value></param>';
	SendXml+='</params>';
	SendXml+='</methodCall>';
	XmlHttp.send(SendXml);

	if(200<=XmlHttp.status&&XmlHttp.status<300)
	{
		var XmlDom=CreateXmlDom();
		XmlDom.async=false;
		XmlDom.load(XmlHttp.responseBody);
		//各プロパティに必要な情報を設定。
		BlogData.ID=XmlDom.selectNodes('//member[name="blogid"]/value/string')[BlogData.Num].firstChild.nodeValue;
		BlogData.URL=XmlDom.selectNodes('//member[name="url"]/value/string')[BlogData.Num].firstChild.nodeValue;
		BlogData.Name=XmlDom.selectNodes('//member[name="blogName"]/value/string')[BlogData.Num].firstChild.nodeValue;
	}

	return true;
}

記事のタイトル一覧を得る

続いて「Posts」配列のに個別の記事情報(記事タイトルや URL など)を設定するわけですが、これには最新の記事から指定した件数分の記事の、すべての情報を一気に得る「metaWeblog.getRecentPosts」と、タイトルなど最小限の一覧を取得する「mt.getRecentPostTitles」の二種類のコマンドが存在します。
全情報をまとめて取得するのは、サーバーへの負荷やメモリ不足の危険性などを考慮すると避けたほうが良い方法だと思われますので、まずはタイトル一覧を取得します。
そのためには、以下のような XML 文書を送り付けます。

<?xml version="1.0" ?>
<methodCall>
	<methodName>mt.getRecentPostTitles</methodName>
	<params>
		<param><value><string>「ブログID」</string></value></param>
		<param><value><string>「ユーザーID」</string></value></param>
		<param><value><string>「パスワード」</string></value></param>
		<param><value><int>「取得記事数」</int></value></param>
	</params>
</methodCall>

この XML 文書によって、最新の投稿記事から順に、「取得記事数」で指定した件数分の記事情報が返されるわけですが、ここで問題になるのが「取得記事数」です。
全記事を取得するためには何件を指定すればよいのか、つまり「そのブログが保持しているすべての記事の件数」を知る必要があるのですが、そのような情報を取得できるコマンドが見当たりません。
とりあえず FC2 ブログでは「0」を指定することで全件取得できましたが、これが規格として定められているものなのか、FC2 ブログが独自に実装している仕様なのかわかりません。
逆に保有記事数をはるかに超える大きな数値を渡しても、特にエラーになるわけでもなく、返せる限りの情報(つまり全保有記事情報)を返してくるようですので、こちらの方が汎用性の高い方法のような気がします。
成功すると以下のような XML 文書が返送されてきます。

<?xml version="1.0" ?>
<methodResponse>
	<params>
		<param>
			<value>
				<array>
					<data>
						<value>
							<struct>
								<member>
									<name>dateCreated</name>
									<value><dateTime.iso8601>「年月日時分秒」</dateTime.iso8601></value>
								</member>
								<member>
									<name>userid</name>
									<value><string>「ブログID」</string></value>
								</member>
								<member>
									<name>postid</name>
									<value><string>「投稿ID」</string></value>
								</member>
								<member>
									<name>title</name>
									<value><string>「記事タイトル」</string></value>
								</member>
							</struct>
						</value>
					</data>
				</array>
			</value>
		</param>
	</params>
</methodResponse>

ここから読み取れる(新規の)情報は「投稿日時」「投稿 ID」「記事タイトル」の3つです。
ここでもまた一つ問題があり、つまりこのコマンドでは「記事の URL」が取得できません。
この「記事の URL」を取得するためには、ここからまた「投稿 ID」を使って個別に各記事の全情報を取得するコマンドを呼び出して確認していく必要があります(であれは初めから「metaWeblog.getRecentPosts」の方を使って全記事の全情報を取得してしまってもいいかもしれませんが)。
ただ、ブログ・サービスの多くは、「投稿 ID」から規則的に生成された文字列を、その記事の URL としているのではないかと思います。
少なくとも FC2 ブログにおいては「”ブログの URL”/blog-entry-“投稿 ID”.html」が記事の URL になりますので、ここではそれを前提として、各記事ごとの「投稿日時」「投稿 ID」「URL」「タイトル」を取得していく関数を作ります。

//指定されたブログ情報から記事基本(投稿日時・投稿ID・URL・タイトル)情報一覧を取得し、配列に格納して返します。
//URLはFC2ブログを前提として生成したものが格納されます(BlogData.URL+"blog-entry-"+投稿ID+".html")。
function GetPostTitles(BlogData)
{
	var XmlHttp=CreateXmlHttp();
	XmlHttp.open("POST",BlogData.Rpc,false);
	XmlHttp.setRequestHeader("Content-Type","text/xml;");
	var SendXml='<?xml version="1.0"?>';
	SendXml+='<methodCall>';
	SendXml+='<methodName>mt.getRecentPostTitles</methodName>';
	SendXml+='<params>';
	SendXml+='<param><value><string>'+Blog.ID+'</string></value></param>';
	SendXml+='<param><value><string>'+Blog.User+'</string></value></param>';
	SendXml+='<param><value><string>'+Blog.Pass+'</string></value></param>';
	SendXml+='<param><value><int>'+Blog.Max+'</int></value></param>';
	SendXml+='</params>';
	SendXml+='</methodCall>';
	XmlHttp.send(SendXml);

	var PostList=new Array();
	if(200<=XmlHttp.status&&XmlHttp.status<300)
	{
		var XmlDom=CreateXmlDom();
		XmlDom.async=false;
		XmlDom.load(XmlHttp.responseBody);

		var Structs=XmlDom.selectNodes('//struct');
		for(var PostCnt=0;PostCnt<Structs.length;PostCnt++)
		{
			var PostData={"Date":"","ID":"","URL":"","Name":""};
			//各プロパティに必要な情報を設定。
			PostData.Date=Structs[PostCnt].selectSingleNode('member[name="dateCreated"]/value/dateTime.iso8601').firstChild.nodeValue;
			PostData.ID=Structs[PostCnt].selectSingleNode('member[name="postid"]/value/string').firstChild.nodeValue;
			PostData.Name=Structs[PostCnt].selectSingleNode('member[name="title"]/value/string').firstChild.nodeValue;
			//FC2ブログ形式でURLを生成。
			PostData.URL=BlogData.URL;
			if(!PostData.URL.match(/\/$/))PostData.URL+="/";
			PostData.URL+="blog-entry-"+PostData.ID+".html"
			//配列に追加。
			PostList[PostList.length]=PostData;
		}
	}

	return PostList;
}

ブログ目次(タイトル一覧)HTML作成スクリプト

ここまでのまとめとして、取得した情報をもとに、目次ページの HTML ファイルを作るスクリプトを書いてみました。

上記スクリプト冒頭部分の「XML-RPC の窓口の URL」「ユーザー ID」「パスワード」を記入して実行すると、「FC2BlogIndexer.html」が生成されます。
「FC2BlogIndexer.html」は、該当記事の URL にリンクされた記事タイトルが列挙されています。
ただし、URL を FC2 ブログ前提で生成しているため、FC2 ブログ専用です(URL 文字列生成部を少し変更すればほかでも使えると思いますが)。

ファイルのアップロード

ついででファイル・アップロード用関数のサンプルも作ってみました。
上記で説明した「FC2BlogIndexer.js」の後ろの方にくっつけてあります(FC2BlogIndexer.js 自体はこの関数を使用しません)。
関数は「UploadFile(BlogData,FileName,UploadName)」で呼び出し、引数の BlogData は上記「ブログの基本情報を得る」で作った GetBlogData を通した「Blog」オブジェクト、FileName はアップロードするファイル名、UploadName はアップロードされたファイルに付けるファイル名です。
成功するとアップロードされたファイルの場所(URL)が返り、失敗すると空文字(“”)が返ります。
当然ながら FC2 ブログFC2 ブログで扱えない形式(.zip や .exe)をアップロードしても失敗するわけですが、よくわからないのが同一(同名)ファイルをアップロードした場合で、これが上書きとならず、管理画面では時間違いの同アドレス・同ファイル名のファイルとして同時複数個リストアップされてしまいます。
そして XML-RPC で可能なファイル操作は、この「アップロード」のみで、ダウンロードも名前の変更も、保有ファイル名一覧の取得ですらできないようです。

欠番の復活(失敗)

当ブログには「投稿 ID」の1番にあたる記事、つまり「blog-entry-1.html」というページが存在しません。
これは開設時にテストで作った後、削除して最初の記事を作り直したためで、FC2 ブログでは記事(下書きを含む)を削除すると、そこに割り当てられていた ID は欠番となり、再利用されないようです。
で、XML-RPC による記事の編集では「投稿 ID」を直接指定することで再編集対象を指示します。
ということは、「ここで欠番となった ID を指定してタイトルや本文を送り付ければ、欠番を復活させられるのではないか?」と考えられるわけで、そしてやってみました。
が、てきませんでした。
特にエラーが発生するわけではありませんが、ID(記事)が復活することもなく、「何事も起こらない」という結果に終わりました。

関連記事

【同じタグを付けた記事の一覧】
ソースコード FC2 プログラミング JavaScript WindowsLiveWriter ブログ

スポンサーサイト

コメントの投稿

非公開コメント

最新記事
最新コメント
Amazonおまかせリンク
カテゴリ
タグクラウド
Amazonお買い得ウィジェット
カレンダー
09 | 2017/10 | 11
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 - - - -
月別アーカイブ
プロフィール

電脳太助

Author:電脳太助
Website:電脳スピーチ web

RSSリンクの表示
メールフォーム

名前:
メール:
件名:
本文:

サイト内検索
Ads by Google
FC2アクセスランキング
Ads by Google
FC2拍手ランキング
ユーザータグ

音楽管理(66)
ポータブル(57)
ソフト紹介(44)
プログラミング(42)
音声技術(41)
自作ソフト(35)
サイト運営(32)
FC2(31)
ブログ(30)
iTunes(27)
Windows(25)
LISMO(24)
音声合成(23)
音声認識(22)
x-アプリ(22)
電子ブック(22)
eラーニング(20)
バックアップ(19)
語学学習(19)
foobar2000(18)
ソースコード(18)
WindowsLiveWriter(15)
画像管理(15)
C++(14)
アフィリエイト(10)
DnspTools(10)
fi-6130(9)
FLAC(9)
JavaScript(9)
ウォークマン(9)
英語音読学習計画(8)
Gracenote(8)
Prolog(8)
ベクター(8)
雑記(8)
CodeBlocks(7)
SyntaxHighlighter(7)
TraConv(7)
spcbght(7)
wxWidgets(7)
VirtualBox(6)
W63CA(6)
DCP-J552N(6)
WinRT(6)
WindowsLiveMesh(6)
iGoinLM(6)
英語発音矯正実験(6)
ExactAudioCopy(6)
MP3Gain(6)
LAME(5)
音楽技術(5)
Mery(5)
楽器演奏(5)
GalateaTalk(4)
nLite(4)
WindowsLiveSkyDrive(4)
ホームページ(4)
GalateaProject(4)
MIDI(4)
LLVM(4)
PC-98(3)
カウンター(3)
AACGain(3)
iTCDini(3)
OverCutChecker(3)
拍手(3)
PK-513L(3)
UniversalExtractor(3)
アクセスランキング(3)
ImageCompositeEditor(2)
アクセス解析(2)
OCR(2)
qtaacenc(2)
資格試験(1)
AquesTalk(1)
AquesCmdDl(1)

FC2アクセスランキング
最新トラックバック
アクセスランキング
[ジャンルランキング]
コンピュータ
161位
アクセスランキングを見る>>

[サブジャンルランキング]
ソフトウェア
19位
アクセスランキングを見る>>
FC2カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。