让你的Ajax应用被Google抓取

今天一大早看到消息说Google可以抓取Ajax应用的内容了,并且Google也给出了使你的Ajax站可被抓取要做的一些事情。如果你看得懂英文就跳过下面的翻译,直接访问:http://code.google.com/intl/zh-CN/web/ajaxcrawling/docs/getting-started.html

Google爬虫怎么爬我就不管了,要让你的Ajax站可被Google爬虫抓取,你需要做以下几个事情:

1,告诉爬虫你的站点支持Ajax内容的抓取

如果你的URL里有带“#”(所谓的锚点或URL hash),那么这个hash段必须以感叹号(“!”)开头。例如你的URL是这样的:

www.example.com/ajax.html#mystate
那么现在你要将它改成:
www.example.com/ajax.html#!mystate
这样,只要你的站点支持HTML快照,那么这样的URL就是可Ajax抓取的。

2,设置您的服务器来处理包含的_escaped_fragment_的URL请求

假设您想让www.example.com/index.html#!mystate被收录。那么你要给爬虫提供一份这个URL地址的HTML快照,使爬虫能够看到内容。服务器怎么知道什么时候返回HTML快照而不是常规的页面呢?答案是:爬虫在请求URL的时候,会将URL:www.example.com/ajax.html#!mystate临时改成www.example.com/ajax.html?_escaped_fragment_=mystate

你也许想知道为什么要这么做。有两个重要原因:

  • hash段不会作为http请求的一部分被发送到服务器。换句话说,爬虫要通过某种方式告诉你的服务器请求的是www.example.com/ajax.html#!mystate的内容而不是www.example.com/ajax.html
  • 另一方面,你的服务器要知道该返回的是HTML快照,而不是普通的页面。记住:所谓的HTML快照就是在JavaScript执行后所有显示在页面上的内容。最终服务器要返回给爬虫的是www.example.com/ajax.html#!mystate的内容

注意:在转换过程中,爬虫会将hash段中的某些字符编码。要检索原来的(hash)片段,你需要对所有的%XX 字符进行解码。(如,%26变成&,%20变成空格,%23变成#,%25变成%等等)

现在你有了原始地址并且知道爬虫需要请求什么内容了,你需要生成一份HTML快照。要怎么做呢?下面是几种方法:

  • 如果很大一部分内容都是通过JavaScript生成的,你可以使用无head浏览器如 HtmlUnit来得到HTML快照,或者你可以用其他的如crawljax和watij.com
  • 如果很多内容是通过服务器端技术(php或asp.net)生成的,你可以用现成的代码代替JavaScript生成静态页面
  • 现在的做法是创建一些离线的静态页面。例如,很多应用都是从数据库读取内容到浏览器渲染。其实你可以为每个Ajax URL创建单独的页面

最好是把各种HTML快照机制都尝试一遍。确保无head浏览器能正确的你的应用的内容是很重要的。当然你肯定想知道爬虫看到了什么,是么?你可以写一个小的测试程序来看输出的内容,或者你可以用工具来看,如: Fetch as Googlebot

注意:在测试的时候要使用_escaped_fragment_语法,因为这是Googlebot真正向服务器请求的地址。也就是说,使用www.example.com/ajax.html?_escaped_fragment_=mystate而不是www.example.com/ajax.html#!mystate

总结一下,你需要让服务器做下面的事情:

  • www.example.com/ajax.html?_escaped_fragment_=mystate的请求映射为www.example.com/ajax.html#!mystate
  • (hash)令牌是解码的。最简单的方法是使用标准的URL解码。在java程序中你可以这样写:
    mydecodedfragment = URLDecoder.decode(myencodedfragment, “UTF-8″);
  • 返回一个HTML快照,最好在页面上有页面的链接,_escaped_fragment_ URL对终端用户为不可用(记住:_escaped_fragment_ URL只对爬虫有用)。所有的请求都不要包含_escaped_fragment_,不然服务器会返回前面说的那些内容。

3,没有hash段的页面处理

有的页面是没有hash段的。比如你的首页是www.example.com而不是www.example.com#!home。对于这样的页面,我们有另一个处理方式。那就是在你页面HTML的head里加入
<meta name=”fragment” content=”!”>
如果你在www.example.com页面加入了<meta name=”fragment” content=”!”>,爬虫会临时地将页面映射为www.example.com?_escaped_fragment_= 并向服务器发出请求。服务器就相应的返回www.example.com的HTML快照。请注意,使用这个标签有一个重要的限制:唯一有效的内容是“!”,换句话说,这个meta标签只有这个格式:<meta name=”fragment” content=”!”>,表示一个无hash段的,通过Ajax获取内容的页面。

4,更新您的Sitemap加入新的Ajax URL地址

爬虫使用Sitemap来发现可抓取的内容。您的Sitemap包含了您要显示在搜索结果中的URL,通常是http://example.com/ajax.html#!foo=123,不要在你的Sitemap中包含http://example.com/ajax.html?_escaped_fragment_=foo=123 这样的地址。Googlebot 不会抓取包含_escaped_fragment_的页面。入口地址(没有包含hash段的地址,如:http://example.com/)的写法按正常写法,页面的处理参考第三点。

5,可选的,却很重要的,测试你的应用的可抓取性:用“Fetch as Googlebot”查看爬虫看到的内容

Google提供了一个可以查看爬虫看到的内容的工具:“Fetch as Googlebot”。你可以用这个工具来查看你的操作是否正确并确认Googlebot是否能抓取到你想让用户看到的内容。记得在测试的时候使用包含_escaped_fragment_的URL,因为这是Googlebot真正请求的地址。

跨浏览器剪切板访问解决方案

在改善”小可”(http://code.princeb4d.com/)的使用体验的过程中遇到一个问题,那就是需要通过JavaScript将Ajax返回得到的URL复制到剪切板,我第一想到的就是下面的代码

?View Code JAVASCRIPT
1
window.clipboardData.setData('text', sometext);

很不幸的是,这句是IE ONLY的,于是需要兼容.后来我找到一段代码,我以为除了IE外,其他的都可以使用.

?View Code JAVASCRIPT
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
32
33
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var clip = Components.classes['@mozilla.org/widget/clipboard;1']
		 .createInstance(Components.interfaces.nsIClipboard);
if (!clip) return;
 
var trans = Components.classes['@mozilla.org/widget/transferable;1']
		  .createInstance(Components.interfaces.nsITransferable);
if (!trans) return;
 
trans.addDataFlavor('text/unicode');
 
var str = new Object();
var len = new Object();
 
var str = Components.classes["@mozilla.org/supports-string;1"]
		.createInstance(Components.interfaces.nsISupportsString);
 
var copytext=meintext;
 
str.data=copytext;
 
trans.setTransferData("text/unicode",str,copytext.length*2);
 
var clipid=Components.interfaces.nsIClipboard;
 
if (!clip) return false;
 
clip.setData(trans,null,clipid.kGlobalClipboard);
 
}
} catch (e) {alert('因为安全策略的原因,此项功能已被您的浏览器禁止。关闭此窗口,然后按下“Ctrl+C”组合键完成复制。');		codeobj.focus();
}
codeobj.select();

依然不幸的是这样只是兼容Firefox的,而WebKit内核的Chrome和Safari同样是无效的,而且兼容Firefox的前提还是需要用户到about:config中手动修改默认安全设置,这个方法是不可取的,除了安全性问题外,用户体验极差!

最终找到的方案是:使用JavaScript配合flash实现.

实现方式:
通过JavaScript动态为使用”剪切板”功能的按钮或其他元素添加并覆盖一层透明的flash,当用户点击时把相应的内容复制到剪切板中,从而实现跨浏览器应用.
优点:
  1. 现在的浏览器都支持flash,它为我们省去了跨浏览器兼容性问题
  2. 在目前浏览器对JavaScript限制越来越严格的现状中,浏览器对action script的限制相对宽松(本文就剪切板操作而言),这也是我们可以利用的地方.
  3. 有现成的开源项目可以使用并做了很好的封装,使用非常容易.
缺点:
  1. 需要额外的加载flash资源和使用更多的JavaScript代码.
  2. 这个不算缺点,但很现实!在开发者没有上述开源项目资源,又对action script不熟悉的情况下,需要一定的学习和开发成本.
使用时需要注意什么?
  1. 由于flash是通过JavaScript动态加载进来的,因此使用Demo需要服务器环境.
  2. 除非将该项目的JavaScript文件和swf文件放置在页面所在目录,否则即使该JavaScript文件和swf文件在同一文件夹下,也同样需要在初始化时通过ZeroClipboard.setMoviePath( ‘某文件夹/ZeroClipboard.swf’ );来指定flash路径,该JavaScript使用的是相对页面路径来加载flash,由于一开始没注意到这点,让我困惑了很久.

下面是我写的初始化代码,提供参考,更多的使用方式请参考后面给出的项目地址所给出的内容.

?View Code JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
function clipboardini(copyobj){
	ZeroClipboard.setMoviePath( 'scripts/ZeroClipboard.swf' );
	clip = new ZeroClipboard.Client();
 
	clip.setHandCursor( true );
	clip.addEventListener('mousedown', copycode);
	clip.addEventListener('complete', function(){popup('popup','URL已经复制到剪切板了,赶快去分享吧!');});
	clip.glue( copyobj );
}
function copycode(){
	clip.setText( 'http://code.princeb4d.com/#'+document.getElementsByTagName('em')[0].innerHTML );
}

该开源项目地址:http://code.google.com/p/zeroclipboard/
项目DEMO:http://bowser.macminicolo.net/~jhuckaby/zeroclipboard/

“小可”,丸子的在线代码分享编辑器

中午突然想写这么一个在线代码编辑器,主要是为了大家讨论问题的时候方便分享代码,虽然已经不是什么新鲜的东西了,但还是值得一做.于是花了一个下午时间,学习了下Edit Area文档并写了这么一个页面.功能还不完善,我会继续维护加强的.取了个可爱的名字:小可.O(∩_∩)O~

前往围观=>http://code.princeb4d.com/

欢迎大家给我反馈意见,我也有自己的一些想法了.

下一步要做的是:
  1. 支持插入各种流行的JavaScript library或framework.[一直在进行]
  2. 优化细节,改善使用体验[一直在进行]
  3. 加入zen coding插件[已支持]

看看视频,先睹为快!

ZD YouTube FLV Player

为能清晰地看到演示过程,请点击视频播放器右下角的全屏查看按钮进行观看

前端博客搜索


想写这个插件也是纯属偶然,话说2010年1月12日,百度突然不能访问,页面上显示被伊朗网络军队入侵,具体是啥原因咱也不研究了,平常我是用百度做首页的,虽说我更喜欢Google,但基本上百度的一些东西符合我的使用习惯,既然它挂了,我也就奔着Google去了,遂将首页改为Google.想不到第二天[2010年1月13日]Google要退出中国市场的消息又搞得沸沸扬扬.不管这些了,关键是某MM她习惯用百度搜索一些东西,包括百科,MP3等,于是我便到IE8 addons里去翻,看到分类里有个地图,我就想啊,貌似地图搜索也经常用到,也顺便给加一个吧,可惜了,没有我平常用的某地图的addon,于是我想自己开发一个,接着我又想,平常经常翻读各位前端大牛的博客,有时候一个知识点忘记了在哪里看到的,又急着要找,又要去翻,甚是麻烦,于是乎这个”前端博客搜索”就这样诞生了.
大家先来看看吧:前端搜索

如果你有收藏觉得不错的前端博客或者你觉得自己也想加入的话,欢迎联系我哦!

在Firefox下添加这个插件后,你可以直接在浏览器的搜索栏搜索,也可以选中文本,在右键快捷菜单中搜索.

在Google chrome中你可以方便的使用该扩展

而如果你要在ie8中使用右键搜索,你可以添加ie8加速器后便可使用.

短地址还原(开放API)

一个下午无聊的”杰作”.之前Leeiio同学的博客上写过这样的一篇文章RealURL,还原你的短地址!,我之前跟Leeiio讨论过,他的思路给了我启发,今天下午突然想练练php,于是有了这东西.简单测试了几个短地址,全部通过.本人并不擅长php,难免写得有缺陷,如果你有发现失败的记得给我留言哦!测试地址:http://www.princeb4d.com/demo/realurl.php

如你所见,只是一个简单的查询界面.听肉饼说他要做个Bookmarklet刚好可以用到,于是我就把API放出来了.

API使用方式
API支持POST和GET两种方式.
调用时使用参数type=api和surl=短地址url.
例如,使用GET方式:http://www.princeb4d.com/demo/realurl.php?type=api&surl=http://ff.im/-dLCds.
使用POST方式:直接将http://ff.im/-dLCds以POST方式提交给http://www.princeb4d.com/demo/realurl.php?type=api.
返回结果:短地址还原后的实际url.

在放出API后,我在考虑如果有很多url要处理,请求数会不会太多了,嗯,期待下个版本增强吧!解决一次请求处理多个URL的情况.

Change Log

  • realurl v1.0
    1. 实现短地址还原查询,并提供调用API
    2. 默认超时时间为10秒,如果请求超时则返回原地址.
    3. 尚未实现多个短地址还原,将在下个版本提供.
    4. 尚未实现类似ow.ly这类无跳转短地址还原,将在下个版本提供.

JavaScript动画效果数学模型整理

数学模型之圆周运动

提示:你可以先修改部分代码再运行。

数学模型之反弹

提示:你可以先修改部分代码再运行。

IE下,写innerHTML出现未知的运行时错误

前天把之前写的Ajax代码稍微的修改下,结果在ie下竟然报错了,说是:”未知的运行时错误”.我晕,但是在Firefox下的表现是正常的.我只是修改了下几个标签的内容,照理说不会有问题的,而且原来也是一直很正常的,这让我感到很困惑.于是,我在网上搜寻了一番,网上的看到的解释是:

这主要是IE在对innerHTML进行写操作的时候会检查element是否具备做为这些内容中html对象容器的要求,比如如果你是在一个p里加入包含<li>的html代码,这是会出错的。所以如果你发现这些错误出现,尝试做两件事情:

1、检查你尝试加到innerHTML的html代码是否包含破损的html标签,如没有<ul></ul>包围的<li></li>.

2、把你的容器改成那些比较“包容”的标签,如<span></span>,<div></div>

特别注意的是<tbody>,<table>,<tr>标签里面加正确格式的<td>标签都有可能出错,所以应该避免使用这些标签作为容器,必要时可以用<td>作为容器,再嵌套一层表格。

这段描述让我想起我刚才改了的标签,我把原来的容器由div改成了p,而返回的内容里是3个ul列表,虽然标签是闭合的,但p不能包含ul列表!因为在嵌套规则里,p应该是包含inline元素的,而ul是block元素,所以才出现了前面说的那个错误,于是我把标签改了回去,果然又正常了.

最后附上:正确的嵌套规则图

php怪异编码问题

问题是这样产生的:
我想写一个php来遍历当前php所在文件夹下的文件及目录,因此写了下面的代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>丸子的Demonstration</title>
</head>
 
<body>
<?php 
function lsfile(){
$files=glob('*');//scandir(dirname(__FILE__));
foreach($files as $val)
if($val!="." and $val!=".." and $val!="index.php")
echo "<li><a href=\"".urlencode($val)."\">".$val."</a></li>";
}
?>
<ol id="demo">
<?php
lsfile();
?>
</ol>
</body>
</html>

可是事实并没有我想的这么简单,$val输出的中文是乱码!于是,我按照常规的编码检查方式查了一遍,发现文件编码是utf-8,meta设置的是utf-8,浏览器使用的还是utf-8,编码都是一致的,而且如果是直接echo中文的话,并不会是乱码,那么问题出在哪里呢?跟几个朋友讨论后未果.Along说把文件保存成ANSI,再保存成utf-8,失败!文件发给netseye测试,正常显示中文!难道是我本地服务器的问题?或者是php函数读取中文时的问题?于是,我上传到我的php空间访问,乱码!于是,我纠结了!但是,我有一个发现,如果浏览器编码改成gb2312,中文就正常显示了!这是为什么呢?知道了这个以后,我有了临时的解决方案:将中文输出前先转换编码,转换方式为iconv(“gb2312″,”utf-8″,$val);完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>丸子的Demonstration</title>
</head>
 
<body>
<?php 
function lsfile(){
$files=glob('*');//scandir(dirname(__FILE__));
foreach($files as $val)
if($val!="." and $val!=".." and $val!="index.php")
echo "<li><a href=\"".urlencode($val)."\">".iconv("gb2312","utf-8",$val)."</a></li>";
}
?>
<ol id="demo">
<?php
lsfile();
?>
</ol>
</body>
</html>

至于原因,定位在php函数的读取上,Leeiio给了我一个重要的提示,让我恍然大悟:不同的文件系统对文件名或文件夹名的编码是不一样的,我想这就是为什么同一个文件在不同的电脑里测试结果不一样的原因了.于是我想从php文件流编码着手,看看是否能够通过函数设置全局编码而不是每次都转换.继续跟朋友讨论研究.希望能得到一个满意的结果.

JS图片加载与获取远程图片大小

哎,过了个国庆,我那个堕落呀…博客都长草了…其实还有一个主要原因是我的本本屏幕出问题了,没心情对着它,昨天去换了个屏幕,感觉那怎么就不是我的本本了…

之前有看到群里有人问用js实现图片加载的,原来死去的blog上是有Demo的,挂了!后来又有人问了用js获取远程图片大小,就顺便写一下好了一起放上来,看Demo.

提示:你可以先修改部分代码再运行。

当然,你可以试试直接用imgElem.onload来加载看看.

javascript类的实现及原理的理解

一,概述

类,是面向对象编程中一个很重要的概念.一直很遗憾JavaScript中没有实质的类的定义,但这并不会影响我们在JavaScript实现类的操作.在学习JavaScript的过程中,我也了解了JavaScript中对一些高级语言中的类的模拟实现方式,实现的形式不一,但是不管是怎样的形式,它们实质上都是用构造函数模拟类,如

var aClass=function(){};

var anInst=new aClass();

虽然知道如此,但是对于这些实现的方式,还是很困惑,原因在于,它们的区别是什么?都有什么各自的优缺点?能否真正实现类的模拟?是否有更好的方式来实现?这些问题一直困扰着我,也是本文中要思考的问题.

More >