第6章 搜索引擎优化
现在互联网已经成为人们日常生活中一个重要的组成部分,人们上网看新闻,在网上逛商店,在论坛中讨论问题等。广大企业和商家也充分意识到了互联网平台的重要性,纷纷建立企业网站,发布供求信息。企业网站建立以后,如何让更多的用户来访问是一个非常重要的问题。
在当今互联网上,用户查找某种资料的方式一般都是使用搜索引擎进行搜索,然后根据搜索引擎列出的搜索结果和链接访问相关网站。这就意味着,在搜索引擎的搜索结果中,排名越靠前的网站被访问到的机率越高,而更高的访问次数也就意味着更多的商品销售和利润。如何才能使自己的网站在搜索引擎的搜索结果中排名靠前呢?这对企业来说是一个很重要的问题,这也就是搜索引擎优化所要解决的问题。
6.1 搜索引擎优化简介
搜索引擎优化对应的英文名称是Search Engine Optimization,简称SEO,是近年来较为热门的一个技术领域。本节将介绍搜索引擎优化的概念、动机和原理,使读者对搜索引擎优化建立一个印象。
6.1.1 搜索引擎优化基本概念
随着互联网的普及,越来越多的企业和商家建立自己的网站,在互联网上发布自己的产品信息,甚至有很多个人也建立了个人网站或者博客。作为一个展示产品的平台和媒介,相对于传统的产品宣传方式来说,互联网拥有更丰富的内容、更广泛的受众和更廉价的成本,因而也越来越得到企业和商家的重视。
对于一个企业来说,为了达到宣传企业商品的目的,建立企业网站只是第一步。网站建立后,如果没有顾客访问,则起不到任何作用。企业要想提高网站的访问量,可以有两种主要途径,第一是为网站做广告,这种方式成本较高;第二就是搜索引擎优化,这种方式成本较低且效果明显。
所谓搜索引擎优化,是指通过总结搜索引擎的排名规律,对网站进行合理优化,使目标网站在搜索引擎(如Google和百度)的排名提高,从而获得更多的用户访问量。搜索引擎优化技术的出现与搜索引擎的广泛应用密不可分。现在人们要从网上查找某些信息时,通常都会通过搜索引擎针对某关键字进行搜索,然后再从搜索引擎的搜索结果中点击链接进入相应网站。
由于互联网的海量信息量,对于一个关键字的搜索通常会有庞大的搜索结果,搜索引擎以分页的方式显示这些结果。显然列在第一页的搜索结果会有最大的机率得到访问,而排列在第几十、几百、甚至几千页以后的搜索结果几乎不会被访问。例如,在Google中搜索“烤箱”,则得到获得约4 830 000条结果,若每页显示10条结果,则一共有48万页。对于一个生产烤箱的企业来说,当然希望自己厂家的网站能够显示在搜索结果第一页甚至第一条的位置,而不希望在位于搜索结果的几十页以后。
如何才能使自己的网站在搜索结果中排名靠前呢?这就需要理解和掌握搜索引擎的工作原理和对搜索结果进行排名的算法,并且以此为根据有针对性的完善自己的网站,从而提高自己网站的搜索引擎排名。
6.1.2 搜索引擎工作原理
搜索引擎按其工作方式主要分为3种,分别是全文搜索引擎(Full Text Search Engine)、目录索引类搜索引擎(Search Index/Directory)和元搜索引擎(Meta Search Engine)。
全文搜索引擎从互联网上提出各个网站和页面信息,并把这些信息处理后保存到数据库中,用户使用搜索引擎进行搜索时,从数据库中检索与用户查询条件匹配的记录,并按照一定的条件对查询结果进行排序后将结果返回给用户。全文搜索引擎是真正意义上的搜索引擎,比较典型的有国外的Google和国内的百度。本章所讲的搜索引擎优化主要针对此类搜索引擎。
目录索引虽然有搜索功能,但在严格意义上讲算不上是真正的搜索引擎,仅仅是按目录分类的网站链接列表而已。目录搜索引擎收录了互联网上存在的网站,给每个网站分配若干个标签,用户可以根据标签找到相关网站。例如,用户可以通过“编程”、“游戏”、“新闻”等标签找到相关的网站。比较典型的目录索引有雅虎、搜狐等。
元搜索引擎是建立在其他搜索引擎基础上的搜索引擎。当用户在元搜索引擎中进行查询时,元搜索引擎把查询条件传递给其他多个搜索引擎,使用其他搜索引擎进行搜索,然后将查询结果返回给用户。比较典型的元搜索引擎有MetaSearch(http://metasearch.com/)、觅搜(http://www.metasoo.com/)等。
鉴于目前主流的搜索引擎为全文搜索引擎,本章将以这种搜索引擎为例说明搜索引擎工作原理和优化技术。当用户使用搜索引擎进行搜索时,并不是实时在互联网上进行搜索,而是在一个预先整理好的网页索引数据库中进行搜索,然后将搜索结果排序并显示给用户。搜索引擎的工作原理包含3个步骤:从互联网上抓取网页;建立索引数据库;在索引数据库中搜索排序。
1. 抓取网页
页面抓取是指从互联网上不断访问页面并取回页面内容的过程,这一工作是搜索引擎最基础的工作。互联网上的页面是由HTML文件组成的,这些HTML文件相互链接,形成一个网状结构。搜索引擎的页面抓取程序通过这些链接从一个页面到达另外一个页面,好像一个蜘蛛在页面组成的网上爬来爬去,因此搜索引擎的页面抓取程序通常被形象的称为“蜘蛛(Spider)”或者“爬虫(Crawler)”。
2. 建立索引数据库
搜索引擎蜘蛛把网页数据抓取回来以后,需要对这些内容进行分析整理,提取相关网页信息(包括网页所在URL、编码类型、页面内容包含的关键词、关键词位置、生成时间、与其他网页的链接关系等),根据一定的相关度算法进行大量复杂计算,得到每一个网页针对页面内容中及超链中每一个关键词的相关度(或重要性),然后用这些相关信息保存到一个特定结构的索引数据库中。
3. 搜索和排序
当用户输入关键词搜索后,由搜索系统程序从网页索引数据库中找到符合该关键词的所有相关网页。因为所有相关网页针对该关键词的相关度早已算好,所以只需按照现成的相关度数值排序,相关度越高,排名越靠前。最后,由页面生成系统将搜索结果的链接地址和页面内容摘要等内容组织起来返回给用户。
搜索引擎蜘蛛一般要定期重新访问所有网页(各搜索引擎的周期不同,可能是几天、几周或几个月,也可能对不同重要性的网页有不同的更新频率),更新网页索引数据库,以反映出网页内容的更新情况,增加新的网页信息,去除死链接,并根据网页内容和链接关系的变化重新排序。这样,网页的具体内容和变化情况就会反映到用户查询的结果中。
6.1.3 搜索引擎排名因素
搜索引擎排名算法非常复杂,要综合考虑许多因素形成对一个页面的评价。本节将介绍影响搜索引擎排名的主要因素,搜索引擎优化就是针对这些因素完善页面,使搜索引擎对页面的评价增加,从而提高在搜索结果中的排名。影响一个页面在搜索引擎中排名的因素可以分为两大类:页面内部因素和页面外部因素。页面内部因素包括以下内容:
1. 页面标题
页面标题即HTML文档中
标记中的内容。页面标题应该明确说明页面的内容,最好包含搜索关键字。给页面指定一个合适的标题能够大大提高搜索引擎对页面的评价值。页面标题应该与页面内容密切相关,不能使用太笼统的标题,更不能使标题与内容无关。一个网站中要避免所有页面具有完全相同的标题。
2. 正文标题
此处使用正文标题是为了与页面标题相区别。页面标题是指HTML文档中标记中的内容,在浏览器中显示于浏览器窗口标题栏。而正文标题是指HTML文档中、等标记内容。通常这些标题说明了页面的主要内容和大纲,对搜索引擎来说,这些标题具有比正文更大的权重。如果页面的正文标题中包含与搜索关键字密切相关的内容,则搜索引擎对此页面的评价更高。
3. 页面内容
页面正文中应该包含与被搜索关键字相关的文字和图片。页面中出现搜索关键字的密度越高,则说明页面与被搜索内容相关性越高,从而具有更好的搜索引擎评价。对于页面中的图片要设置其替换文本(即img标记中的alt属性)。替换文本的作用主要有两个,一方面是当图片无法加载时可以显示替换文本作为说明性文字,另一方面是由于搜索引擎目前无法识别图片内容,只能根据替换文本来判断图片内容。
影响一个页面搜索引擎排名的外部因素主要包括到此页面的外部链接,包括链接的数量、相关性、链接文本等。一个页面被越多外部链接引用,就说明这个页面越重要,搜索引擎对其评价也就越高。链接数量只是一个因素,不同的链接其质量也不同。例如,一个来自著名网站的链接和一个来自不知名网站的链接其价值是不一样的。对于外部链接还应考虑其相关性,例如,如果页面a是一个讲编程知识的页面,那么在链接到页面a的所有链接中,如果大部分都来自编程相关的页面,则认为这些链接质量较好,而来自于其他无关行业页面的链接质量就不好。
综上所述,如果一个页面具有明确的与内容相关的标题、页面内容与内容密切相关、页面拥有高质量的外部链接,那么这个页面在搜索引擎中就具有较好的排名。页面标题、内容、外部链接也是搜索引擎优化的主要方向,通过合理的设置页面标题和内容,想办法让其他页面引用自己的页面,就可以提高自己页面的搜索引擎排名。
6.1.4 SEO作弊
如果所述,通过设置合适页面标题、页面文本、图片替换文本、页面链接等,可以提高页面的搜索引擎排名。但是如果滥用这些因素,甚至做假试图欺骗搜索引擎,不但达不到预想的目的,而且还会受到搜索引擎的惩罚,被降低排名甚至从搜索引擎结果中除名。在做搜索引擎优化时,需要了解这些作弊手段,以避免无意中使用受到搜索引擎惩罚。搜索引擎优化常用作弊手段主要有以下几种。
1. 关键字堆砌
为了增加关键词的出现频次,故意在网页代码中,如在meta、title、注释、图片alt等地方重复书写某关键词的行为。下面的页面代码是一个关键字堆砌的例子,此页面为了提高ASP.NET、C#、案例、教程等关键字搜索排名,在页面中添加了许多关键字,而这些关键字仅仅是堆砌而已,不是出现在正文的句子中,这是关键字堆砌作弊。
ASP.NET案例教程
ASP.NET C# jQuery Web Service SQL Server 编程 项目 案例 教程
ASP.NET C# jQuery Web Service SQL Server 编程 项目 案例 教程
ASP.NET C# jQuery Web Service SQL Server 编程 项目 案例 教程
ASP.NET C# jQuery Web Service SQL Server 编程 项目 案例 教程
ASP.NET C# jQuery Web Service SQL Server 编程 项目 案例 教程
2. 虚假关键词
通过在meta或title中设置与网站内容无关的关键词,如设置热门关键词,以达到误导用户进入网站的目的。同样的情况也包括链接关键词与实际内容不符的情况。
HTML文档中的元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词。meta标签必须位于文档的头部,不包含任何内容。meta标签的属性定义了与文档相关联的名称/值对,meta标签与搜索引擎相关的属性主要有以下两个。
Meta元素中的内容对于用户来说是不可见的,但是却可以被搜索引擎访问到。如上代码所示,通过在meta标记中添加适当内容,可以通知搜索引擎页面此页面的关键词和主要内容。如果在meta中指定了某些热门关键词或内容,而页面上的实际内容与此无关,这种现象就是虚假关键词作弊。以下是一个虚假关键词的例子,例如在电影阿凡达刚上映时,网上许多人都在搜索阿凡达,为了提高页面被搜中的概率,在页面的中添加了虚假的关键词,试图欺骗搜索引擎。
页面中的真实内容,其实是一些其他文字,与阿凡达无关,更不能在线观看
3. 垃圾链接
“链接工厂”(也称为“大量链接机制”)指由大量网页交叉链接而构成的一个网络系统。一个站点加入“链接工厂”后,一方面可得到来自该系统中所有网页的链接,同时作为交换需要奉献自己的链接,籍此方法来提升链接得分。
4. 隐形文本和链接
为了增加关键词的出现频次,故意在网页中放一段与背景颜色相同的、包含密集关键字的文本。这样,访问网页的用户看不到,搜索引擎却能找到。类似方法还包括超小号文字、文字隐藏层等手段。隐形链接是在隐形文本的基础上在其他页面添加指向目标优化页的行为。以下是一个隐形文本作弊的例子。
ASP.NET案例教程
这一段是正文。正文结束。
ASP.NET C# jQuery Web Service SQL Server 编程 项目 案例 教程
这是asp.net
另外C#
一段案例
正文教程
。正文结束。
5. 重定向
使用刷新标记、CGI程序、Javascript或其他技术,当用户进入该页时,迅速自动跳转到另一个网页。重定向使搜索引擎与用户访问到不同的网页。下面是一个重写向的例子,这个页面中包含针对搜索引擎进行了优化的页面,这个页面仅被搜索引擎搜索到,不能被用户看到,访问此页面的用户被重定向到另外的页面。
这是页面正文,是给搜索引擎看的,用户不会看到。
6. 复制站点或内容
通过复制整个网站或部分网页内容并分配以不同域名和服务器,以此欺骗搜索引擎对同一站点或同一页面进行多次索引的行为。一个最典型例子是镜像站点。
7. 门页
门页也称桥页,针对某一关键词专门制作一个优化的页面,链接指向或重定向到目标页面。有时候为动态页面建立静态入口,或为不同的关键词建立不同内页也会用到类似方法,但与门页不同的是,前者是网站实际内容所需而建立的,是访问者所需要的,而门页本身无实际内容,只针对搜索引擎作了一堆充斥了关键词的链接而已。
6.2 URL重写优化
URL是一个页面的“名片”,用户和搜索引擎都是通过URL访问页面。一个简洁、易懂、易记的URL能够使页面更加友好,也能够提高搜索引擎对页面的评价。本节将介绍URL设计的原则,并结合ASP.NET编程技术说明如何通过重写URL实现优化。
6.2.1 静态URL和动态URL
根据URL中是否包含检索字符串(即URL中问号及其后面的内容),可以将URL分成静态和动态两种类型。静态URL是指不包含检索字符串的URL,静态URL通常具有更好的可读性,也更加容易记忆,下面是两个静态URL的例子。
http://www.mysite.com/home
http://www.mycompany.com/help/aboutme.html
动态URL是指包含检索字符串的URL。这种URL在ASP.NET开发的Web项目中很常见,检索字符串通常用来给页面传递参数。以下是两个动态URL的例子。
http://www.mycompany.com/productdetail.aspx?pid=book1
http://www.mycompany.com/admin/userright.aspx?userid=admin&roleid=01
在ASP.NET开发的项目中,很多时候都需要使用同一个页面显示不同的内容,例如,在网上书店项目中,使用同一个页面BookDetail.aspx显示不同的图书信息。为了给BookDetail.aspx页面指定要显示的图书,通常需要在URL中添加一个检索字符串以指定图书编号,这样就形成了动态URL。
对于搜索引擎来说,静态URL比动态URL拥有更高的价值。在使用ASP.NET开发Web网站时,考虑到搜索引擎优化的因素,应该尽量避免使用动态URL。那么,如何处理检索字符串和静态URL之间的矛盾呢?URL重写技术很好地解决了这个问题。
6.2.2 URL重写概述
URL重写是截取传入的Web请求并自动将请求重定向到其他资源的过程。执行URL重写时,通常会检查被请求的URL,并基于URL的值将请求重定向到其他URL。例如,在进行网站重组而将/people/目录下的所有网页移动到/info/employees/目录中时,需要使用URL重写来检查Web请求是否指向了/people目录中的文件。如果请求指向/people/目录中的文件,则自动将请求重定向到/info/employees/目录中的同一文件。
利用URL重写技术,将一个静态URL重定向到一个动态URL,可以避免让用户和搜索引擎看到动态URL。例如,当用户请求BookDetail/12页面时,把这个静态URL重定向到BookDetail.aspx?id=12的页面,后者即为ASP.NET项目中的真实页面,可以通过检索字符串获得页面参数,并根据参数动态生成页面内容。而这一切对用户来说都是透明的,用户看到的是请求页面BookDetail/12,就返回了编号为12的图书信息,请求另外一个页面BookDetail/20,就返回编号为20的图书信息。
6.2.3 使用HTTP模块重写URL
在ASP.NET程序中,处理每一个HTTP请求时都会执行已经注册的HTTP模块,所以在HTTP模块中可以编写适用于所有请求的公用代码。对于重写URL来说,在得到一个请求以后,要分析这个被请求的URL,如果需要重写,则按照重写规则跳转到重写后的页面。这个任务适合在HTTP模块中实现。下面通过一个例子说明具体如何实现。
【例6-1】 简单的URL重写。
在第4章所开发的网上书店项目中,有许多动态URL,一个典型的例子是图书详情页面。当用户在图书列表中单击一本图书时,就会转到图书详情页面,并将所选择的图书编号作为检索字符串传递给图书详情页面,形成类似于BookDetail.aspx?id=10的动态URL。为了将动态URL优化成静态URL,希望使用BookDetail/10的URL访问刚才的页面,这就需要URL重写。当网上书店程序收到一个静态URL请求时,将其重写为动态URL。
(1)打开第4章所创建的网上书店项目BookShop。
(2)在项目中添加一个HTTP模块,命名为MyUrlRewriter。在HttpApplication的BeginRequest事件中,检查所请求的URL并在必要时进行重写。代码如下:
public class MyUrlRewriter : IHttpModule
{
public void Dispose()
{ }
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
string path = context.Request.Path.ToLower(); //得到请求路径
if (path.StartsWith("/bookdetail/"))
{
//静态URL形式为/bookdetail/6,其中6为图书编号,重定向动态URL
string id = path.Substring(path.LastIndexOf('/') + 1);
context.RewritePath("~/BookDetail.aspx?id=" + id, false);
}
}
}
(3)打开图书列表控件BookListControl.ascx,其中的链接地址原为动态URL,如下 代码:
">
" alt="<%#Eval("BookTitle") %>" style ="width: 100px;" />
将上述代码中的动态URL改为静态URL,代码如下:
">
" alt="<%#Eval("BookTitle") %>" style ="width: 100px;" />
(4)运行网上书店项目,从图书列表中选择一个图书查看详情,可以看到图书详情页面的URL已经变成了静态URL。运行界面如图6.1所示。
图6.1 图书详情URL重写示例1
(5)从图6.1中可以看到,页面上所有图片(包括顶部横幅、图书封面等)都不能显示了。这是由于在页面代码中使用相对路径引用图片,当URL重写后路径就发生了变化。例如,图书封面的相对路径为BookImages/1b.jpg,在未进行URL重写以前,这个图片的绝对路径为/BookImages/1b.jpg,而进行了URL重写后,图片绝对路径变为/BookDetail/BookImages/1b.jpg。为了解决这个问题,所有图片应该使用绝对路径而非相对路径。在母版中修改LOGO和横幅图片路径,如下代码所示(注意图片路径是以/开头,表示绝对路径,如果没有这个/,则为相对路径)。
同样的道理,在BookDetail.aspx页面中需要修改图书封面为绝对路径。
cover.ImageUrl = "/BookImages/" + book.bigImage;
(6)如果页面上的JavaScript和CSS使用相对路径引用,则也存在与图片相同的问题,将所有JavaScript和CSS文件引用都修改为绝对路径。
(7)再次运行项目,并查看某图书详情,可以看到所有图片都能正常显示,所有CSS样式也正常应用,如图6.2所示。
图6.2 图书详情URL重写示例2
6.2.4 处理回发
URL重写后,由于改变了页面的路径,会带来一系列的问题,例如找不到图片、外部CSS文件、外部JavaScript文件等,解决这个问题的方法是使用绝对路径引用这些文件,如例6-1中所采取的方案一样。除此以外,由于URL重写造成页面路径改变还会带来一个更加严重的问题,就是页面上的所有服务器端事件(如按钮的服务器端Click事件)都无法正常工作。在例6-1中,在图书详情页面上单击“加入购物车”按钮,则会出现如图6.3所示的错误界面。
图6.3 单击按钮后产生错误
在图6.3中,提示无法找到资源/BookDetail/BookDetail.aspx。从浏览器的地址栏中可以看到,在查看图书详情时,页面URL为/BookDetail/1(其中1为正在查看的图书编号),当单击“加入购物车”按钮后,页面地址变为/BookDetail/BookDetail.aspx?id=1。这种情况带来两个问题:首先是这个页面地址不存在,造成无法找到资源;其次是这个地址又变成了URL优化以前的动态URL,URL优化失去了作用。
之所以会再现上述情况,是由于URL重写后,浏览器端所显示的路径与服务器端真实路径不同。具体来说,ASP.NET的Web窗体会在所生成的HTML代码中自动添加一个action属性,这个属性指明了页面所要提交的地址。ASP.NET Web窗体默认为自提交,即提交到页面本身。在例6-1中,由于URL重写,浏览器端比服务器端多了一级BookDetail路径。图书详情页面在提交时,仍然提交到自身,即BookDetail.aspx,这个页面与浏览器端路径相结合,就得到了一个错误的路径/BookDetail/BookDetail.aspx,于是出现了图6.3所示的错误。
URL重写后,不仅单击按钮会造成如图6.3所示的错误,其他服务器端事件也会出现同样错误。这是因为ASP.NET服务器端事件都是提交到服务器端页面本身,然后再用页台代码进行处理。由于URL重写后页面提交的地址不对,找不到要提交的页面,就会出现如图6.3所示的错误。
由于URL的重写造成页面无法提交,从而导致服务器端任何事件都不能工作,这是一个很严重的问题。解决这个问题的方法是使页面提交时仍然提交到经过重写优化以后的URL。由于页面提交的地址是在页面生成的HTML文档中form元素的action属性中指定,要想修改提交地址,必须想办法修改页面所生成的HTML代码中的action属性。下面通过一个例子来说明具体实现步骤。
【例6-2】 正确处理回发。
本例演示了如何解决URL重写带来的页面回发错误,基本思想是重写form元素的action属性,将其修改为经过优化以后的URL(即浏览器端看到的URL)。这个解决方案来源于Scott Guthrie的博客,其中用到了ASP.NET控件适配器,读者可以参考原文以获得更多信息:http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp- net.aspx。
(1)打开网上书店项目。
(2)在项目中添加一个控件适配器类UrlRewriterControlAdapter和一个UrlRewriter类,这两个类协作完成HtmlForm类的自定义呈现,修改了最终生成的HTML代码中的action属性。代码如下:
public class UrlRewriterControlAdapter:ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
//使用自定义的UrlRewriter类呈现控件(此处为HtmlForm)
base.Render(new UrlRewriter(writer));
}
}
public class UrlRewriter : HtmlTextWriter
{
//构造函数
public UrlRewriter(HtmlTextWriter writer)
: base(writer)
{
this.InnerWriter = writer.InnerWriter;
}
///
/// 重写基类的WriteAttribute()方法
///
/// 要写的Attribute的名称
/// 要写入的Attribute的值
/// 是否对属性名称和值进行编码
public override void WriteAttribute(string name, string value,bool
fEncode)
{
//检查当前要写入的属性是否为action属性
if (name.ToLower() == "action")
{
var context = HttpContext.Current;
if (context.Items["ActionRewrite"] == null)
{
value = context.Request.RawUrl;
//将其重写为浏览器请求的Url
context.Items["ActionRewrite"] = true;
//设置标记,已经重写action属性
}
}
base.WriteAttribute(name, value,fEncode);
//调用基类相应方法
}
}
(3)在解决方案资源管理器中,在项目名称上右击,从弹出的快捷菜单中选择“添加”|“添加ASP.NET文件夹”|“App_Browsers”命令,添加一个App_Browsers文件夹。
(4)在App_Browsers文件夹中,添加一个浏览器文件my.browser,并在其中注册刚才所创建的控件适配器类UrlRewriterControlAdapter,代码如下:
(5)运行网上书店,转到图书详情页面,单击“加入购物车”按钮,可以看到,按钮能够正常工作,再转到查看购物车页面,可以看到商品已经被放入购物车中,从而说明这种方案成功解决了URL重写后的页面回发问题。
6.3 正则表达式与URL重写
在6.2的例子中,只实现了一个页面即BookDetail.aspx的URL重写,实现的代码简单直观,判断所请求的URL是否具有类似“/bookdetail/1”的形式,如果是,则将URL重写为动态URL的形式BookDetail.aspx?id=1,代码如下:
HttpContext context = HttpContext.Current;
string path = context.Request.Path.ToLower();
//得到请求路径
if (path.StartsWith("/bookdetail/"))
{
//静态URL形式为/bookdetail/1,其中1为图书编号,重定向动态URL
string id = path.Substring(path.LastIndexOf('/') + 1);
context.RewritePath("~/BookDetail.aspx?id=" + id, false);
}
在较大型的网站和项目中有很多页面需要重写URL,如果都采用上述方法,则每个页面都需要写一个if或者switch分支,代码很复杂。而且,每当添加一个新的需要重写的URL,都需要修改代码,增加一个if或者switch分支。这种URL重写方式不灵活,经常需要修改,不是一种好的设计。使用正则表达式可以实现更加通用的解决方案。
6.3.1 正则表达式语法
正则表达式是指一个用来描述或者匹配,一系列符合某个句法规则的字符串的单个字符串。一个正则表达式通常被称为一个模式(pattern),被广泛用于各种字符串处理中,如查找、替换、格式验证等。正则表达式定义了一个字符串模式,例如,规定了字符串中应该包含哪些字符,不能包含哪些字符,以什么字符开头和结尾,某些字符出现多少次等。正则表达式本身也是一个字符串,用一种特定语法描述模式,其是区分大小写的。组成正则表达式的元素大致有以下几种。
1. 普通字符
普通字符表示字符本身。例如,模式“abc”表示包含abc的任意字符串。
2. 转义字符
正则表达式中的转义字符与C#字符串中的转义字符含义类似。转义字符可以看成是普通字符的一种特殊形式。例如,模式“a\tb”匹配任意一个字符a后面紧跟一个水平制表符再紧跟一个字符b。
3. 元字符
元字符可以匹配一类字符,例如匹配数字、字符、空白等。常用的元字符及其含义 如下。
* […]:匹配出现于[]中的任意单个字符。
* [^…]:匹配不在[]出现的任意单个字符。
* .:匹配除换行符以外的任意单个字符。
* \w:匹配任意单个字母、数字、下划线、汉字。
* \W:匹配任意单个不属于\w模式的字符。
* \s:匹配任意单个空白字符。
* \S:匹配任意单个非空白字符。
* \d:匹配单个数字字符,相当于[0-9]。
* \D:匹配任意单个非数字字符,相当于[^0-9]。
?提示:如果要在字符串中匹配小数点“.”,则需要在模式中写成“\.”的形式,即在“.”的前面添加“\”将其转义成普通字符。如果直接使用“.”进行匹配,那么可以匹配除换行符以外的任意单个字符。
4. 开头和结尾
在定义正则表达式的模式时,有两个特殊字符^和$,其中^表示匹配字符串开头,$表示匹配字符串结尾。例如,模式^[A-Z]匹配以大写英文字符开头的字符串,模式\.$匹配以英文句号(小数点)结尾的字符串。
?提示:在正则表达式中,^字符有两种完全不同的含义。当^出现于方括号[]内时,表示“非”,匹配不出现在方括号[]中的任意字符。当^出现在方括号[]外时,表示字符串开头。
5. 重复
在正则表达式中还可以指定元素出现的次数,有以下几种方式。
* {m,n}:匹配m到n次重复(闭区间)。例如a{1,3}匹配1到3个字符a。
* {m,}:匹配大于等于m次重复。例如a{2,}匹配2个以下字符a。
* {m}:匹配正好m次重复。
* ?:匹配0到1次重复,相当于{0,1}。这种重复也称为可选,即可以出现也可不 出现。
* +:匹配1次以上重复,相当于{1,}。
* *:匹配0次以上重复,相当于{0,}。
6. 选择
正则表达式使用符号|表示“或”的关系。例如a|A表示小写字母或者大写字母a。
7. 分组和引用
正则表达式使用圆括号()将多个字符分成一组成为逻辑上的一个单位。例如,模式(\+0)?123表示括号里面的+0是可选的(其中+前面的\为转义符),而\+0?123则指0是可选的。字符串+0123和123都能够匹配这两个模式,字符串+123能够匹配第2个模式但不能匹配第1个模式。
在正则表达式中还经常需要引用前面的内容,例如,模式\d{3}可以表示一个三位数,那么如果要表示这个三位数重复2次应该如何写呢?显然不能用\d{3}\d{3},这个模式其实表示6个数字,或者说任意一个6位数。如果要表示一个3位数重复2次,那么就需要在第1个3位数后面再出现与前一个3位数完全相同的内容,也就是说,在模式中需要引用前面出现的内容。
正则表达式提供的分组和引用机制可以给模式中出现的一部分内容分组,并且在后面的模式中引用这个分组。如前所述,正则表达式使用圆括号()进行分组,还可以为分组指定一个名称,语法为在紧跟在左侧圆括号后面写上一个问号和一对包括在尖括号中的名字。这种分组称为命名分组,在模式中可以通过分组名称引用某个分组。如刚才所说的一个3位数重复出现2次可以用以下模式来描述:
(?\d{3})\k
正则表达式中的分组也可以是匿名分组。如果在分组的左侧圆括号后面没有写问号、尖括号和组名,则这个分组为匿名分组。引用匿名分组的方法是使用分组序号,整个模式中第1个分组的序号为1,第2个分组的序号为2,依次类推。可以通过\1的方式引用第1个分组。例如,刚才所说的3位数重复出现2次的例子也可以用以下模式来描述:
(\d{3})\1
6.3.2 正则表达式验证
正则表达式有很多用途,其中的一个常用功能就是验证,即验证某些文本是否符合某种模式。在.NET Framework中,使用System.Text.RegularExpressions命名空间中的Regex类表示一个正则表达式,通过在Regex类的构造函数中传递一个字符串参数指定正则表达式模式,代码如下:
Regex r = new Regex(@"\d{6}");
Regex类的Match方法执行匹配并返回一个Match类型的结果,Match类的Success属性表示匹配是否成功,Match类的Value属性表示匹配成功的文本,Match类的NextMatch方法执行下一次匹配并返回匹配结果。下面是一个简单的例子,查找字符串中出现的数字并输出。
Regex r = new Regex(@"\d+"); //创建正则表达式
Match m = r.Match("123aa45bb7cc89"); //执行第一次匹配
while (m.Success) //当匹配成功时循环
{
Console.WriteLine(m.Value); //输出当前匹配的文本
m=m.NextMatch(); //执行下一次匹配
}
/* 输出结果为:
123
45
7
89 */
如果读者仔细观察上面的例子,就会发现一个问题:正则表达式的模式为\d+,表示匹配一到多个连续数字,如果目标字符串中包含多个连续数字,那么这个模式是只匹配第1个数字还是匹配全部数字呢?根据\d+模式的含义,匹配1个或者2个或者更多直到全部数字都是正确的,但是在实际执行时必须返回一个匹配结果。应该返回最小的匹配结果还是最大的匹配结果,这就是所谓的“贪婪”问题。
在正则表达式的重复模式中,默认使用贪婪方式进行匹配,就是说,要匹配尽可能多的次数。例如在前面的例子中,\d+总是匹配最多的连续数字。但是贪婪匹配也不总是最贪婪的,也要考虑到后面其他内容的匹配,考虑下面的例子。
Regex r = new Regex(@"\w+end$");
Match m = r.Match("begin_to_end");
if (m.Success)
{
Console.WriteLine("匹配成功,贪婪匹配并不是无限制的贪婪");
}
else
Console.WriteLine("贪婪匹配太贪婪,把@也匹配了,导致整个正则表达式匹配失败");
在上面这段代码中,第一行定义了一个模式用来以end结尾文字,模式中的\w+可以匹配1到多个字母、数字、下划线、汉字,当然也可以匹配end。如果贪婪匹配把end也匹配到\w+中,那么代码中定义的\w+end$就是永远不可满足的。而上面的代码运行的结果却显示匹配成功,说明贪婪匹配在最大限度的匹配更多字符时,也要受其后模式的限制,或者说,贪婪匹配是在满足整个模式匹配成功的基础上,去匹配尽可能多的重复次数。
与贪婪相反的另外一种模式匹配称为非贪婪匹配,非贪婪匹配总是尽可能匹配最少的重复次数。通过在重复次数后面添加一个问号指定该重复使用非贪婪匹配,如下例所示。
Regex r = new Regex(@"\d+?"); //使用非贪婪匹配
Match m = r.Match("123aa45bb7cc89"); //执行第一次匹配
while (m.Success) //当匹配成功时循环
{
Console.WriteLine(m.Value); //输出当前匹配的文本
m=m.NextMatch(); //执行下一次匹配
}
上面的代码运行时,会输出九行分别显示1到9,说明每次\d+?模式只匹配了一个数字,即满足模式的最小重复次数。与贪婪匹配类似,非贪婪匹配也要受整体模式匹配的限制,为了使整个模式匹配成功,非贪婪模式在必要时也会匹配更多重复次数。
使用Regex进行字符串模式匹配时,默认情况是区分大小写,如下代码会匹配失败。
Regex r = new Regex(@"end$"); //此模式要求以end结尾
Match m = r.Match("TheEnd"); //待检验文本以End结尾
if (m.Success)
{
Console.WriteLine("匹配成功");
}
else
Console.WriteLine("匹配失败"); //由于大小写不一致匹配失败
如果要使Regex忽略字符大小写,则需要指定一个选项。Regex类有一个Options属性,这是一个RegexOptions枚举,描述了正则表达式匹配时的一些选项,如是否区分大小写、是否从右向左查找匹配、是否执行多行模式等。如果要忽略字母大小写,可以在Regex构造函数中传递一个RegexOptions.IgnoreCase值,代码如下:
Regex r1 = new Regex("end$", RegexOptions.IgnoreCase);
下面通过一个综合性的例子演示如何通过Regex验证常用的字符串模式。
【例6-3】 正则表达式验证。
本例演示使用Regex验证常用的字符串模式。
(1)创建一个控制台应用程序RegexSample。
(2)在项目中添加一个类RegexTest用以测试正则表达式,类的代码如下:
//测试Regex的类
class RegexTest
{
//测试数据类
class RegexTestData
{
public string pattern; //正则表达式模式
public string description; //模式的说明
}
public void test()
{
List list = new List();
//中国邮箱由6位数字组成
RegexTestData test = new RegexTestData()
{
pattern = "^[0-9]{6}$",
description = "中国邮政编码"
};
list.Add(test);
//中国电话号码: 3~4位区号 + 横线- + 7~8位市话号码
test = new RegexTestData()
{
pattern = @"^(\d{3}-|\d{4}-)?\d{8}|\d{7}$",
description = "中国电话号码"
};
list.Add(test);
//URL地址:多个字母开头,后跟://, 然后是多个非空白字符
test = new RegexTestData()
{
pattern=@"[a-zA-z]+://[^\s] *",
description="URL地址"
};
list.Add(test);
/* 电子邮箱地址模式分析:
* 1到多个字母数字下划线开头, 中间可以有-+.符号, 必须有@符号
* @号后面的内容被.分成两部分,每一部分的模式与@号前面的模式基本相同*/
test = new RegexTestData()
{
pattern=@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*",
description="电子邮件地址"
};
list.Add(test);
for (int i = 0; i < list.Count; i++)
{
Console.Write("请输入一个{0}:",list[i].description);
string s = Console.ReadLine();
testMatch(s, list[i].pattern);
}
}
///
/// 测试正则表达式是否匹配
///
/// 要测试的文本
/// 用来测试的模式
private void testMatch (string text,string pattern)
{
Regex r = new Regex(pattern);
Match m = r.Match(text);
if (m.Success)
Console.WriteLine("匹配成功,结果为:"+m.Value);
//匹配成功则输出匹配结果
else
Console.WriteLine("匹配失败");
}
}
(3)在Main()方法中调用以上测试方法。
static void Main(string[] args)
{
new RegexTest().test();
Console.ReadKey();
}
(4)运行此程序,运行结果如下:
请输入一个中国邮政编码:100123
匹配成功,结果为:100123
请输入一个中国电话号码:021-12345678
匹配成功,结果为:021-12345678
请输入一个URL地址:http://test.com
匹配成功,结果为:http://test.com
请输入一个电子邮件地址:my-this@163.com
匹配成功,结果为:my-this@163.com
6.3.3 正则表达式查找和替换
正则表达式可以实现复杂的字符串查找和替换。在.NET Framework中,使用正则表达式进行字符串查找时,除了Regex类以外,还需要用到Capture、Group和Match类。Capture类表示一个捕获结果有3个重要属性:Index表示捕获的字符串在整个字符串中的位置,Length表示所捕获字符串的长度,Value表示所捕获字符串的值。Group类表示模式匹配中的一个组,Group类从Capture类继承。Match类表示一次匹配,Match类从Group类继承。Group类有一个CaptureCollection类型的Captures属性,表示这一组所有捕获的集合。Match类有一个GroupCollection类型的Groups属性,表示这个匹配的所有组的集合。这几个类之间的关系如图6.4所示。
图6.4 正则表达式类关系图
Capture、Group、Match这几个类之间的关系较为复杂,既有继承关系又有集合关系。通过正确理解并合理使用这些类,可以完成复杂的字符串操作。下面通过两个例子来演示这几个类的关系及应用。
【例6-4】 捕获、组和匹配。
本例演示捕获Capture、组Group和匹配Match类的应用及其关系。
(1)创建一个控制台应用程序,在Main()方法中编写以下代码。
static void Main()
{
int count;
Regex regex = new Regex("(123)+"); //匹配一到多个123
string text = "abc123123123abc123123abc"; //要查找的字符串
Match match = regex.Match(text); //执行一次匹配
while (match.Success) //若匹配成功则循环
{
Console.WriteLine("找到一个新的匹配");
GroupCollection groups = match.Groups;
//获得此次匹配组的集合
Console.WriteLine("该匹配共有{0}组",groups.Count);
for (int i = 0; i < groups.Count; i++)
{
Group g = groups[i];
Console.WriteLine("\t新的一组");
CaptureCollection captures = g.Captures;
//获得当前组的所有捕获
count = captures.Count;
Console.WriteLine("\t该组中共有{0}个捕获", count);
for (int j = 0; j < count; j++)
{
Capture c = captures[j];
//显示一个捕获的详细信息
Console.WriteLine("\t\t捕获详情:位置:{0},文本:{1}",c.Index,
c.Value);
}
}
match = match.NextMatch(); //尝试下一个匹配
}
}
(2)运行上述程序,输出结果如下:
找到一个新的匹配
该匹配共有2组
新的一组
该组中共有1个捕获
捕获详情:位置:3,文本:123123123
新的一组
该组中共有3个捕获
捕获详情:位置:3,文本:123
捕获详情:位置:6,文本:123
捕获详情:位置:9,文本:123
找到一个新的匹配
该匹配共有2组
新的一组
该组中共有1个捕获
捕获详情:位置:15,文本:123123
新的一组
该组中共有2个捕获
捕获详情:位置:15,文本:123
捕获详情:位置:18,文本:123
【例6-5】 HTML文本提取。
本例演示如何从HTML table中去除等标记,读取其中的文字。
问题分析:根据对HTML中table的格式可知,table中能够显示在浏览器中的内容就是 | 标记中的内容。因此,要提取table中的内容,只需取出 | 和 | 标记之间的文本即可。用正则表达式很容易实现这个功能。
(1)创建一个控制台应用程序,并编写如下代码。
static void getHtml() //版本1
{
//原始HTML字符串
string table = ""
+"one | two |
"
+"three | four |
"
+"
";
Regex regex = new Regex("(.)+ | ");
//正则表达式
Match match = regex.Match(table);
while (match.Success)
{
Console.WriteLine(match.Value);
match = match.NextMatch();
}
}
(2)运行上述代码,但是并没有得到期望的输出,而是输出以下内容:
one | two |
three | four |
观察上述输出结果,可以看出匹配的整个字符串的第一个和最后一个 | 之间的内容,这是由于贪婪匹配造成的。正则表达式默认使用贪婪匹配,总是匹配尽可能多的字符,而定义的模式为以开头、以 | 结尾的字符串,按照贪婪匹配原则,就匹配了最开始的和最末尾的 | 。
(3)为了解决贪婪匹配带来的问题,正确取出和 | 之间的内容,需要使用非贪婪匹配,对前面的代码做以下修改(只给出修改部分)。
Regex regex = new Regex("(?.+?) | "); //使用非贪婪匹配,命名分组
Match match = regex.Match(table);
while (match.Success)
{
Console.WriteLine(match.Groups["g1"].Value); //输出命名分组中的内容
match = match.NextMatch();
}
(4)再次运行程序,这次输出了正确结果如下:
one
two
three
four
在处理字符串时,除查找功能外,也经常用到替换功能。例如,在URL搜索引擎优化中,需要把一个动态URL重写为静态URL,从动态URL到静态URL的转换就是一个字符串替换的过程。Regex类提供了一个Replace()方法可以实现字符串替换,方法签名如下。
public string Replace(string input,string replacement);
Regex类Replace()方法的功能在指定的输入字符串内,使用指定的替换字符串替换与某个正则表达式模式匹配的所有字符串。该方法第一个参数input为要替换的原始字符串,replacement参数为替换字符串,方法返回替换以后得到的字符串。例如,下面的代码把字符串中的多个连续空格替换为一个空格。
Regex regex = new Regex(@"\s+");
string text = "this is the original text";
string newText=regex.Replace(text, " ");
在字符串替换时,有时候需要用前面出现的字符串来替换后面的某些字符串,例如,英语中姓名有两种写法,名在前或者姓在前,当姓在前名在后时需要在姓和名之间添加一个逗号。例如,比尔盖茨的名字可以写作Bill Gates或者Gates, Bill都可以。如果要把一个人的英文名字从名在前的格式替换为姓在前的格式,则可以使用以下代码实现。
string input="Sun Ji-Lei";
//定义模式(其中包含两个命名分组)
Regex regex=new Regex(@"(?\S+) (?\S+)");
//执行替换,使用{$组名}方式引用原始字符串中的分组
string output = regex.Replace(input,@"${last},${first}");上述代码
6.3.4 正则表达式URL重写
使用正则表达式强大的匹配替换功能,可以实现更加灵活通用的URL重写。其基本原理也是使用HTTP模块捕获所有访问请求,将请求的URL(通常是静态URL)按照一定的模式进行替换,得到一个新的URL(通常是动态URL),然后访问这个重写后的URL。
根据以上理论,如果要自己编码实现利用正则表达式重写URL,需要在例6-1的基础上,在HTTP模块中定义重写URL所使用的正则表达式,并使用这些正则表达式对静态URL进行替换即可。目前网络上也有几个开源的URL重写工具,如UrlRewriter.NET(网址http://urlrewriter.net/)、UrlRewritingNet(网址http://www.urlrewriting.net/149/en/home.html)等,都支持正则表达式。这两个URL重写工具都使用配置文件定义重写的正则表达式。下面以UrlRewriter.Net为例说明如何实现URL重写。
【例6-6】 UrlRewriter.Net实现URL重写。
假设有一个面向全球各个国家的销售网站,使用多种语言向客户展示商品信息。网站根目录下有一个Detail.aspx页面,用来显示商品信息。访问此页面时需要指定两个参数:商品编号和界面语言。动态URL使用检索字符串传递参数,具有类似这种形式:Detail.aspx?language=en&id=10,其中en表示语言为英语,10表示要查看的商品编号为10。为了实现URL优化,希望用户使用静态URL访问这个页面,静态URL具有以下形式:/en/Detail10.aspx。本例演示如何使用UrlRewriter.NET实现这个功能。
(1)在ASP.NET项目中添加对Intelligencia.UrlRewriter程序集的引用。
(2)在web.config文件的configSection节中添加以下代码,这些代码的作用是告诉UrlRewriter.NET从web.config文件的rewriter结点中读取配置信息。
(3)在web.config文件中注册UrlRewriter.NET所使用的HTTP模块。
(4)在web.config文件中用添加用正则表达式描述的URL重写规则。
(5)为了测试URL重写效果,在项目中添加一个页面Detail.aspx。出于演示目的,本页并不根据参数显示不同的语言界面和产品信息,而只是在页面上显示从QueryString获得的语言和产品编号参数。
(6)在Detail.aspx.cs文件中,在Page_Load事件中显示当前的语言和产品编号。
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
product.Text = Request.QueryString["id"];
language.Text = Request.QueryString["language"];
}
}
(7)在网站中添加一个HTML页面,页面中放置几个超链接,以打开产品详情页面。
en,产品01
中文,产品p10
(8)运行网站,运行结果如图6.5所示。
图6.5 URL重写效果
6.4 页面内容优化
对于搜索引擎优化而言,URL优化仅是较简单的一步,更重要的还是要对页面内容进行优化。页面内容优化要比URL优化更加复杂,本节将介绍页面内容优化的主要原则和 方法。
6.4.1 页面代码优化
页面代码优化是指通过合理编写页面代码(包括HTML、JavaScript等),达到突出页面主题和主要内容,减少页面文件大小的目的。页面代码的优化主要包括以下几个方面。
1. 合理使用关键字
搜索引擎检索页面信息时,根据页面内容来判断页面是否与所搜索的关键字匹配,关键字在页面上出现的次数越多,一般来说就意味着页面与被搜索关键字相关程度越高。当然,如本章6.1节所述,关键字的出现必须是合理的,如果堆砌关键字就成为搜索引擎作弊,会受到搜索引擎的惩罚。
关键字应该在页面代码的部分和部分都出现。在部分,可以在中为页面指定一个包含关键字的标题,在中指定页面的描述信息和关键词。在部分,即页面显示的正文中,也要注意让关键字合理出现。例如,下面是一个介绍ASP.NET编程案例的页面,较好地使用关键字说明了页面的主题。
ASP.NET项目案例
ASP.NET项目教程之案例篇
…
2. 消除冗余代码
当使用所见即所得的可视化开发工具(如Visual Studio、Dreamweaver等)编辑页面时,会产生一些冗余代码。页面中包含这些代码不但没有任何作用,反而增加页面大小,扰乱页面结构。例如当使用Visual Studio插入和编辑表格后,会产生许多空格字符和样式信息,如下代码所示。
上述代码中,标记中的 是多余的,从第2个 | 开始,其中的class="style1"也是多余的。把这些冗余代码删除以后,一方面减小了页面字节数,另一方面使页面更加清晰易读。
3. 突出重点内容
无论对于搜索引擎还是用户而言,页面中的某些内容显得比其他内容更为重要,例如,页面中的标题(在各级标记中的内容)、页面上用粗体显示的字体等。当人们在浏览页面时,这些元素会受到更多重视,搜索引擎也模拟人的这一行为,认为这些元素更能说明页面内容。根据这一点,应该合理使用标题和加粗字体,突出页面主题,而不能整个页面都使用一样的文本格式。
4. JavaScript导航优化
搜索引擎是通过页面之间的链接对整个互联网进行搜索的。如果一个页面没有任何外部链接,那么这个页面不可能被搜索引擎蜘蛛访问到,更不可能被检索收录。目前,搜索引擎只识别标记为链接标记,根据href属性访问到所指向的页面。在设计网站时,大多数链接都是通过实现的,但是也存在例外情况,最常见的就是有时候需要用JavaScript打开一个窗口,如下代码所示。
ASP.NET项目案例
对于搜索引擎的蜘蛛程序来说,认为上述代码中标记没有目标链接页面,从而无法访问到mypage.aspx。可是如果直接把页面地址写到href属性中,又无法实现弹出窗口的效果。这时可以采用一个小技巧,就是一方面在href属性中包含要链接的真实地址,这样搜索引擎就能访问到目标页面,别一方面在onclick事件中用window.open打开目标窗口,然后返回false,以避免打开元素的目标地址,如下代码所示。
ASP.NET项目案例
6.4.2 消除重复内容
如本章第1节所述,如果2个或多个页面包含基本相同的内容,会降低搜索引擎对页面的评价。由于各种原因,有些重复页面是不可避免的,最常见的情况就是打印机友好(print friendly)页面。
大多数的网页设计是为了在计算机屏幕上观看的。然而,有的时候用户需要将某些页面打印出来,也许就是为了保留一个长期的记录,或者将其用作方便的离线参考资料。但随之而来的问题是,显示器是彩色的,而大多数打印机都是黑白的,在显示器上看起来引人注目和五彩缤纷的很多特性,都无法在打印机上表现出相同的效果。在被降级为灰度打印的时候,彩色的组合会失去原有的对比效果;图形会看起来失真,而且耗费太长的打印时间;在Web页面上起着重要作用的导航按钮在打印页面上也毫无用处。
为了克服这些问题,网页开发人员常常会为页面专门设计一个打印机友好的版本。打印机友好的版本通常都包括有和主要 Web页面相同的内容,但是会省略掉大多数的图形、背景和导航元素。页面还会把彩色转换成某种形式,以便生成能够让人接受的灰度图像。
打印机友好页面很好的解决了显示和打印页面的矛盾,但是却在搜索引擎优化方面带来了另外一个问题,就是页面内容重复。如前所述,显示页面和打印机友好页面上的内容几乎是相同的,只是显示样式和图片不同。从搜索引擎优化的角度来说,希望搜索引擎只能看到其中的一个页面,通常是显示页面。为了达到这个目的,可以在页面中添加一个标记以拒绝搜索引擎对此页面的索引,如下代码所示。
上述代码中的robots表示这个内容是针对搜索引擎蜘蛛程序的,noindex表示不索引当前页面内容,nofollow表示不跟踪当前页面中的链接。
6.5 小 结
作为一个ASP.NET开发人员,需要掌握基本的搜索引擎优化知识,使开发的网站具有较好的搜索引擎评价。本章介绍了搜索引擎优化的基础知识,先介绍了搜索引擎优化的原理和误区,然后重点介绍了两种搜索引擎优化手段:URL优化和页面内容优化。
第1篇 ASP.NET网络开发关键技术
第6章 搜索引擎优化
·268·
·267·