SEO対策の一環としてGoogleなどの検索エンジンで自分のサイトの順位を確認したいと思うことがよくあります。
毎日、検索エンジンで検索して結果を記録するようなアナログ的な作業を行っている人もいるかもしれませんが、世の中には沢山のSEO対策ツールがあり、このようなツールを利用する事で自サイトのランキング情報を手軽に確認することができます。
しかしながらツールでは満足できないという人も多いのではないのでしょうか。
そのような要望からでしょうか、GoogleやYahoo、MSNの各検索エンジン共に検索用WebサービスAPIを公開してくれるようになりました。
YahooやMSNでは現在(2007年11月時点)も開発者向けにWebサービスAPIを提供していますが、Googleは2006年12月でWebサービスの新規利用登録を終了しました。
このサンプルではブラウザからのGoogle検索結果(HTMLデータ)より、検索結果を解析するサンプルを作成しました。
尚、このサンプルの方法を用いたGoogleへのアクセスは自己責任でお願いします。(莫大なアクセスを行った場合、ペナルティが課せられる可能性があります)
本サンプルを理解するには、最低以下の知識が必要になります。(なくてもサンプルは動きますが拡張は難しいと思います)
- DOM(Document Object Model)とDOM API(GetElementById,GetElementsByTagNameの二つのメソッドは最低必要)
- CSSのクラス名という概念
Google検索結果ページの解析は以下の手順で行います。
- 検索条件をパラメータとして付加したURLでGoogleサイトへアクセス(GET)する。
- レスポンスページ(HTMLデータ)の検索結果エリア(DIVタグのid="res"の領域)のデータを解析する。
上記2のHTM解析ですが、通常HTMLパーサーを利用してHTMLデータを構造化して解析を行いますが、.net FrameworkにはHTML Parserが用意されていない為、このサンプルではWebBrowserコントロールを利用してHTML解析を行います。
WebBrowserコントロール(COM)はWebBrowserを利用してブラウザイメージをキャプチャするサンプルでも紹介しましたが
『シングルスレッドアパートメント』モード(STAモード)のスレッド内からではないと利用できません。
Googleに限った事ではありませんが、検索エンジンからの検索方法はGETアクセスにて行います。
つまり、検索条件を検索URLにパラメータとして付加してアクセスし、サーバ側(Google側)でパラメータを取得して、それをもとに検索して結果を返すというものです。
Googleで『ASP.NET サンプル』と検索した場合、以下のようなURLになります。(利用する環境により付加されるパラメータは若干異なります)
http://www.google.co.jp/search?source=ig&hl=ja&rlz=&q=ASP.NET+%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB&btnG=Google+%E6%A4%9C%E7%B4%A2&meta=
| パラメータ | 意味 |
| hl | 言語設定(日本語) |
| q | 検索文字(URLエンコードした内容) |
| num | 1ページあたりの表示件数(default:10,MAX:100) |
| start | 検索結果が複数ページある場合の表示対象ページの先頭のデータ件数番号(0オリジン) ※1ページあたり10件表示で2ページ目ならこの値は10となります |
GoogleのURLパラメータの仕様が公開されていないか調べたのですが、見つけられませんでした。上記はあくまで私が判断した内容となります。
検索した結果ページのHTMLは以下のようになっています。(検索結果部を抜粋しています)
<div id=res>
<div>
<div class=g>
<h2 class=r>
<a href="http://www.squabu.com/" class=l >ASP.NET2.0実用<b>サンプル</b>コード コピペでどうぞ Topページ</a>
</h2>
</div>
</div>
</div>
上記のようにdivタグ(id=res)内のh2タグ(class=r)に検索結果データが格納されています。
h2タグ下のaタグのテキストとURLが検索結果のサイトのタイトルとURLになりますが、検索結果順位データはありませんので、自身でカウントする必要があります。
以上の仕様を踏まえたサンプルコードは以下のようになります。
Googleへ検索~解析を行うクラスと検索条件・結果情報を格納するクラス
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Windows.Forms;
using System.Text;
using System.Threading;
/// <summary>
/// GoogleSearch の概要
/// ※GoogleへGETアクセスで検索結果を解析するサンプルクラス
/// </summary>
public class GoogleSearch
{
/// <summary>
/// Http通信完了を受け取る為のManualResetEvent
/// </summary>
private ManualResetEvent _mre = new ManualResetEvent(false);
public GoogleSearch()
{
}
/// <summary>
/// Google検索~結果解析処理
/// ※ASP.NETからも利用できるように、スレッドを生成しGoogleアクセス処理を行っています。
/// </summary>
/// <param name="sInfo"></param>
public void GetSearchRankingData(GoogleSearchInfo sInfo)
{
//イベントの状態を非シグナル状態に設定
this._mre.Reset();
//スレッドを生成(.net Frameword2.0よりParameterizedThreadStartを利用する事により引数付きスレッドの生成が可能になりました)
Thread getRankThread = new Thread(new ParameterizedThreadStart(getSearchRankingDataForThread));
//WebBrowserはシングルスレッドアパートメントモードでのみ実行可能なのでスレッドのモードを設定して実行します
getRankThread.SetApartmentState(ApartmentState.STA);
getRankThread.Start(sInfo);
//スレッドが終了するまで、メインスレッドを待機
this._mre.WaitOne();
//スレッドを終了
getRankThread.Abort();
}
/// <summary>
/// Googleアクセス処理(別スレッドとして起動されます)
/// ※ASP.NETからWebBrowserコンポーネントを利用する為に別スレッドとして起動する必要があります。
/// </summary>
/// <param name="obj"></param>
private void getSearchRankingDataForThread(object obj)
{
GoogleSearchInfo sInfo = (GoogleSearchInfo)obj;
//検索~解析処理を実行
this.googleSearch(sInfo);
if (this._mre != null)
{
this._mre.Set();
}
}
/// <summary>
/// Googleにアクセスし、検索結果を解析して対象のサイトの順位を格納します。
/// </summary>
/// <param name="sInfo">検索条件・結果情報格納クラスのインスタンス</param>
/// <returns>true:処理成功 false:タイムアウト</returns>
private bool googleSearch(GoogleSearchInfo sInfo)
{
using (WebBrowser browser = new WebBrowser())
{
//Google検索結果ページへのURLを作成する。(1ページ:100件の結果を取得)
string searchUrl = this.createGoogleSearchUrl(0, sInfo.SearchKeyword);
DateTime startTime = DateTime.Now;
//Googleにアクセス
browser.Navigate(searchUrl);
while (browser.ReadyState != WebBrowserReadyState.Complete)
{
Thread.Sleep(0);
Application.DoEvents();
TimeSpan span = DateTime.Now - startTime;
if (span.Seconds > sInfo.TimeOut)
{
//タイムアウト値を超えたので強制終了する
return false;
}
}
//検索結果を解析
this.analyzeGoogleResult(browser, 0, sInfo);
}
return true;
}
/// <summary>
/// Google検索用のURL(GETパラメータに検索条件を付加)の作成処理
/// </summary>
/// <param name="accessPageIndex">検索結果ページへのインデックス(0オリジン)</param>
/// <param name="searchKeyWord">検索キーワード</param>
/// <returns>Google検索結果ページへのURL</returns>
private string createGoogleSearchUrl(int accessPageIndex, string searchKeyWord)
{
int countPerPage = 100;//検索結果1ページの表示件数(ここを10にすれば1ページあたり10件の結果ページになります)
StringBuilder buf = new StringBuilder();
buf.Append("http://www.google.co.jp/search?")
.Append("source=ig&hl=ja&")
.Append("num=").Append(countPerPage.ToString()).Append("&")
.Append("q=").Append(HttpUtility.UrlEncode(searchKeyWord));
if (accessPageIndex > 0)
{
buf.Append("&start=").Append(countPerPage * accessPageIndex);
}
return buf.ToString();
}
/// <summary>
/// Google検索結果を解析処理
/// </summary>
/// <param name="browser">GoogleへアクセスしたWebBrowserのインスタンス</param>
/// <param name="curPageIndex">取得ページのIndex(0オリジン)</param>
/// <param name="sInfo">検索条件と結果を格納するGoogleSearchInfoクラスのインスタンス</param>
private void analyzeGoogleResult(WebBrowser browser, int curPageIndex, GoogleSearchInfo sInfo)
{
//Googleの検索結果はDIV id=res領域に格納され、各結果データはDIV領域class=gに格納される。
//サイトタイトルとURLはh2タグ(class=r)に格納されている。(URLはh2タグの子エレメントで格納されている)
//検索結果領域を取得
int count = 0;
HtmlElement googleResElm = browser.Document.GetElementById("res");
if (null != googleResElm)
{
//検索結果データを取得
HtmlElementCollection elmCol = googleResElm.GetElementsByTagName("h2");
foreach (HtmlElement tmpElm in elmCol)
{
//クラス名の取得する場合は、IHTMLElementにキャストする。
//mshtml名前空間はMicrosoft HTML Object Libraryを参照設定に追加する必要があります。
mshtml.IHTMLElement iElm = (mshtml.IHTMLElement)tmpElm.DomElement;
if (!iElm.className.Equals("r")) continue;
HtmlElementCollection aTagElmCol = tmpElm.GetElementsByTagName("a");
if (null == aTagElmCol || aTagElmCol.Count == 0) continue;
string title = aTagElmCol[0].InnerText;
string link = aTagElmCol[0].GetAttribute("href");
if(link.Contains(sInfo.TargetSiteKeyword)){
//検索結果のサイトのURLに検索対象のキーワード(通常ドメインを設定する)が含まれている場合、検索対象と判断する。
sInfo.Ranking = count + 1;
}
count++;
}
}
}
}
/// <summary>
/// Google検索時の検索条件と結果情報を格納するクラス
/// </summary>
public class GoogleSearchInfo
{
/// <summary>
/// 検索キーワード(Googleで検索する場合と同様に複数キーワドの場合、スペースで区切ります)
/// </summary>
private string _searchKeyword = "";
/// <summary>
/// ランキングを調べる為のターゲットサイト判別情報(通常ドメイン 例:http://www.squabu.com)
/// </summary>
private string _targetSiteKeyword = "";
/// <summary>
/// ランキング情報
/// </summary>
private int _ranking = -1;
/// <summary>
/// Googleアクセス時のタイムアウト値(5秒)
/// </summary>
private int _timeOut = 5;
public GoogleSearchInfo(string searchKeyword,string targetSiteKeyword)
{
this._searchKeyword = searchKeyword;
this._targetSiteKeyword = targetSiteKeyword;
}
public string SearchKeyword
{
get
{
return this._searchKeyword;
}
}
public string TargetSiteKeyword
{
get
{
return this._targetSiteKeyword;
}
}
public int Ranking
{
get
{
return this._ranking;
}
set
{
this._ranking = value;
}
}
public int TimeOut
{
get
{
return this._timeOut;
}
}
}
上記クラスは以下のようにして利用します
GoogleSearchクラスの利用例
protected void btnSearch_Click(object sender, EventArgs e)
{
GoogleSearch gSearch = new GoogleSearch();
GoogleSearchInfo sInfo = new GoogleSearchInfo("ASP.NET サンプルコード","www.squabu.com");
gSearch.GetSearchRankingData(sInfo);
//検索結果を出力
this.Label1.Text = sInfo.Ranking.ToString();
}
※このサイトが稼動している環境のTruest LevelがMediumである為、当サンプルの動作確認を本サイトで行う事はできません。
※GoogleのURLパラメータ仕様及び結果出力仕様が変更された場合、上記サンプルは正常に動作しません。(2007年11月15日時点の仕様です)