現在大多數提供網站排名的網站,其資料都是取自於 Alexa 發布的資料。但是 Alexa 的網站排名資料並不能簡單、直接地得到,這是因為 Alexa 使用了幹擾碼技術,使得編程變得困難和繁瑣。
但是從理論上講,只要能在頁面上看到的資訊,除過圖片識別現在還是個頂尖技術以外,文字資訊都可能通過抓取頁面得到源檔案,再進行分析而得到具體的資料。
我們先分析一下“電腦學習網” http://www.why100000.com 在 http://www.alexa.com 查詢排名時得到的資料:在 http://www.alexa.com 頁面輸入網址 http://www.why100000.com,點擊按鈕“Get traffic Detail”。頁面下載完畢後,查看源檔案,搜尋字串“traffic rank”(搜尋“Why100000.com has a traffic rank”可能找不到,因為單詞之間有多個空格,無法匹配),找到排名資料的附近,往後看,可以看到以下類似的字串:
<!--Did you know? Alexa offers this data programmatically. Visit http://aws.amazon.com/awis for more information about the Alexa Web Information Service.--><span class="ca53">61</span>5<span class="c34d">57</span>,<span class="c8a7">78</span><span class="c1db">35</span>4</span><!-- google_ad_section_end(name=default) -->
之所以強調“類似”,是因為每次看到的資訊一般都不完全相同,Alexa說了,是“programmatically”產生的這些資料,已經通過編程對真實資料進行了幹擾,雖然在頁面上眼睛看到的是數字字串“557354”,但是即使在頁面上“選擇-複製-粘貼”,得到的卻是字串“61557,78354”(每次重新查詢後差不多都是不相同的)。從上面的代碼中也可以看到“61557,78354”的資訊。
但是,如果仔細觀察,就會看到,真正的排名資料“557354”是包含在“61557,78354”字串中的,而實際上,它 “總是”包含在這個變化的字串中的,多試幾次就會看到!多試幾次還會看到,真正的排名字元有時沒有包含在<span class="xxxx">61</span>中,這讓我們想到,幹擾字元是通過包含在<span class="xxxx">……</span>中,使用class引用樣式表“xxxx”來使其在頁面隱含的。但是,有時排名真實資料的數字也包含在<span class="yyyy">……</span>中,那麼該樣式表應該不設定該字元(串)隱含。--這就是幹擾演算法的思想:在真實資料中插入頁面上看不到的數字,不影響人眼觀看,但對程式分析造成幹擾,而且對部分可見字元有的也加上<span>…</span>標籤,更進一步增加了程式分析的複雜度。
但是,既然瀏覽器看到了資訊,其秘密應該就包含在源檔案之中,而且一般和 Javascript、CSS 引用都可能有關係。按照這個思路,我們把源檔案相關的Javascript 指令碼、CSS 指令碼都下載下來進行分析。
而秘密其實就包含在 http://client.alexa.com/common/css/scramble.css 樣式表檔案中。開啟該檔案,可以看到以下 189 個 CSS 類的定義:
.c11e {
display: none
}
.c125 {
display: none
}
.c12d {
display: none
}
……
……
.cfe9 {
display: none
}
混淆字串中以<span class=”xxxx”>……<span>包含的“xxxx”都是在這裡定義的!真實字元<span class=”yyyy”>……<span>的樣式yyyy可能是隨機產生的,但肯定不會和這189個定義相同。
到此真相大白。我們可以通過編寫程式來獲得該真實排名資料了。
我們可以編寫傳統型程式,比如用 Delphi、C# 等編寫Windows傳統型程式,這個需要用到 Internet Browser 組件,來擷取
http://www.alexa.com/data/details/traffic_details/why100000.com
的頁面源檔案,然後進行分析,最終儲存到資料庫中。
也可以在網頁中通過Javascript指令碼操作頁面文檔來擷取該資料。這個需要對javascript很熟悉。但要把資料儲存到後台資料庫中,一般需要手工開啟頁面來操作。
也可以通過網站後台編程來實現,需要後台程式設計語言有後台擷取網頁源檔案的能力,這個用Java、ASP.NET、PHP等語言都能實現。
以上所有編程過程,都需要程式設計語言或指令碼有很強的字串分析和處理能力,常見的就是使用Regex,一般流行的語言都支援。
我選用現在很流行的 Web 程式設計語言 PHP 來實現。
編程過程簡述:
觀察源檔案,發現段落:
<!--Did you know? Alexa offers this data programmatically. Visit http://aws.amazon.com/awis for more information about the Alexa Web Information Service.-->
在源檔案中是第一次出現的。排名資訊就包含在它與
<!-- google_ad_section_end(name=default) -->
之間。這為編程提供了便利。我們可以根據這個特徵首先把最有用的資訊挖出來,以縮小範圍,為精細編程提供基礎。這個是用getBody()函數實現的,實際採用的特徵字串是“Information Service.-->”和“<!-- google_ad_section_end(name=default) -->”,抽取它們之間的字串即可。
getAlexaRank是主函數。其他函數通過擷取<span class=”zzzz”>……<span>中的樣式表“zzzz”並存入數組,通過對數組的遍曆,判斷核心資料區塊內在數字(串)是該刪除還是保留,最終產生出真實的資料。程式中充分發揮了PHPRegex和字串處理函數的極大威力,使其比ASP等指令碼代碼短得多。
在Alexa查詢結果中還有其他大量的資料可能也用得到,也可以通過本編程思想來設計和編寫程式碼。比如抽取訪問某網站的全球使用者的比例,一周內的平均值,可以參考以下部位的代碼:
Percent of global Internet users who visit this site
<td><!--Did you know? Alexa offers this data programmatically.
Visit http://aws.amazon.com/awis for more information about the Alexa Web Information Service.-->
<span class="cded">10</span>0<span class="cd83">.0</span>00<span class="cd81">18</span>%
</td>
方法還是尋找段落特徵,取出<span>部分的精細資訊,再進行深入分析得到資料。
用該文的方法也可以編寫網頁資訊採集程式,包括新聞、部落格等等互連網上的大量文章。
但是,如果Alexa下次再修改演算法,該方法就會失效了。
附件:
Index.php檔案:
<form name="alexaform" method="post" action="get_alexa.php">
輸入網址:<input type="text" name="url" value="http://www.why100000.com" size=40>
<input type="submit" value="查 詢">
</form>
get_alexa.php檔案:
<?php
//PHP版本要求:PHP 4.4.7
//支援原創,請保留該處注釋:
//作者:張慶(網眼)陝西-西安
//網站地址:
//電腦學習網:http://www.why100000.com
//《部落格》:http://blog.why100000.com
//《問吧!》http://ask.why100000.com
//示範地址:http://www.why100000.com/test/alexa/alexa.php
$url = $_POST['url'];
if($url!="")
{
echo "您的網站 ". $url ." 在 ALEXA 的排名為:<br>";
$rank = getAlexaRank($url);
echo '[' . $rank .']';
}
function getAlexaRank($weburl)
{
$weburl = strtolower($weburl);
$tempurl = getDomainUrl($weburl);
//讀取http://client.alexa.com/common/css/scramble.css中的資料
$strAlexaCss = file_get_contents('http://client.alexa.com/common/css/scramble.css');
$alexaRankQueryUrl = 'http://www.alexa.com/data/details/traffic_details/' . $tempurl;
$strAlexaContent = file_get_contents($alexaRankQueryUrl);
$rankContent = getBody($strAlexaContent, 'Information Service.-->', '<!-- google_ad_section_end(name=default) -->');
echo '<xmp>';
echo $rankContent;
echo '</xmp>';
$arrSpanClass = getArray($rankContent, '<span class="', '">');
echo '<xmp>';
print_r($arrSpanClass);
echo '</xmp>';
foreach($arrSpanClass as $css)
{
//global $rankContent;
if (strpos($strAlexaCss, '.' . $css)>0)
{
echo $css .'(h)';
$rankContent = ScriptHtml($rankContent, "span", 2, $css);
}
else
{
echo $css .'(s)';
$rankContent = ScriptHtml($rankContent, "span", 1, $css);
}
echo '<xmp>';
echo $rankContent;
echo '</xmp>';
}
$rankContent = str_replace('</span>', '', $rankContent);
$rankContent = str_replace(',', '', $rankContent);
return $rankContent;
}
function getBody($ContentStr, $StartStr, $EndStr)
{
$ContentStr = strtolower($ContentStr);
$StartStr = strtolower($StartStr);
$EndStr = strtolower($EndStr);
$StartPos = strpos($ContentStr, $StartStr);
$EndPos = strpos($ContentStr, $EndStr);
return substr($ContentStr, $StartPos+strlen($StartStr), $EndPos-$StartPos-strlen($StartStr));
}
//由於代碼太長,以下代碼省略,需要的發郵件到 zhangking2008@gmail.com 索取
//請在郵件標題註明“索取Alexa排名PHP代碼”
//……
?>
張慶(網眼)
網眼視界:http://blog.why100000.com
2008-6-9