一月 5th, 2010

jQuery选择器

No Comments, 前端开发, by admin.

$的选择器部分:
凡是运用$,其返回值是一个object
$
选择器主要用于选择标签.基本用法是同css的选择器.但是,很让人兴奋的是,他支持常见的浏览器,css中很多选择器是IE6所不支持的.
1.
基本选择器(3):
$(“
标签名“),$(“p”)是选取了所有的p标签节点
$(“#id
“),$(“#test”)是选取了idtest的标签节点
$(“.class
“),$(“.test”)是选取了所有classtest的标签节点
上面的$(“标签名“)$(“.class“)返回的都是所有满足的节点,至于进一步筛选可以添加一些函数,eq,gt,lt等等.
2.
组选择器:
下面还是现做一个约定:标签名或#id名或.class记作mix,mix表示一个标签名,或一个#id或一个.class.
$(“mix,mix,mix,…”),
:$(“div,#test1,p,.test2,#test3″)


3.
后代选择器:
$(“mix mix”),
当然可以是多个嵌套,但后代选择器可以是深层子代,所以$(“mix mix mix …”)这种写法作用不大.例子:$(“div .test”):div标签内的所有具有testclass的后代元素(就是被div嵌套的class属性为test的标签)
可以见DEMO


4.
子选择器:
$(“mix>mix”),
这个放在后代选择器后面是为了和它做对比.子选择器只能选择第一代子代.不处理深层嵌套.例子:
$(“div>.test”)
<div><p class=”test”></p></div>
对这里的p段落标签有效.但对
<div><p><p class=”test”></p></p></div>
对这里的p段落标签无效,这里要用
$(“div .test)

5.临近选择器:
$(“mix+mix”),
选取下一个兄弟节点.:$(“div +#test”),idtest的的节点必须是div的下一个兄弟节点.
<div></div><p id=”test”></p>
$(“div + #test”)中能取到p段落节点
<div></div><p></p><p id=”test”></p>
则不能取到

6.属性选择器:
把属性选择器不放在css选择器里面是因为jQuery中写法是不一样的.至于css中写法可以参考我之前写的一篇css的选择器一文.jQuery中是和xPath类似的写法:
$(“mix[@attr]“):
选取所有该mix且具有attr属性的节点
$(“mix[@attr=a_value"]):
选取所有该mix且具有attr属性并满足属性值为a_value的节点
$(“mix[@attr^=a_value_head"]):attr
属性的属性值是以a_value_head开头的
$(“mix[@attr$=a_value_end"]):attr
属性的属性值是以a_value_end结尾的
$(“mix[@attr*=a_value"]):attr
属性的属性值中包含a_value

7.进一步选择器:
这个名称是我自己起的,其实选择器组合都有进一步的意思,你明白后面所介绍的知识即可.
具有限定子节点选择器:$(“mix1[mix2]“):返回包含mix2mix1节点.:$(“div[a]“):包含a标签的div.
这个和$(“div a”)不相同.后者表示div中的a标签,返回的是a标签对象,前者返回的是div标签对象
冒号限定结点选择器:$(“mix:condition”):mix标签,并且满足限定条件.
E:root:
类型为E,并且是文档的根元素
E:nth-child(n):
是其父元素的第n个类型为E的子元素 ,基数从1开始
E:first-child:
是其父元素的第1个类型为E的子元素
E:last-child:
是其父元素的最后一个类型为E的子元素
E:only-child:
且是其父元素的唯一一个类型为E的子元素
E:empty:
没有子元素(包括text节点)的类型为E的元素
E:enabled
E:disabled:
类型为E,允许或被禁止的用户界面元素
E:checked:
类型为E,处于选中状态的用户界面元素(例如单选按钮或复选框)
E:visible:
选择所有可见元素(display值为blockvisible,visibility值为visible元素,不包括hide)
E:hidden:
选择所有隐藏元素(Hide,display值为blockvisible,visibility值为visible的元素)
E:not(s):
类型为E,不匹配选择器s
E:eq(n),E:gt(n),E:lt(n):
元素限定
E:first:
相当于E:eq(0)
E:last:
最后一个匹配的元素
E:even:
从匹配的元素集中取序数为偶数的元素
E:odd:
从匹配的元素集中取序数为奇数的元素
E:parent:
选择包含子元素(包含text节点)的所有元素
E:contains(‘test’):
选择所有含有指定文本的元素
表单选择器:
E:input:
选择表单元素(input,select,textarea,button)
E:text:
选择所有文本域(type=”text”)
E:password:
选择所有密码域(type=”password”)
E:radio:
选择所有单选按钮(type=”radio”)
E:checkbox:
选择所有复选框(type=”checkbox”)
E:submit:
选择所有提交按钮(type=”submit”)
E:image:
选择所有图像域 (type=”image”)
E:reset:
选择所有清除域(type=”reset”)
E:button:
选择所有按钮(type=”button”)
当然包括E:hidden

8.xPath路径查询:

先介绍下xPath的语法:
/:
选取根节点
//:
选取文档中所有符合条件的节点,不管该节点位于何处
.:
选取当前节点
..:
选取单前节点的父节点
@:
选取属性,这个在之前说过了(属性选择器)
nodename:
选取节点下的所有节点
jQuery
中的应用:
根节点是很少用到的,常用的如下面的例子:
$(“div/p”)
相当于$(“div>p”)
$(“div//p”)
相当于$(“div p”)
$(“//div/../p”):
所有div节点的父节点下的p标签
还有相对路径的写法以及支持的Axis选择器,还不是会应用,不介绍了已经一大堆了

$的其他用法:

$(html节点):根据提供的原始HTML标记字符串,动态创建由jQuery对象包装的DOM元素.:
$(“<div><p>Hello</p></div>”).appendTo(“#body”);//
<div><p>Hello</p></div>添加到body元素中
$(document):
网页文档对象
$(document.body):
网页body对象,$(“body”)是一样的
$(
函数):DOM载入后就执行该函数.所以$(document).ready()可以写做$()
$(
选择器部分,选择器来源):这个举例说明
$(“input:radio”,document.forms[0]):
在文档的第一个表单中,搜索所有单选按钮
$(“div”,xml.responseXML):
查询指定XML文档中的所有div元素
选择器来源可以是:作为上下文的DOM元素,文档或jQuery对象
还有两个:$.extend(prop)$.noConflict()是和插件以及和其他库兼容的使用,以后再写

jQuerycore部分还有:

eq(数字):将匹配的元素集合缩减为一个元素。这个元素在匹配元素集合中的位置变为0,而集合长度变成1
gt(
数字):将匹配的元素集合缩减为给定位置之后的所有元素
lt(
数字):将匹配的元素集合缩减为给定位置之前的所有元素
上面三个的例子:
$(“div:eq(1)”)//
2div
$(“div:gt(2)”)//
3div以及之后的div
$(“div:lt(2)”)//
2div以及之前的div,即第1div和第2div

lengthsize():当前匹配的元素数量

each():以每一个匹配的元素作为上下文来执行一个函数。这意味着,每次执行传递进来的函数时,函数中的this关键字都指向一个不同的元素(每次都是一个不同的匹配元素).而且,在每次执行函数时,都会给函数传递一个表示作为执行环境的元素在匹配的元素集合中所处位置的数字值作为参数.
$(“img”).each(function(i){ this.src = “test” + i + “.jpg”; });//
迭代图像,并设置它们的src属性

get():如果没有参数,返回所有,是一个对象数组;如果带参数,必须是数字,基数从0开始.例子:
$(“div”).get():
返回一个div对象数组
$(“div”).get(1):
返回第二个div对象
index(
需求的元素节点对象):返回数字.用个例子说明:
$(“div”).index($(“.test”))[1] //
表示从所有div节点中查找class属性为test的节点.并且找的是第二个节点(基数从0开始).返回值是该节点在div节点中的位置(基数也是从0开始).

一月 5th, 2010

条件CSS的高级用法

No Comments, 前端开发, by admin.

介绍

条件CSS(Conditional-CSS)的开发源于在多数浏览器上修正 CSS 渲染 bug 的需求,以确保尽量多的用户看到正确的网站设计。核心思想建立在 Internet Explorer 上发现的条件注释方法,并扩展到包含其他的浏览器,而且将条件声明内联到 CSS 定义里面。

条件CSS(Conditional-CSS)并不仅仅对用户使用的浏览器感兴趣,而是对用户浏览器使用的渲染引擎更感兴趣。这就是条件(Conditional-CSS)使用 ‘Geckko’ 而非广为所知的 Firefox 作为它浏览器条件之一的原因。同样地, ‘Webkit’ 代替了 Safari。这使得其他使用同样渲染引擎地浏览器接受到那些同样地定位 CSS。这个规则地一个例外是使用了 IE(而不是 ‘Trident’),因为这是使用的 IE 的条件注释,而 ‘Trident’ 并不怎么为人所知。同样地,对于 Opera,因为只有 Opera 使用 Presto 渲染引擎,所以使用了 ‘Opera’。

需要注意的是,如果所有浏览器都能正确地执行 W3C 发布的 CSS 标准,那么条件CSS(Conditional-CSS)就没有需求了。但是,CSS bug 对于开发者是无法回避的现实,而且往往都及其让人沮丧。条件CSS(Conditional-CSS)给我们提供了一个简单的方法来解决这些问题。

高级条件声明

条件块

一个典型的条件声明只应用于一条 CSS 规则。当然,也有可能对整个 CSS 块使用条件,这样的块只用于特定的浏览器。下面的例子中 CSS 块只用于 IE 浏览器。

// 条件块实例
�
[if IE] .box {
    width: 500px;
    padding: 100px 0;
}

一个更高级的使用了条件CSS(Conditional-CSS)的实例样式表可以看这里。它展示了使用条件声明的各种方法。需要注意的是在条件声明和 CSS 声明之间不需要空格。

条件导入

条件CSS(Conditional-CSS)不仅仅自动将 CSS 中找到的任意 ‘@import’ 声明进行扩展并引入(为了减少 HTTP 请求),也允许条件导入语句。这可以用于为特定浏览器引入一些规则。下面的例子会为移动版 Safari(iPhone / iPod Touch)导入一个样式表,为其他浏览器导入另一个不同的样式表。

// 条件导入实例
�
[if SafMob] @import('iphone.css');   
[if ! SafMob] @import('non-iphone.css');

浏览器分组

将浏览器按照若干支持级别进行分组是一种常见并且是很好的做法。一个由我们在U4EA中提供的浏览器列表展示了这中方法,在那里我们将浏览器分成4个不同的支持级别:

  • A 级: 最高级,支持所有组件
  • C 级: 核心支持,基本显示信息
  • X 级: CSS 在该类浏览器中被锁定,仅依赖HTML渲染
  • U 级: 不支持。获得的CSS将和C级浏览器一样

可能遇到的情况是,你只想让A级浏览器获取某些CSS,而又要确保C级以及更低级的浏览器不能看到它们。比如,想让A级浏览器将一个UL列表显示为tab,而其他浏览器按照原始格式显示点式列表。

条件CSS 允许你通过定义一组浏览器到特定的分组来实现此类需求,然后使用标准条件语句中的浏览器区域来匹配这个浏览器分组。条件CSS 有一套内置的标准浏览器分组(当然,你也可以定义你自己的分组):

  • ‘cssA’ – A 级CSS支持
    • IE 6+
    • Gecko 1.0+ (Firefox, Camino, Flock, etc)
    • Webkit 312+ (Safari 1.3+, Google Chrome)
    • Opera 7+
    • Konqueror 3.3+
  • ‘cssX’ – X 级CSS支持
    • IE 4 以下
    • IE Mac 4.5 以下

一个使用条件CSS的浏览器分组的例子:

1
2
3
4
5
6
7
// 条件CSS浏览器分组实例
[if cssA] ul.li {
	display: block;
	margin-left: 5px;
	width: 50px;
	/* etc */
}

正如你可以看到的,浏览器分组的条件语句被格式化为与标准条件语句同样的语法。注意’cssX’是一个特殊的浏览器分组,它可以引起条件CSS返回空值除了它自己默认的。

  • [if {!} browser_group]

在这里:

  • ! – 代表否定声明(i.e. NOT) – 可选择的
  • browser_group – 指定浏览器分组声明标签
    • ‘cssA’ – A 级浏览器

浏览器是如何被检测到的

默认浏览器通过条件CSS和相应的样式被检测到,然后通过读取浏览器的user agent字符串处理。这使得设置和整合条件CSS到你的网站变得灰常容易,你所需要做的仅仅是上传代码并在你的HTML中编辑CSS import声明就可以了。User agent 检测是一种检测那种浏览器及其版本被使用的有效的方法,不过有一种倒退的现象就是有些用户改变他们的浏览器的user agent 以显示他们很了不起(通常是IE)。在这种情况下,最终用户将会获取错误的CSS到他们的浏览器。我对此的观点是,如果你的目标浏览器是IE,那么你应该预料到IE将会看到的情况。

通过HTTP GET 变量设置浏览器

之前我们有说过确保IE并且只有IE可以获得IE特定的CSS是可行的。要做到这些我们需要使用IE的条件注释并结合条件CSS能够使用GET变量获取浏览器信息的能力。条件CSS 接受浏览器的两个不同变量:

  • b – 浏览器名称
  • v – 浏览器版本(可选)

下面的这个例子显示使用条件注释声明的HTML需要确定IE6和IE7将获取它们的特定颜色,而其它的所有浏览器将获取其它样式:

1
2
3
4
5
6
7
8
9
<!--[if !IE]><!-->
  <style type="text/css">@import '/media/css/c-css.php?b=nonIE';</style>
<!--<![endif]-->
<!--[if IE 6]>
  <style type="text/css">@import '/media/css/ic-css.php?b=IE&v=6';</style>
<![endif]-->
<!--[if gte IE 7]>
  <style type="text/css">@import '/media/css/c-css.php?b=IE&v=7';</style>
<![endif]-->

使用静态CSS文件

使用条件CSS为每一个浏览器生成一个定制的CSS文件的方法看起来很不错, 它只是十分适用于管理一个相对比较轻量级的网站,因为程序必须运行于样式的每一个请求。对于中到大型网站,这的确不太合适,特别是当创建的文件数量受到限制的时候。所以条件语句有接受命令行参数的能力,它可以指定某个浏览器和版本(可选)应该生效然后输出最终样式到标准输出文件。下面的脚本可以用于为IE6/7以及非IE浏览器生成CSS文件(注意,该脚本描述了PHP版本的条件CSS,但是命令行选项和C版本一样):

1
2
3
4
#!/bin/sh
php -q c-css.php IE 7 > ie7.css
php -q c-css.php IE 6 > ie6.css
php -q c-css.php > nonie.css

公平的说,这是你需要的最合适的样式组合。因此,下面的使用HTML注释可以配合上面的脚本来调用需求的CSS文件。

1
2
3
4
5
6
7
8
9
<!--[if !IE]><!-->
  <style type="text/css">@import '/media/css/nonie.css';</style>
<!--<![endif]-->
<!--[if IE 6]>
  <style type="text/css">@import '/media/css/ie6.css';</style>
<![endif]-->
<!--[if gte IE 7]>
  <style type="text/css">@import '/media/css/ie7.css';</style>
<![endif]-->

享受条件CSS的好处吧!

糖伴西红柿说:
最终的条件CSS(Conditional-CSS)的高级用法也新鲜出炉了,加上老江的条件CSS(Conditional-CSS)介绍,这个系列就全了。
剩下的就是学习、讨论了。嗯,这篇文章非我一人之功,神飞同学做出了巨大的贡献。

原文地址:http://www.conditional-css.com/advanced
译文地址:http://www.qianduan.net/?p=6177

因为搜索引擎的流行,网络爬虫已经成了很普及网络技术,除了专门做搜索的Google,Yahoo,微软,百度以外,几乎每个大型门户网站都有自己的搜索引擎,大大小小叫得出来名字得就几十种,还有各种不知名的几千几万种,对于一个内容型驱动的网站来说,受到网络爬虫的光顾是不可避免的。

一些智能的搜索引擎爬虫的爬取频率比较合理,对网站资源消耗比较少,但是很多糟糕的网络爬虫,对网页爬取能力很差,经常并发几十上百个请求循环重复抓取,这种爬虫对中小型网站往往是毁灭性打击,特别是一些缺乏爬虫编写经验的程序员写出来的爬虫破坏力极强。曾经有一次我在JavaEye的日志里面发现一个User-Agent是Java的爬虫一天之内爬取了将近100万次动态请求。这是一个用JDK标准类库编写的简单爬取网页程序,由于JavaEye网站内部链接构成了回环导致程序陷入了死循环。对于JavaEye这种百万PV级别的网站来说,这种爬虫造成的访问压力会非常大,会导致网站访问速度缓慢,甚至无法访问。

此外,相当数量的的网页爬虫目的是盗取目标网站的内容。比方说JavaEye网站就曾经被两个竞争对手网站爬取论坛帖子,然后在自己的论坛里面用机器人发帖,因此这种爬虫不仅仅影响网站访问速度,而且侵犯了网站的版权。

对于一个原创内容丰富,URL结构合理易于爬取的网站来说,简直就是各种爬虫的盘中大餐,很多网站的访问流量构成当中,爬虫带来的流量要远远超过真实用户访问流量,甚至爬虫流量要高出真实流量一个数量级。像JavaEye网站虽然设置了相当严格的反爬虫策略,但是网站处理的动态请求数量仍然是真实用户访问流量的2倍。可以肯定的说,当今互联网的网络流量至少有2/3的流量爬虫带来的。因此反爬虫是一个值得网站长期探索和解决的问题。

一、手工识别和拒绝爬虫的访问

有相当多的爬虫对网站会造成非常高的负载,因此识别爬虫的来源IP是很容易的事情。最简单的办法就是用netstat检查80端口的连接:
C代码
netstat -nt | grep youhostip:80 | awk ‘{print $5}’ | awk -F”:” ‘{print $1}’| sort | uniq -c | sort -r -n

这行shell可以按照80端口连接数量对来源IP进行排序,这样可以直观的判断出来网页爬虫。一般来说爬虫的并发连接非常高。

如果使用lighttpd做Web Server,那么就更简单了。lighttpd的mod_status提供了非常直观的并发连接的信息,包括每个连接的来源IP,访问的URL,连接状态和连接时间等信息,只要检查那些处于handle-request状态的高并发IP就可以很快确定爬虫的来源IP了。

拒绝爬虫请求既可以通过内核防火墙来拒绝,也可以在web server拒绝,比方说用iptables拒绝:
C代码
iptables -A INPUT -i eth0 -j DROP -p tcp –dport 80 -s 84.80.46.0/24

直接封锁爬虫所在的C网段地址。这是因为一般爬虫都是运行在托管机房里面,可能在一个C段里面的多台服务器上面都有爬虫,而这个C段不可能是用户宽带上网,封锁C段可以很大程度上解决问题。

有些人提出一种脑残的观点,说我要惩罚这些爬虫。我专门在网页里面设计动态循环链接页面,让爬虫掉进陷阱,死循环爬不出来,其实根本用不着设置陷阱,弱智爬虫对正常网页自己就爬不出来,这样做多此一举不说,而且会让真正的搜索引擎降低你的网页排名。而且运行一个爬虫根本不消耗什么机器资源,相反,真正宝贵的是你的服务器CPU资源和服务器带宽,简单的拒绝掉爬虫的请求是反爬虫最有效的策略。

二、通过识别爬虫的User-Agent信息来拒绝爬虫

有很多爬虫并不会以很高的并发连接爬取,一般不容易暴露自己;有些爬虫的来源IP分布很广,很难简单的通过封锁IP段地址来解决问题;另外还有很多各种各样的小爬虫,它们在尝试Google以外创新的搜索方式,每个爬虫每天爬取几万的网页,几十个爬虫加起来每天就能消耗掉上百万动态请求的资源,由于每个小爬虫单独的爬取量都很低,所以你很难把它从每天海量的访问IP地址当中把它准确的挖出来。

这种情况下我们可以通过爬虫的User-Agent信息来识别。每个爬虫在爬取网页的时候,会声明自己的User-Agent信息,因此我们就可以通过记录和分析User-Agent信息来挖掘和封锁爬虫。我们需要记录每个请求的User-Agent信息,对于Rails来说我们可以简单的在app/controllers/application.rb里面添加一个全局的before_filter,来记录每个请求的User-Agent信息:
Ruby代码
logger.info “HTTP_USER_AGENT #{request.env["HTTP_USER_AGENT"]}”

然后统计每天的production.log,抽取User-Agent信息,找出访问量最大的那些User-Agent。要注意的是我们只关注那些爬虫的User-Agent信息,而不是真正浏览器User-Agent,所以还要排除掉浏览器User-Agent,要做到这一点仅仅需要一行shell:
Ruby代码
grep HTTP_USER_AGENT production.log | grep -v -E ‘MSIE|Firefox|Chrome|Opera|Safari|Gecko’ | sort | uniq -c | sort -r -n | head -n 100 > bot.log

统计结果类似这样:
C代码
57335 HTTP_USER_AGENT Baiduspider+(+http://www.baidu.com/search/spider.htm)
56639 HTTP_USER_AGENT Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
42610 HTTP_USER_AGENT Mediapartners-Google
19131 HTTP_USER_AGENT msnbot/2.0b (+http://search.msn.com/msnbot.htm)

从日志就可以直观的看出每个爬虫的请求次数。要根据User-Agent信息来封锁爬虫是件很容易的事情,lighttpd配置如下:
C代码
$HTTP["useragent"] =~ “qihoobot|^Java|Commons-HttpClient|Wget|^PHP|Ruby|Python” {
url.rewrite = ( “^/(.*)” => “/crawler.html” )
}

使用这种方式来封锁爬虫虽然简单但是非常有效,除了封锁特定的爬虫,还可以封锁常用的编程语言和HTTP类库的User-Agent信息,这样就可以避免很多无谓的程序员用来练手的爬虫程序对网站的骚扰。

还有一种比较常见的情况,就是某个搜索引擎的爬虫对网站爬取频率过高,但是搜索引擎给网站带来了很多流量,我们并不希望简单的封锁爬虫,仅仅是希望降低爬虫的请求频率,减轻爬虫对网站造成的负载,那么我们可以这样做:
C代码
$HTTP["user-agent"] =~ “Baiduspider+” {
connection.delay-seconds = 10
}

对百度的爬虫请求延迟10秒钟再进行处理,这样就可以有效降低爬虫对网站的负载了。

三、通过网站流量统计系统和日志分析来识别爬虫

有些爬虫喜欢修改User-Agent信息来伪装自己,把自己伪装成一个真实浏览器的User-Agent信息,让你无法有效的识别。这种情况下我们可以通过网站流量系统记录的真实用户访问IP来进行识别。

主流的网站流量统计系统不外乎两种实现策略:一种策略是在网页里面嵌入一段js,这段js会向特定的统计服务器发送请求的方式记录访问量;另一种策略是直接分析服务器日志,来统计网站访问量。在理想的情况下,嵌入js的方式统计的网站流量应该高于分析服务器日志,这是因为用户浏览器会有缓存,不一定每次真实用户访问都会触发服务器的处理。但实际情况是,分析服务器日志得到的网站访问量远远高于嵌入js方式,极端情况下,甚至要高出10倍以上。

现在很多网站喜欢采用awstats来分析服务器日志,来计算网站的访问量,但是当他们一旦采用Google Analytics来统计网站流量的时候,却发现GA统计的流量远远低于awstats,为什么GA和awstats统计会有这么大差异呢?罪魁祸首就是把自己伪装成浏览器的网络爬虫。这种情况下awstats无法有效的识别了,所以awstats的统计数据会虚高。

其实作为一个网站来说,如果希望了解自己的网站真实访问量,希望精确了解网站每个频道的访问量和访问用户,应该用页面里面嵌入js的方式来开发自己的网站流量统计系统。自己做一个网站流量统计系统是件很简单的事情,写段服务器程序响应客户段js的请求,分析和识别请求然后写日志的同时做后台的异步统计就搞定了。

通过流量统计系统得到的用户IP基本是真实的用户访问,因为一般情况下爬虫是无法执行网页里面的js代码片段的。所以我们可以拿流量统计系统记录的IP和服务器程序日志记录的IP地址进行比较,如果服务器日志里面某个IP发起了大量的请求,在流量统计系统里面却根本找不到,或者即使找得到,可访问量却只有寥寥几个,那么无疑就是一个网络爬虫。

分析服务器日志统计访问最多的IP地址段一行shell就可以了:
C代码
grep Processing production.log | awk ‘{print $4}’ | awk -F’.’ ‘{print $1″.”$2″.”$3″.0″}’ | sort | uniq -c | sort -r -n | head -n 200 > stat_ip.log

然后把统计结果和流量统计系统记录的IP地址进行对比,排除真实用户访问IP,再排除我们希望放行的网页爬虫,比方Google,百度,微软msn爬虫等等。最后的分析结果就就得到了爬虫的IP地址了。以下代码段是个简单的实现示意:
Ruby代码
whitelist = []
IO.foreach(“#{RAILS_ROOT}/lib/whitelist.txt”) { |line| whitelist << line.split[0].strip if line }

realiplist = []
IO.foreach(“#{RAILS_ROOT}/log/visit_ip.log”) { |line|  realiplist << line.strip if line }

iplist = []
IO.foreach(“#{RAILS_ROOT}/log/stat_ip.log”) do |line|
ip = line.split[1].strip
iplist << ip if line.split[0].to_i > 3000 && !whitelist.include?(ip) && !realiplist.include?(ip)
end

Report.deliver_crawler(iplist)

分析服务器日志里面请求次数超过3000次的IP地址段,排除白名单地址和真实访问IP地址,最后得到的就是爬虫IP了,然后可以发送邮件通知管理员进行相应的处理。

四、网站的实时反爬虫防火墙实现策略

通过分析日志的方式来识别网页爬虫不是一个实时的反爬虫策略。如果一个爬虫非要针对你的网站进行处心积虑的爬取,那么他可能会采用分布式爬取策略,比方说寻找几百上千个国外的代理服务器疯狂的爬取你的网站,从而导致网站无法访问,那么你再分析日志是不可能及时解决问题的。所以必须采取实时反爬虫策略,要能够动态的实时识别和封锁爬虫的访问。

要自己编写一个这样的实时反爬虫系统其实也很简单。比方说我们可以用memcached来做访问计数器,记录每个IP的访问频度,在单位时间之内,如果访问频率超过一个阀值,我们就认为这个IP很可能有问题,那么我们就可以返回一个验证码页面,要求用户填写验证码。如果是爬虫的话,当然不可能填写验证码,所以就被拒掉了,这样很简单就解决了爬虫问题。

用memcache记录每个IP访问计数,单位时间内超过阀值就让用户填写验证码,用Rails编写的示例代码如下:
Ruby代码
ip_counter = Rails.cache.increment(request.remote_ip)
if !ip_counter
Rails.cache.write(request.remote_ip, 1, :expires_in => 30.minutes)
elsif ip_counter > 2000
render :template => ‘test’, :status => 401 and return false
end

这段程序只是最简单的示例,实际的代码实现我们还会添加很多判断,比方说我们可能要排除白名单IP地址段,要允许特定的User-Agent通过,要针对登录用户和非登录用户,针对有无referer地址采取不同的阀值和计数加速器等等。

此外如果分布式爬虫爬取频率过高的话,过期就允许爬虫再次访问还是会对服务器造成很大的压力,因此我们可以添加一条策略:针对要求用户填写验证码的IP地址,如果该IP地址短时间内继续不停的请求,则判断为爬虫,加入黑名单,后续请求全部拒绝掉。为此,示例代码可以改进一下:
Ruby代码
before_filter :ip_firewall, :except => :test
def ip_firewall
render :file => “#{RAILS_ROOT}/public/403.html”, :status => 403 if BlackList.include?(ip_sec)
end

我们可以定义一个全局的过滤器,对所有请求进行过滤,出现在黑名单的IP地址一律拒绝。对非黑名单的IP地址再进行计数和统计:
Ruby代码
ip_counter = Rails.cache.increment(request.remote_ip)
if !ip_counter
Rails.cache.write(request.remote_ip, 1, :expires_in => 30.minutes)
elsif ip_counter > 2000
crawler_counter = Rails.cache.increment(“crawler/#{request.remote_ip}”)
if !crawler_counter
Rails.cache.write(“crawler/#{request.remote_ip}”, 1, :expires_in => 10.minutes)
elsif crawler_counter > 50
BlackList.add(ip_sec)
render :file => “#{RAILS_ROOT}/public/403.html”, :status => 403 and return false
end
render :template => ‘test’, :status => 401 and return false
end

如果某个IP地址单位时间内访问频率超过阀值,再增加一个计数器,跟踪他会不会立刻填写验证码,如果他不填写验证码,在短时间内还是高频率访问,就把这个IP地址段加入黑名单,除非用户填写验证码激活,否则所有请求全部拒绝。这样我们就可以通过在程序里面维护黑名单的方式来动态的跟踪爬虫的情况,甚至我们可以自己写个后台来手工管理黑名单列表,了解网站爬虫的情况。

这个策略已经比较智能了,但是还不够好!我们还可以继续改进:

1、用网站流量统计系统来改进实时反爬虫系统

还记得吗?网站流量统计系统记录的IP地址是真实用户访问IP,所以我们在网站流量统计系统里面也去操作memcached,但是这次不是增加计数值,而是减少计数值。在网站流量统计系统里面每接收到一个IP请求,就相应的cache.decrement(key)。所以对于真实用户的IP来说,它的计数值总是加1然后就减1,不可能很高。这样我们就可以大大降低判断爬虫的阀值,可以更加快速准确的识别和拒绝掉爬虫。

2、用时间窗口来改进实时反爬虫系统

爬虫爬取网页的频率都是比较固定的,不像人去访问网页,中间的间隔时间比较无规则,所以我们可以给每个IP地址建立一个时间窗口,记录IP地址最近12次访问时间,每记录一次就滑动一次窗口,比较最近访问时间和当前时间,如果间隔时间很长判断不是爬虫,清除时间窗口,如果间隔不长,就回溯计算指定时间段的访问频率,如果访问频率超过阀值,就转向验证码页面让用户填写验证码。

最终这个实时反爬虫系统就相当完善了,它可以很快的识别并且自动封锁爬虫的访问,保护网站的正常访问。不过有些爬虫可能相当狡猾,它也许会通过大量的爬虫测试来试探出来你的访问阀值,以低于阀值的爬取速度抓取你的网页,因此我们还需要辅助第3种办法,用日志来做后期的分析和识别,就算爬虫爬的再慢,它累计一天的爬取量也会超过你的阀值被你日志分析程序识别出来。

总之我们综合运用上面的四种反爬虫策略,可以很大程度上缓解爬虫对网站造成的负面影响,保证网站的正常访问。
原文地址:http://robbin.javaeye.com/blog/451014

一月 5th, 2010

值得一看的俏皮话

No Comments, 随笔记录, by admin.

1、等我有钱了,我就买一辆公交车,专门走公交专用车道,专门停在公交车站,等有人想上车了,我就说:对不起,这是私家车
2、客官 您是打尖还是住店
我大便
3、我年轻过,你们呢,老过么?
4、绅士无非就是耐心的狼
5、所有的人都站在一边并不一定是好事,譬如他们都站在船的一边
6、别紧张,我不是什么好人……
7、你说…你喜欢我?其实…我一开始…其实我也…唉跟你说了吧,其实我也挺喜欢我自己的.
8、作为失败的典型,你实在是太成功了
9、小时候刚学骑自行车,还不太会就跑到大街上,看到前面一个老大爷在走,自己感觉要撞上,就大叫,不要动,不要动。那个老大爷一下站在那里没有动,结果我拐来拐去,还是撞上了。老大爷站起来说,你瞄准呢
10、如果有300W,大家说是买奔驰好还是法拉利好呀。
回复:最好买300辆二手奥托,再雇300个司机,让他们跟在你后面开,一会排成s形,一会排成B形
11、多多微笑,阴天谨防情绪感冒!
12、我自横刀向天笑,笑完我就去睡觉!
13、 路遥知马力不足,日久见人心叵测 www.5dhz.com
14、我爸面对我发胖一事发表了看法:没有韩红的命,还得了韩红的病。
15、我这人从不记仇,一般有仇当场我就报了。
16、别在我的坟前哭。脏了我轮回的路。
17、是这样的张总,你在家里的电脑上按了CTRL+C,然后在公司的电脑上再按CTRL+V是肯定不行的。即使同一篇文章也不行。不不,多贵的电脑都不行。
18、我以为你只是1和3中间的数,没想到你还是1和3俩数的组合。
19、上帝肯定会原谅我的,因为那是他的职业。
20、一食人族去班,经理再三交代不能吃同事,答应。过几天忍不住,偷吃一个清洁工 。人,当即被发现。其感悟是:千万别吃真正做事的人。
21、现在你骂我,是因为你还不了解我,等你以后了解了我,你一定会动手打我的。
22、人永远不知道谁哪次不经意的跟你说了再见之后就真的再也不见了。
23、通往成功的路,总是在施工中。
24、我不下地狱,谁爱下谁下
25、猜一句英文:「ABABBBAAAAAABBBABAAAABBBBAABBBAAAAA」?〈答案:Long time no C〉
26、想你的眉目,想到模糊。——突然觉得,思念大都如此,越来越淡(我依稀记得这是王菲《我也不想这样》的歌词,不知对否?)
27、在经年后,感叹,那两个少年:一个惊艳了时光,一个温柔了岁月。
28、如果她(他)对你说:“忘了我吧。”你告诉对方:“我一直没记住。”
29、你对咱的好 俺永远记得 俺做鬼都不会放过你的。
30、各位女同事,请不要对我放电,我老婆有来电显示.
31、微笑,挥手,再见,结束。
32、我想起来,我曾立志做一个好玩的人
33、将薪比薪的想一下,算了,不想活了。
34、那啥,就给我个经济适用坟好了。
35、活了二十多年,没能为祖国、为人民做点什么,每思及此,伤心欲绝
36、趁着年轻把能干的坏事都干了吧,没几年了。
37、挣着卖白菜的钱,****着卖白粉的心
38、七岁的小男孩是地球上最可怕的生物,他们有好奇心、行动力、破坏力以及《未成年人保****》。
39、人贵在言而有信——我说不还钱就不还钱!
40、老子曰:睡可睡,非常睡。
41、我虽然相信海誓山盟,但是未必相信你啊
42、上帝说:出门不要忘记带伞,一会儿我要浇花
43、特别的人从来不说自己特别,比如说我
44、我的否已极了 可泰说他没空来
45、我知道,天下无不散宴席,可是,至少,宴席上我要吃得爽!
46、我走我的阳光道,你过你的奈何桥。
47、世界是我们的,也是孩子们的,但最终是那帮孙子们的!
48、每当困难的时候我就念藏经:“噢嘛呢哞嘛哄”, 翻译成英文就是:All money go my home!
49、最简单的长寿秘决————–保持呼吸,不要断气
50、孔子曰:中午不睡,下午崩溃。孟子曰:孔子说的对!
51、善良就是别人挨饿的时候,我吃肉不bia ji嘴
52、漫漫人生路,总会错几步。
53、我从不以强凌弱~~~我欺负他之前真不知道他比我弱…
54、你走你的过街天桥,我过我的地下通道。
55、我的兴趣爱好可分为静态和动态两种,静态就是睡觉,动态就是翻身…
56、哪里跌倒,哪里爬起……老是在那里跌倒,我怀疑那里有个坑!
57、唉~这人要一没正形,连头痛都是偏的
58、我这人不太懂音乐,所以时而不靠谱,时而不着调。
59、人干点好事儿总想让鬼神知道,干点坏事儿总以为鬼神不知道,我们让鬼神太为难了。
60、问世间谁最坦荡,直叫我当仁不让 www.sou1sou.com
61、如果你容不下我,说明不是你的心胸太狭小,就是我的人格太伟大。
62、行到水穷处,口渴;坐看云起时,头晕。
63、我想学凤凰涅磐,谁知道一不小心……熟了!
64、反正我这命老和他们算的不一样,不知道是他们没算对,还是我活错了。
65、你嘴角三十度的微笑,百度搜索不到。

一月 5th, 2010

js验证表单大全

No Comments, 前端开发, by admin.

1. 长度限制
<script>
function test()
{
if(document.a.b.value.length>50)
{
alert(“不能超过50个字符!”);
document.a.b.focus();
return false;
}
}
</script>
<form name=a onsubmit=”return test()”>
<textarea name=”b” cols=”40″ wrap=”VIRTUAL” rows=”6″></textarea>
<input type=”submit” name=”Submit” value=”check”>
</form>

2. 只能是汉字
<input onkeyup=”value=”/oblog/value.replace(/[^\u4E00-\u9FA5]/g,”)”>

3.” 只能是英文
<script language=javascript>
function onlyEng()
{
if(!(event.keyCode>=65&&event.keyCode<=90))
event.returnvalue=false;
}
</script>

<input onkeydown=”onlyEng();”>

4. 只能是数字
<script language=javascript>
function onlyNum()
{
if(!((event.keyCode>=48&&event.keyCode<=57)||(event.keyCode>=96&&event.keyCode<=105)))
//考虑小键盘上的数字键
event.returnvalue=false;
}
</script>

<input onkeydown=”onlyNum();”>

5. 只能是英文字符和数字
<input onkeyup=”value=”/oblog/value.replace(/[\W]/g,””) “onbeforepaste=”clipboardData.setData(‘text’,clipboardData.getData(‘text’).replace(/[^\d]/g,”))”>

6. 验证油箱格式
<SCRIPT LANGUAGE=javascript RUNAT=Server>
function isEmail(strEmail) {
if (strEmail.search(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/) != -1)
return true;
else
alert(“oh”);
}
</SCRIPT>
<input type=text onblur=isEmail(this.value)>

7. 屏蔽关键字(这里屏蔽***和****)
<script language=”javascript1.2″>
function test() {
if((a.b.value.indexOf (“***”) == 0)||(a.b.value.indexOf (“****”) == 0)){
alert(“:)”);
a.b.focus();
return false;}
}
</script>
<form name=a onsubmit=”return test()”>
<input type=text name=b>
<input type=”submit” name=”Submit” value=”check”>
</form>

8. 两次输入密码是否相同
<FORM METHOD=POST ACTION=”">
<input type=”password” id=”input1″>
<input type=”password” id=”input2″>
<input type=”button” value=”test” onclick=”check()”>
</FORM>
<script>
function check()
{
with(document.all){
if(input1.value!=input2.value)
{
alert(“false”)
input1.value = “”;
input2.value = “”;
}
else document.forms[0].submit();
}
}
</script>
够了吧
屏蔽右键 很酷
oncontextmenu=”return false” ondragstart=”return false” onselectstart=”return false”
加在body中

2.1   表单项不能为空

<script   language=”javascript”>
<!–
function   CheckForm()
{
if   (document.form.name.value.length   ==   0)   {
alert(“请输入您姓名!”);
document.form.name.focus();
return   false;
}
return   true;
}
–>
</script>

2.2   比较两个表单项的值是否相同

<script   language=”javascript”>
<!–
function   CheckForm()
if   (document.form.PWD.value   !=   document.form.PWD_Again.value)   {
alert(“您两次输入的密码不一样!请重新输入.”);
document.ADDUser.PWD.focus();
return   false;
}
return   true;
}
–>
</script>

2.3   表单项只能为数字和”_”,用于电话/银行帐号验证上,可扩展到域名注册等

<script   language=”javascript”>
<!–
function   isNumber(String)
{
var   Letters   =   “1234567890-”;   //可以自己增加可输入值
var   i;
var   c;
if(String.charAt(   0   )==’-')
return   false;
if(   String.charAt(   String.length   -   1   )   ==   ‘-’   )
return   false;
for(   i   =   0;   i   <   String.length;   i   ++   )
{
c   =   String.charAt(   i   );
if   (Letters.indexOf(   c   )   <   0)
return   false;
}
return   true;
}
function   CheckForm()
{
if(!   isNumber(document.form.TEL.value))   {
alert(“您的电话号码不合法!”);
document.form.TEL.focus();
return   false;
}
return   true;
}
–>
</script>

2.4   表单项输入数值/长度限定

<script   language=”javascript”>
<!–
function   CheckForm()
{
if   (document.form.count.value   >   100   ||   document.form.count.value   <   1)
{
alert(“输入数值不能小于零大于100!”);
document.form.count.focus();
return   false;
}
if   (document.form.MESSAGE.value.length<10)
{
alert(“输入文字小于10!”);
document.form.MESSAGE.focus();
return   false;
}
return   true;
}
//–>
</script>

2.5   中文/英文/数字/邮件地址合法性判断

<SCRIPT   LANGUAGE=”javascript”>
<!–

function   isEnglish(name)   //英文值检测
{
if(name.length   ==   0)
return   false;
for(i   =   0;   i   <   name.length;   i++)   {
if(name.charCodeAt(i)   >   128)
return   false;
}
return   true;
}

function   isChinese(name)   //中文值检测
{
if(name.length   ==   0)
return   false;
for(i   =   0;   i   <   name.length;   i++)   {
if(name.charCodeAt(i)   >   128)
return   true;
}
return   false;
}

function   isMail(name)   //   E-mail值检测
{
if(!   isEnglish(name))
return   false;
i   =   name.indexOf(“   at   “);
j   =   name   dot   lastIndexOf(“   at   “);
if(i   ==   -1)
return   false;
if(i   !=   j)
return   false;
if(i   ==   name   dot   length)
return   false;
return   true;
}

function   isNumber(name)   //数值检测
{
if(name.length   ==   0)
return   false;
for(i   =   0;   i   <   name.length;   i++)   {
if(name.charAt(i)   <   “0″   ||   name.charAt(i)   >   “9″)
return   false;
}
return   true;
}

function   CheckForm()
{
if(!   isMail(form.Email.value))   {
alert(“您的电子邮件不合法!”);
form.Email.focus();
return   false;
}
if(!   isEnglish(form.name.value))   {
alert(“英文名不合法!”);
form.name.focus();
return   false;
}
if(!   isChinese(form.cnname.value))   {
alert(“中文名不合法!”);
form.cnname.focus();
return   false;
}
if(!   isNumber(form.PublicZipCode.value))   {
alert(“邮政编码不合法!”);
form.PublicZipCode.focus();
return   false;
}
return   true;
}
//–>
</SCRIPT>

2.6   限定表单项不能输入的字符

<script   language=”javascript”>
<!–

function   contain(str,charset)//   字符串包含测试函数
{
var   i;
for(i=0;i<charset.length;i++)
if(str.indexOf(charset.charAt(i))>=0)
return   true;
return   false;
}

function   CheckForm()
{
if   ((contain(document.form.NAME.value,   “%\(\)><”))   ||   (contain(document.form.MESSAGE.value,   “%\(\)><”)))
{
alert(“输入了非法字符”);
document.form.NAME.focus();
return   false;
}
return   true;
}
//–>
</script>

1. 检查一段字符串是否全由数字组成
—————————————
<script language=”Javascript”><!–
function checkNum(str){return str.match(/\D/)==null}
alert(checkNum(“1232142141″))
alert(checkNum(“123214214a1″))
// –></script>

2. 怎么判断是否是字符
—————————————
if (/[^\x00-\xff]/g.test(s)) alert(“含有汉字”);
else alert(“全是字符”);

3. 怎么判断是否含有汉字
—————————————
if (escape(str).indexOf(“%u”)!=-1) alert(“含有汉字”);
else alert(“全是字符”);

4. 邮箱格式验证
—————————————
//函数名:chkemail
//功能介绍:检查是否为Email Address
//参数说明:要检查的字符串
//返回值:0:不是 1:是
function chkemail(a)
{ var i=a.length;
var temp = a.indexOf(‘@’);
var tempd = a.indexOf(‘.’);
if (temp > 1) {
if ((i-temp) > 3){
if ((i-tempd)>0){
return 1;
}

}
}
return 0;
}

5. 数字格式验证
—————————————
//函数名:fucCheckNUM
//功能介绍:检查是否为数字
//参数说明:要检查的数字
//返回值:1为是数字,0为不是数字
function fucCheckNUM(NUM)
{
var i,j,strTemp;
strTemp=”0123456789″;
if ( NUM.length== 0)
return 0
for (i=0;i<NUM.length;i++)
{
j=strTemp.indexOf(NUM.charAt(i));
if (j==-1)
{
//说明有字符不是数字
return 0;
}
}
//说明是数字
return 1;
}

6. 电话号码格式验证
—————————————
//函数名:fucCheckTEL
//功能介绍:检查是否为电话号码
//参数说明:要检查的字符串
//返回值:1为是合法,0为不合法
function fucCheckTEL(TEL)
{
var i,j,strTemp;
strTemp=”0123456789-()# “;
for (i=0;i<TEL.length;i++)
{
j=strTemp.indexOf(TEL.charAt(i));
if (j==-1)
{
//说明有字符不合法
return 0;
}
}
//说明合法
return 1;
}

7. 判断输入是否为中文的函数
—————————————
function ischinese(s){
var ret=true;
for(var i=0;i<s.length;i++)
ret=ret && (s.charCodeAt(i)>=10000);
return ret;
}

8. 综合的判断用户输入的合法性的函数
—————————————
<script language=”javascript”>
//限制输入字符的位数开始
//m是用户输入,n是要限制的位数
function issmall(m,n)
{
if ((m<n) && (m>0))
{
return(false);
}
else
{return(true);}
}

9. 判断密码是否输入一致
—————————————
function issame(str1,str2)
{
if (str1==str2)
{return(true);}
else
{return(false);}
}

10. 判断用户名是否为数字字母下滑线
—————————————
function notchinese(str){
var reg=/[^A-Za-z0-9_]/g
if (reg.test(str)){
return (false);
}else{
return(true);     }
}

11. form文本域的通用校验函数
—————————————
作用:检测所有必须非空的input文本,比如姓名,账号,邮件地址等等。
该校验现在只针对文本域,如果要针对form里面的其他域对象,可以改变判断条件。

使用方法:在要检测的文本域中加入title文字。文字是在提示信息,你要提示给用户的该字段的中文名。比如要检测用户名
html如下<input name=”txt_1″ title=”姓名”>,当然,最好用可视化工具比如dreamweaver什么的来编辑域。
如果要检测数字类型数据的话,再把域的id统一为sz.
javascript判断日期类型比较麻烦,所以就没有做日期类型校验的程序了.高手可以补充。

程序比较草,只是提供一个思路。抛砖引玉! :)
哦,对了,函数调用方法:< form   onsubmit=”return dovalidate()”>

function dovalidate()
{
fm=document.forms[0] //只检测一个form,如果是多个可以改变判断条件
for(i=0;i<fm.length;i++)
{
//检测判断条件,根据类型不同可以修改
if(fm[i].tagName.toUpperCase()==”INPUT” &&fm[i].type.toUpperCase()==”TEXT” && (fm[i].title!=”"))

if(fm[i].value=”/blog/=”")//
{
str_warn1=fm[i].title+”不能为空!”;
alert(str_warn1);
fm[i].focus();
return false;
}
if(fm[i].id.toUpperCase()==”SZ”)//数字校验
{
if(isNaN(fm[i].value))
{ str_warn2=fm[i].title+”格式不对”;
alert(str_warn2);
fm[i].focus();
return false;
}
}
}
return true;
}

2 >表单提交验证类

2.1 表单项不能为空

<script language=”javascript”>
<!–
function CheckForm()
{
if (document.form.name.value.length == 0) {
alert(“请输入您姓名!”);
document.form.name.focus();
return false;
}
return true;
}
–>
</script>

2.2 比较两个表单项的值是否相同

<script language=”javascript”>
<!–
function CheckForm()
if (document.form.PWD.value != document.form.PWD_Again.value) {
alert(“您两次输入的密码不一样!请重新输入.”);
document.ADDUser.PWD.focus();
return false;
}
return true;
}
–>
</script>

2.3 表单项只能为数字和”_”,用于电话/银行帐号验证上,可扩展到域名注册等

<script language=”javascript”>
<!–
function isNumber(String)
{
var Letters = “1234567890-”; //可以自己增加可输入值
var i;
var c;
if(String.charAt( 0 )==’-')
return false;
if( String.charAt( String.length – 1 ) == ‘-’ )
return false;
for( i = 0; i < String.length; i ++ )
{
c = String.charAt( i );
if (Letters.indexOf( c ) < 0)
return false;
}
return true;
}
function CheckForm()
{
if(! isNumber(document.form.TEL.value)) {
alert(“您的电话号码不合法!”);
document.form.TEL.focus();
return false;
}
return true;
}
–>
</script>

2.4 表单项输入数值/长度限定

<script language=”javascript”>
<!–
function CheckForm()
{
if (document.form.count.value > 100 || document.form.count.value < 1)
{
alert(“输入数值不能小于零大于100!”);
document.form.count.focus();
return false;
}
if (document.form.MESSAGE.value.length<10)
{
alert(“输入文字小于10!”);
document.form.MESSAGE.focus();
return false;
}
return true;
}
//–>
</script>

2.5 中文/英文/数字/邮件地址合法性判断

<SCRIPT LANGUAGE=”javascript”>
<!–

function isEnglish(name) //英文值检测
{
if(name.length == 0)
return false;
for(i = 0; i < name.length; i++) {
if(name.charCodeAt(i) > 128)
return false;
}
return true;
}

function isChinese(name) //中文值检测
{
if(name.length == 0)
return false;
for(i = 0; i < name.length; i++) {
if(name.charCodeAt(i) > 128)
return true;
}
return false;
}

function isMail(name) // E-mail值检测
{
if(! isEnglish(name))
return false;
i = name.indexOf(” at “);
j = name dot lastIndexOf(” at “);
if(i == -1)
return false;
if(i != j)
return false;
if(i == name dot length)
return false;
return true;
}

function isNumber(name) //数值检测
{
if(name.length == 0)
return false;
for(i = 0; i < name.length; i++) {
if(name.charAt(i) < “0″ || name.charAt(i) > “9″)
return false;
}
return true;
}

function CheckForm()
{
if(! isMail(form.Email.value)) {
alert(“您的电子邮件不合法!”);
form.Email.focus();
return false;
}
if(! isEnglish(form.name.value)) {
alert(“英文名不合法!”);
form.name.focus();
return false;
}
if(! isChinese(form.cnname.value)) {
alert(“中文名不合法!”);
form.cnname.focus();
return false;
}
if(! isNumber(form.PublicZipCode.value)) {
alert(“邮政编码不合法!”);
form.PublicZipCode.focus();
return false;
}
return true;
}
//–>
</SCRIPT>

2.6 限定表单项不能输入的字符

<script language=”javascript”>
<!–

function contain(str,charset)// 字符串包含测试函数
{
var i;
for(i=0;i<charset.length;i++)
if(str.indexOf(charset.charAt(i))>=0)
return true;
return false;
}

function CheckForm()
{
if ((contain(document.form.NAME.value, “%\(\)><”)) || (contain(document.form.MESSAGE.value, “%\(\)><”)))
{
alert(“输入了非法字符”);
document.form.NAME.focus();
return false;
}
return true;
}
//–>
</script>