可能是因为存储成本的降低吧,现代搜引擎对数据的收集都是贪婪的。原来Google在首页上放了一个页面索引数量,记得最高已达到了8个多billion的页面数。另外搜索引擎记录用户搜索的关键字已不是什么秘密,但是大多搜索引擎还记录了用户点选命中。

    用户点选命中这个概念是包含于搜索引擎命中这个范畴中的。搜索引擎的命中,就是你敲入关键字后,搜索引擎返回的所有结果。搜索引擎有效命中,是指找出与搜索关键字含义相匹配的网页。而用户点击命中,是指用户在搜索命中的结果中,发现了自己需要的结果,然后点击的网页链接。

    搜索引擎命中、有效命中和用户点选命中之间的关系如下图:
    SearchResult.png
    绿色的Matched Pages是理想的和关键字完全相关的页面。金黄色的Result Pages就是搜索返回的结果集,它与Matched Pages交集的多少,决定了搜索引擎的质量。蓝色的Clicked Pages就是用户点选命中,它是一个很特殊的区域。首先它一定是被限制在金黄色的Result Pages中,然后由于这个点击是每位搜索用户根据网页标题以及摘要判断后进行的,它将最大限度的包含在Matched Pages中,当然少量判断错误的情况也很自然,所以Clicked Pages又不会完全在Matched Pages中。

    当搜索引擎记录了Click Pages后,它就可以根据这个记录,调整Result Pages集和该集内条目的排序,把总是不被Click的页面逐渐移出该集合,而将更多将可能的Matched Pages命中移入。即图中A-->B的变化过程,当然理想的情况是Result Pages就是Matched Pages啦,并将Clicked多的结果条目尽可能往前排。目前的主流搜索引擎:Google、Baidu、Yahoo和微软Live,其中除了baidu外,其它三种引擎都可能收集了Clicked Page,不过他们在实现技术上又有所差别。

    Google收集方案:简单、优雅
    Google-ABIS.png
    // 最初好像这里是指向的google的页面,然后再重定向到目的页面

    当然这里的link,Google对其进行了处理:<class=l href="http://bi.u-soft.com.cn/Products/ai2005/" target=_blank onmousedown="return clk(0,'','','res','1','')">极光商智2005概览- 宏信极光商智</a>。简单的加上了一个onmousedown事件,clk函数定义如下:
window.clk=function(b,c,d,h,i,j)
{
    
if(document.images)
    
{
        
var a=window.encodeURIComponent?encodeURIComponent:escape,e="",f="",g="";
        
if(b){e="&url="+a(b.replace(/#.*/,"")).replace(/\+/g,"%2B")}
        
if(c){f="&oi="+a(c)}
        
if(d){g="&cad="+a(d)}
        (
new Image).src="/url?sa=T"+f+g+"&ct="+a(h)+"&cd="+a(i)+e+"&ei=hhp7RfHwGpnIwQKr-qiTCA"+j
    }

    
return true
}

    // HTML元素中,除了Image可以直接new外,还有Option对象也可以直接new,不过我也不清楚为什么要这么设计

    代码混淆过,相当不可读,不过这不是我们关心的重点。这里可以看到Google巧妙的使用了Image对象会预先载入图片的技巧,实现了Clicked Page的回报。这里图片的实际地址是:/url?sa=T&ct=res&cd=1&ei=hhp7RfHwGpnIwQKr-qiTCA,其中的ei=hhp7RfHwGpnIwQKr-qiTCA,应该是连接标识。对该图片访问后的返回结果,Google是做了最佳优化处理的,该请求返回的http响应是204,即:

HTTP/1.1 204 No Content
Cache-Control: private
Content-Type: text/html
Server: GWS/2.1
Content-Length: 0
Date: Sat, 09 Dec 2006 20:41:45 GMT

    // 关于http协议204的解释请参看RFC2616

    微软Live收集方案:简单、臃肿
    Live-BI-ABIS.png

    怎么简单又臃肿呢,这不是自相矛盾吗?简单是说Live也是用Image对象相来报告Chicked Page的,而臃肿是说微软的这个Live的页面。微软也是简单的对link进行了处理:<href="http://yyq123.wordpress.com/2006/04/" gping="/GLinkPing.aspx?/_1_9SE/1? http://yyq123.wordpress.com/2006/04/ &amp; &amp; DI=6244&amp; IG=e982db6143c048488736e2f890c94aa9&amp; POS=1&amp; CM=WPU&amp; CE=1&amp; CS=AWP&amp; SR=1&amp; sample=0">2006 April « 语虚</a>。似乎是只加了一个gping的自定义属性,但接下来的事情就会让你晕倒死。。。

    完成GLinkPing(名字取得不错,但怎么感觉是尽整没用的呢)功能的脚本代码为:
  

GTracking.GLink = function()
{
    
var TIMEOUT = 3 * 1000;
    
var _timer = null;
    
var _img = null;
    
var _bIsIE = false;
    
var _clickedAnchor = null;
    
var _mouseCapturedElement = null;

    bind_method(document.body, 
"mousedown", onMouseDown, false);
    bind_method(document.body, 
"mouseup", onMouseUp, false);
    bind_method(window, 
"unload", onUnload, false);

    
if(document.body.addEventListener)
    
{
        _bIsIE 
= false;
    }
// document.body.attachEvent is added by atlascompat.js and will confuse this code if loaded.
    else //if(document.body.attachEvent)
    {
        _bIsIE 
= true;
    }


    
function onMouseDown(theEvent)
    
{
        
if(_bIsIE)
            _mouseCapturedElement 
= window.event.srcElement;
        
else
            _mouseCapturedElement 
= theEvent.target;
    }

    
    
function onMouseUp(theEvent)
    
{
        
var evt;
        
var src;

        
if(_bIsIE)
        
{
            evt 
= window.event;
            src 
= evt.srcElement;
        }

        
else
        
{
            evt 
= theEvent;
            src 
= evt.target;            
        }


        
if(_mouseCapturedElement != src || evt.button == 2)
        
{
            _mouseCapturedElement 
= null;
            
return;
        }


        src 
= getPingElement(src);
        
if(src == null)
        
{
            
return;
        }


        
if(AllowParallelNavigation(evt, src))
        
{
            _img 
= new Image();
            _img.src 
= src.attributes["gping"].value;
            
return true;
        }

        
        _clickedAnchor 
= src;

        
if(_bIsIE)
        
{
            evt.returnValue 
= false;
        }

        
else
        
{
            evt.preventDefault();    
        }

        doPing(src.attributes[
"gping"].value);

        
return false;
    }


    
function AllowParallelNavigation(evt, src)
    
{
        
var retVal = false;
        
if(src.attributes["target"&& src.attributes["target"].nodeValue == "_blank")
            retVal 
= true;
        
        
// new window
        if(evt.shiftKey == true)
            retVal 
= true;
        
        
// new tab
        if(evt.ctrlKey == true)
            retVal 
= true;
            
        
//middle mouse button
        if(evt.button == 4)
            retVal 
= true;

        
return retVal;
    }


    
function getPingElement(src)
    
{
        
var retVal = src;
        
var bHasPing = hasPing(src);

        
while( bHasPing == false )
        
{
            src 
= src.parentNode;
            
if(src == null)
            
{
                retVal 
= src;
                
break;
            }


            bHasPing 
= hasPing(src);

            
if(bHasPing)
            
{
                retVal 
= src;
                
break;
            }

        }

        
return retVal;
    }


    
// FF was not behaving properly in the terse version of this check
    function hasPing(node)
    
{
        
var retVal = false;
        
        
if(node.attributes)
        
{
            
if(node.attributes["gping"])
                retVal 
= true;
        }


        
return retVal;
    }

    
    
    
function doPing(payload)
    
{
        _img 
= new Image();
        
        bind_method(_img, 
"load", onPingComplete, false);
        _img.src 
= payload;
       _timer 
= setTimeout(onPingComplete, TIMEOUT);
    }


    
function onPingComplete()
    
{
        _img 
= null;

        resetTimeout();
        doNav();
    }


    
function resetTimeout()
    
{
        
if(_timer)
        
{
            clearTimeout(_timer);
            _timer 
= null;
        }

    }


    
function doNav()
    
{
        
if(_clickedAnchor)
        
{
            
if( _bIsIE )
            
{
                _clickedAnchor.click();
            }

            
else
            
{
                
var clicker = document.createEvent("MouseEvents");
                clicker.initEvent(
"click"truetrue);
                _clickedAnchor.dispatchEvent(clicker);
            }

        }

    }


    
function onUnload()
    
{    
        unbind_method(document.body, 
"mouseDown", onMouseDown, false);
        unbind_method(document.body, 
"mouseup", onMouseUp, false);
        unbind_method(window, 
"unload", onUnload, false);
    }

}

    // 神啊,救救我吧!Google就用了10来行代码,还做了浏览器兼容

    算了,这事儿就当微软的程序员写代码效率高,一袋烟的功夫就是近200行。Live中Image访问的地址是:/GLinkPing.aspx?/_1_9SE/1?http://yyq123.wordpress.com/2006/04/&& DI=6244& IG=31498446f2a144f6993cd513bf80c39a& POS=1& CM=WPU& CE=1& CS=AWP& SR=1& sample=0,其中那个IG应该是个GUID,由于Live还回传了该被点击页面的URL(http://yyq123.wordpress.com/2006/04/),所以那个IG可能是关键字标识。最后看看这个Image的返回结果,没有想到微软还真的把这个请求当了回事儿,给浏览器返回了一个gif图片@_@:

HTTP/1.1 200 OK
Content
-Length: 42
Content
-Type: image/gif
X
-Powered-By: ASP.NET
P3P: CP
="NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND", policyref="http://privacy.msn.com/w3c/p3p.xml"
X
-TraceID: 056743472244460c8331da7eeea643bd
Expires: Sat, 
09 Dec 2006 21:17:51 GMT
Cache
-Control: max-age=0, no-cache, no-store
Pragma: no
-cache
Date: Sat, 
09 Dec 2006 21:17:51 GMT
Connection: keep
-alive

GIF89a

    // 也就42个字节的gif图片,要是一直缓存在内存中可能也没啥吧