整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

利用IE法提取网页数据基础

#34;VBA信息获取与处理"教程中第八个专题"VBA与HTML文档"的第七节"HTML DOM的对象事件及关联"太枯燥了,希望想掌握这方面知识的朋友能参考我的教程学习。我们今天 开始第九个专题的学习"利用IE抓取网络数据"。

我们的网抓部分在讲解了XMLHTTP方法后,利用两个专题的进度进行了一些和VBA关系不是很大的有关网络知识的讲解,这两个专题对于我们重新认识网抓数据有着非常重要的意义,虽然我的讲解还不能面面俱到,但对于我经常倡导的VBA定位来说,是足够的,再者,学习是个不断积累前进的过程,要掌握的是一些基本的理论,然后把这些应用到自己的实际中去,这才是关键。从这个专题开始我们继续网抓的学习。这个专题是利用IE抓取网络数据。其实就是利用控件来完成我们的工作。


第一节 利用IE法提取网页数据基础

为了获取网页的数据,我们可以通过创建IE控件或webbrowser控件,结合htmlfile对象的方法和属性,模拟浏览器操作,获取浏览器页面的数据。

这种方法可以模拟大部分的浏览器操作。浏览器能看到的数据就能用代码获取,但是有个致命的缺点:除去各种弹窗相当烦人外,兼容性也确实是个很伤脑筋的问题。在我自己的实践中感觉这种方法不是很稳定(仅仅是感觉)。

1 IE模型的创建

我们在实际工作中遇到网站和网页相关问题,例如:如何下载网页数据?网页之间的通讯是怎么实现的、它们能不能被控制等等。如果你是用VB/VBA/脚本或其它支持自动化对象(AUTOMATION)的语言编程,有一个值得了解的方法是掌握对象模型:将网页视为对象来控制,这个方法需要了解的是IE的自动化对象(InternetExplorer.Application)或IE控件(Microsoft Internet Controls),以及标准的文档对象模型(Document)。相关的知识我在前两个专题中做了大量的讲解,这里就不再详细的说明了。

我给出下面的代码:

Set ie = CreateObject("InternetExplorer.Application") '创建对象

ie.Visible = True '使IE页面可见,做完这一步,在VBA之外可以看到一个新的IE

ie.navigate "about:blank" '建立一个空白页

上面这几行代码的作用是创建一个IE应用程序对象,并打开一个空白的网页。这个网页独立于VBA的应用程序(WORD或EXCEL)之外,事实上,你必须自已关掉它,或者用ie.Quit下令退出——注意一下,单纯的关闭VBA或SET ie=nothing是不会退出这个网页的。我们经常用的是将第3行的字符串替换成一个网站的名字,或者替换成一个你主机中的文档名,也可以是一个图片名,都是可以的。和你在IE地址栏输入名称浏览这些文档是一样效果。

如果仅仅是创建了一个空的模型是没有任何利用的价值的,我们需要真正的网页,这时就需要我们在VBA的应用程序外打开一个完整的网页了,直到网页完全加载我们的操作才能向下进行。

2 IE网页页面的加载

我们修正一下上面的那段打开空网页的代码:

Sub mynz()

Set ie = CreateObject("InternetExplorer.Application") '创建对象

ie.Visible = True '使IE页面可见,做完这一步,在VBA之外可以看到一个新的IE

ie.navigate " https://baijiahao.baidu.com" '建立一个空白页

Do Until .ReadyState = 4 '检查网页是否加载完毕(4表示完全加载)

DoEvents '循环中交回工作权限给系统,以免"软死机"

Loop

End sub

在上面的代码中增加了几行:

Do Until .ReadyState = 4 '检查网页是否加载完毕(4表示完全加载)

DoEvents '循环中交回工作权限给系统,以免"软死机"

Loop

这几行代码可以保证网页的加载完成,这是根据ie.ReadyState的返回值来判断的。


readyState一共有5中状态:

状态 含义 说明

0 未初始化 对象已建立,但是尚未初始化(尚未调用open方法)

1 初始化 对象已建立,尚未调用send方法

2 发送数据 send()方法已调用,但是当前的状态及http头未知

3 数据传送中 已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误

4 数据接收完毕 此时可以通过通过responseBody和responseText获取完整的回应数据


通过以上的分析,我们可以看出,只用当.ReadyState = 4时网页的数据才是有效的数据。

3 IE页面数据的获得

当网页加载完毕,剩下的工作就是从网页中抓取数据了,数据的抓取主要是利用控件对象的属性和方法。


1)用Set doc = ie.Document 取得网页的文档对象

从文档对象(Document)以下展开的对象模型,它代表网页的内容,和前面那个IE的应用程序不是同一个体系.

Documnet(文档)是文档对象模型,相当于OFFICE对象中的APPLICATION,取得Document之后,不论修改网页还是读写网页,还是触发事件,一切都好说,每个URL都对应有一个Documnet(这是假如定成功导航Navigate到那个URL完成,因此之前要求确定IE对象READSTATE,以确定对应URL的Document打开了)


2) 在Documnet之下可以取得documentElement和body两个节点。


可以用下面的语句:

set xbody=doc.Body '取得body对象

set xDoc=doc. documentElement '取得根节点

body前面已经说过,相当于标记的对象,根节点相当于网页中的标记元素的对象,MHTML的类型库定义里,它们都属于HTMLHtmlElement类型的对象,下面我把这种类型的对象称为一个"节点",不过要注意的是文档对象不是节点对象,它是HTMLDocument类型。根节点和body节点不同的是根节点包括整个网页,在HTML的文档对象模型中,这类对象有几种属性可以取得其中的内容:

对象.innerHtml '对象内部的HTML文本

对象.OuterHtml '对象中的HTML文本,包括对象本身的HTML标记在内

对象.innerText '对象内部的TEXT,不包括HTML标记

对象.OuterText '同上,包括对象本身的文本

所以,如果我们要抓取某个网站的所有HTML内容,代码可以这样写:

set doc=ie.Document

set xDoc=doc. documentElement '取得根节点

strX=xDoc.OuterHtml '取得所有的HTML内容


3) 每一个标记节点对象之下都有一个名为ChildNodes的集合,它包含了"直属于本节点下的标记",就象是文件目录,根目录下的子目录.

我们可以看到:HTML标记是文档的根节点,是Document的Childnodes集合中的一个成员(Document不是节点,是另一种类型对象,上一级文档,但它可以有下级节点集合,正如磁盘可以有下级目录,但它本身不是目录),BODY是根节点的ChildNodes集合中的一个成员,而DIV和P两个节点则是BODY的ChildNodes集合中的两个成员,同样也有自已的Childnoes集合。

我们要注意:文档对象模型中,集合与OFFICE的集合有所不同,集合是从0开始计数的,计数属性是Length而不是Count。


4)除了ChildNodes集合,大家在网页文档对象中还常见到的就是很大气的一种集合:All集合,这是"最糊涂"的一种集合,文档和各级节点都带有这个集合,正如这个名字所示,它是不分层次的,但用起来也很方便:

Set doc=ie.Document

Set xCols=doc.All '取得文档中的所有节点集合

Set xbCols=doc.body.All '取得body节点下所有的节点集合

虽然任何标记节点都有ALL集合,但我们还是喜欢用DOCUMENT的ALL,原因无它,文档最大,一锅烩的ALL找起来也最合适。ALL查找是有条件的:如果这个标记没有ID,你无法查到它的名字。


不过,ALL集合有一个很方便的特性:ID可以挂到ALL集合之下:

strX=doc.All.mytag.innerhtml


5)获得文档对象的getElementsByName集合,可以利用下面的方法:

set mydivs=doc. getElementsByName("div") '取得所有DIV标记,注意还是集合


6) 文档对象的FORMS集合,因为大部分网页的数据提交都是通过FORM标记提交的:

Set myForms=doc.Forms '取得所有的FORM标记

Set frmX=myForms.item(0) '第1个FORM

FORM标记节点代表的对象是很多朋友关心的内容——在网页对象中,它可以发送数据到服务器,使服务器刷新网页(实际上是服务器按某个格式约定发回数据),我们可以把网页的FORM看成是一个远程的函数调用接口,FORM标记中的ACTION指向的URL地址就是函数入口,而FORM标记内的各个INPUT标记节点就是函数的参数,当发出FORM.Submit方法时,就是远程调用函数了,在服务器端,诸如ASP,PHP就是老老实实找FORM的参数,不管你是用GET还是POST:

frmX.submit '相当于用户在页面上按下FORM的发送按键


上面我罗列了获取网页数据的一般的方法,并没有什么特别的使用要求,大家可以根据自己的习惯来利用,这个专题之后的内容就是灵活运用这些知识点来解决实际问题了。



本节知识点回向:


如何提交表单?如何下载图片的地址?如何获得表的数据?


积木编程的思路内涵:

在我的系列书籍中一直在强调"搭积木"的编程思路,这也是学习利用VBA的主要方法,特别是职场人员,更是要采用这种方案。其主要的内涵:

1 代码不要自己全部的录入。你要做的是把积木放在合适的位置然后去修正代码,一定要拷贝,从你的积木库中去拷贝,然后修正代码,把时间利用到高效的思考上。

2 建立自己的"积木库"。平时在学习过程中,把自己认为有用的代码放在一起,多积累,在用到的时候,可以随时拿来。你的积木库资料越多,你做程序的思路就会越广。

VBA的应用界定

VBA是利用Office实现个人小型办公自动化的有效手段(工具)。这是我对VBA的应用界定。在取代OFFICE新的办公软件没有到来之前,谁能在数据处理方面做到极致,谁就是王者。其中登峰至极的技能非VBA莫属!

我记得20年前自己初学VBA时,那时的资料甚少,只能看源码自己琢磨,真的很难。20年过去了,为了不让学习VBA的朋友重复我之前的经历,我根据自己多年VBA实际利用经验,推出了六部VBA专门教程:

第一套:VBA代码解决方案 是VBA中各个知识点的讲解,教程共147讲,覆盖绝大多数的VBA知识点,初学必备;

第二套:VBA数据库解决方案 数据库是数据处理的专业利器,教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法和实例操作,适合中级人员的学习。

第三套:VBA数组与字典解决方案 数组和字典是VBA的精华,字典是VBA代码水平提高的有效手段,值得深入的学习,是初级及中级人员代码精进的手段。

第四套:VBA代码解决方案之视频 是专门面向初学者的视频讲解,可以快速入门,更快的掌握这门技能。这套教程是第一套教程的视频讲解,听元音更易接受。

第五套:VBA中类的解读和利用 这是一部高级教程,讲解类的虚无与肉身的度化,类的利用虽然较少,但仔细的学习可以促进自己VBA理论的提高。这套教程的领会主要是读者的领悟了,领悟一种佛学的哲理。

第六套教程:《VBA信息获取与处理》是一部高级教程,涉及范围更广,实用性更强,面向中高级人员。教程共二十个专题,包括:跨应用程序信息获得、随机信息的利用、电子邮件的发送、VBA互联网数据抓取、VBA延时操作,剪切板应用、Split函数扩展、工作表信息与其他应用交互,FSO对象的利用、工作表及文件夹信息的获取、图形信息的获取以及定制工作表信息函数等等内容。

大家可以根据以上资料1→3→2→6→5或者是4→3→2→6→5的顺序逐渐深入的逐渐学习。教程提供讲解的同时提供了大量的积木,如需要可以WeChat: NZ9668


学习VBA是个过程,也需要经历一种枯燥的感觉

如太白诗云:众鸟高飞尽,孤云独去闲。相看两不厌,只有敬亭山。学习的过程也是修心的过程,修一个平静的心。在代码的世界中,心平静了,心情好了,身体自然而然就好。心静则正,内心里没有那么多邪知邪见,也就没有那么多妄想。利人就是利己。这些教程也是为帮助大家起航,助上我自己之力,我的上述教程是我多的经验的传递,

"水善利万物而不争",绵绵密密,微则无声,巨则汹涌。学习亦如此,知道什么是自己所需要的,不要蜷缩在一小块自认为天堂的世界里,待到暮年时再去做自欺欺人的言论。要努力提高自己,用一颗充满生机的心灵,把握现在,这才是进取。越是有意义的事情,困难会越多。愿力决定始终,智慧决定成败。不管遇到什么,都是风景。看淡纷争,看轻得失。茶,满也好,少也好,不要计较;浓也好,淡也好,其中自有值得品的味道。去感悟真实的时间,静下心,多学习,积累福报。而不是天天混日子,也不是天天熬日子。在后疫情更加严峻的存量残杀世界中,为自己的生存进行知识的储备,特别是新知识的储备。学习时微而无声,利用时则巨则汹涌。

每一分收获都是成长的记录,怎无凭,正是这种执着,成就了朝霞的灿烂。最后将一阙词送给致力于VBA学习的朋友,让大家感受一下学习过程的枯燥与执着:


浮云掠过,暗语无声,

唯有清风,惊了梦中啼莺。

望星,疏移北斗,

奈将往事雁同行。

阡陌人,昏灯明暗,

忍顾长亭。

多少VBA人,

暗夜中,悄声寻梦,盼却天明。

怎无凭!


回向学习利用VBA的历历往事,不胜感慨,谨以这些文字给大家,分享我多年工作实际经验的成果,随喜这些有用的东西,给确实需要利用VBA的同路人。


分享成果,随喜正能量

索引擎和聚合类新闻App之所以有源源不断的新内容供予用户浏览,原因就在于有网络爬虫技术的加持。网络爬虫的应用对于用户来说,是一大福利——我们可以从一个搜索引擎轻松搜索到各个领域的信息。但是,对于原创方来说,就涉及到版权被侵犯的问题了。工具理性,但不意味着操持工具的人就可以假借“工具理性”肆意侵犯他人的合法权益,网络爬虫技术的应用还应该要在法理之内。

一、初识爬虫

工作的时候,想要查找“产品设计”,可以直接在搜索引擎上输入内容,就可以直接找到数以百万计的资料。

上下班路上,刷新闻类APP的时候,只要愿意,就会有源源不断的新的信息,足够刷一路的时间。

搜索引擎和(大多数)新闻类APP都不自己生产内容(虽然有些平台孵化了自己的内容,但也只占整个平台内容的很少的一部分,更重要的是,成本非常高)。

那么,他们的大量的内容从哪里来?

“我们不生产内容,只是内容的搬运工”,将互联网上的内容“搬运”到自己的服务器上,这就是爬虫。

首先,我们需要了解一下互联网的结构。

互联网上的内容数以亿计,虽然很复杂,但说白了就是一张大网,网上的每个节点就是一个网页,连接网页的超链接(Hyperlinks)相当于线,线把所有的节点连接在一起,形成了一个复杂的网。

通过点击超链接的文字或者图片,就可以跳转到对应的网页。爬虫可以自动访问到每一个网页,并把网页的内容保存下来。

世界上第一个网络爬虫由麻省理工学院的学生马修·格雷(Matthew Gray)在1993年写成,之后的爬虫尽管越来越复杂。

比如:可以实现更快的访问速度、访问更多的网页、更好的将网站内容解析出来。但爬虫的基本原理是一样的,都主要包括三个部分:访问网页链接,下载网页内容,解析网页内容。

爬虫的工作过程与我们查找网页的过程是一样的。

比如,我们想要查一下豆瓣上最新的电影:首先,在浏览器地址栏输入网址链接https://movie.douban.com/,之后,浏览器会跳转到豆瓣电影。最后,我们就可以找到当前热映的电影。

同样的,一个最简单的爬虫三步就可以爬取一个网页——首先,访问这个网页,之后,把网页内容下载下来,最后,对下载的内容进行解析。

二、7行代码爬取豆瓣电影

最简单的爬虫三步就可以爬取一个网页,那么要写多少行代码呢?

我们写一个爬虫,爬取豆瓣的“一周口碑榜”,只要7行代码!

这里我们使用Python语言,至于为什么选择Python语言,会在后面交代清楚,如果不懂Python也没有关系,了解爬虫是如何工作的就可以了。

代码如下:

import requests from lxml

import html url=’https://movie.douban.com/’ # 1、需要爬数据的网址

page=requests.Session.get(url) # 2、访问网页

tree=html.fromstring(page.text) # 3、解析网页的过程

result=tree.xpath(‘//td[@class=”title”]//a/text’) #3、解析网页的过程

print(result) # 打印出结果

在Python环境中运行这几行代码,就可以获取“一周口碑榜”了,结果如下:

[‘迦百农’, ‘绿皮书’, ‘驯龙高手3’, ‘速成家庭’, ‘阿丽塔:战斗天使’, ‘肤色’, ‘死亡天使’, ‘黎明墙’, ‘小小巨人’, ‘出·路’]

其中最关键的是解析网页内容,主要是(‘//td[@class=”title”]//a/text’)这行代码,大多数人可能对比较困惑。

这涉及到HTML网页的结构,可以把网页理解成一个文件夹,打开一个文件夹,会发现子文件夹,子文件夹或许还有文件夹。通过打开一个个文件夹,最终找到需要的数据。

  1. //td :这个相当于大目录;
  2. [@class=”title”]:这个相当于小目录;
  3. //a :这个相当于最小的目录;
  4. /text:这个是提取其中的文字内容。

至于是怎么写出来这行代码的,可以通过在网页空白处点击右键,查看源代码,就可以找到对应的td、class=”title”、a等标识符。

大多数程序员写爬虫选择python的理由很简单.

首先,python有很多的库,可以直接调用,比如:上面的代码就引入了requests、lxml库,分别实现访问网页、对网页结构解析。有开源的库,就直接调用,避免重复造轮子。

其次,python写起来很方便,配置也简单,短短几行的代码,就可以直接运行了,如果使用C或者Java,可能配置环境就要老半天。

三、一个简答的爬虫系统

把上面的每个步骤分别实现(模块化),就可以构成一个简答的爬虫系统。

使用URL(可以理解为网址链接)管理器管理所有的网址链接,使用HTML(可以理解为网页内容)下载器下载网页内容,使用HTML解析器对下载的内容解析,再加上数据存储模块、控制整个爬虫的调度模块,就构成了一个简单的爬虫系统。

爬虫基本架构

更具体的说,URL管理器负责管理所有的网址链接,记录下哪些URL已经爬取了,哪些还没有爬取。如果爬取过了,就要避免再次下载,如果没有,就要加入队列,等HTML下载器下载。

HTML下载器可以从服务器下载整个网页的内容,从URL管理器中获取未爬取的网址链接,之后,访问这些网页链接,下载网页。

HTML解析器负责解析下载好的网页,主要有两个任务:一方面,解析出需要的信息,比如上文的“一周口碑榜”;另一方面,解析出新的URL链接,交给URL管理器,继续下载,这个功能在上面的“7行代码”没有实现。

数据存储器实现存储数据的功能,将HTML解析器解析出来的信息存储起来,否则每次使用都要下载,会浪费大量的时间。图片、文档之类的文件可以直接保存到服务器上,文字类的可以通过数据库存储起来。

爬虫调度器作为系统的大脑,负责统筹其他四个模块的协调工作。

无论是大型的还是小型的爬虫虽然在设计细节,性能上有所不同,但都不会脱离这五个模块。

四、更深入的考虑

乍一看,每个模块实现起来都很简单,但细想,似乎每个模块都要考虑很多东西。

1. 初始的网址链接如何获得

7行代码爬取豆瓣电影,直接访问网址链接(https://movie.douban.com/)就可以爬取“一周口碑榜”。对稍大一些的爬虫系统或者商用爬虫,就要有更多的考虑了,在保证获取充足信息的同时,也要保证下载的质量。

对搜索引擎公司而言,要尽可能包括互联网所有的信息。对垂直领域,更多的偏向业务类信息,比如:对新闻类的APP,主要包括一些新闻网站、政府网站等,对Github这类的编程网站,他们可能就不感兴趣。

巧妇难为无米之炊,初始的网址链接基本要靠人工凭经验获取,比如:新闻类的APP,他们的初始URL列表里可能就包括新浪、网易、搜狐等门户网站,也包括各个级别的政府网站,还有人民网、新华社、人民日报等媒体的网站。

2. 如何确定哪些网页已经下载过了

当一个页面下载完成后,从这个网页中提取出其中的网址链接,把它们添加到等待下载的队列中,就可以获得更多的网址链接。

如果一个网页已经下载过了,重新下载,会消耗大量的时间,并占用存储空间。更要命的是,如果一直重复下载,就可能陷入死循环。

那么,如何知道这网址链接是不是已经下载过了?

对于小型爬虫,可以使用列表存储下载过的网址链接,当有新的网址链接的时候,先查找这个列表中有没有该网址链接。如果有的话,就不用插入,如果没有的话,就插入列表,等待访问下载。

对于大型爬虫,有成百上千个“小爬虫”(更加专业的名词叫做分布式爬虫),分布在不同的服务器上,同时爬取网址链接,就要考虑更多的东西。

比如:不同爬虫之间的分工和通信,如何共同维护上述的列表。

当数据很大的时候,就要考虑分布式、通信、存储、带宽等每个环节的限制,无论哪个环节没有做好,都有可能成为系统的瓶颈,这就像是木桶效应中的短板。

数据量增加10倍,之前的代码可能要重写了,工作量可能就要增加100倍,这也是量变引起质量的一个很好的例子。

在计算机领域,这样的例子随处可见,当数据增大到一定量级,原有的算法很可能无法继续使用,需要重新开发,随之而来的是加班、DEBUG以及延期上线。

3. 页面的解析

爬取豆瓣电影的“一周口碑榜”,需要研究网页的源代码,并编写对应的解析代码。但是网页的结构不同,用这个代码爬取知乎,解析不到任何内容。

以新闻类的APP为例:一个好的新闻类APP需要爬虫数以亿计的网页,并把里面的文字、视频、图片分别解析出来,难度可想而知。

好消息是一部分网站会遵守RSS规范(遵守RSS规范的网页结构和代码都有相似性,以便于订阅器获取主要信息),一种类型的爬虫就可以爬取大量这种类似的网页。但大部分的网站的结构,都是不同的,这需要算法工程师花费大量的时间和精力做解析工作。

五、 反爬虫

新闻类APP通过爬虫,获得大量的优质资源,读者也乐意在一个平台上看到所有的内容,但“被爬取”的网站就不太高兴了。对于大多数依靠广告收入的网站,没有了流量,连生存都成了问题,更别说盈利了。

一些自成体系的平台,比如:大型电商平台,他们希望所有的用户在自己的平台上查找信息,所有的商家在自己的平台上吸引卖家(广告费可不能付给搜索引擎),同样不希望爬虫的骚扰。

搜索引擎希望爬取更多的信息,优质的内容提供商又不希望被爬虫骚扰,利益冲突难以调和,于是产生了Robots协议来解决这个问题。

Robots协议网站服务器的一个声明,通常是保存在网站根目录下的一个TXT格式的文件,网站通过Robots协议告诉搜索引擎:哪些页面可以抓取?哪些页面不能抓取?

当爬虫访问一个站点时,它会首先检查该站点根目录下是否存在robots.txt,如果存在,爬虫就会按照该文件中的内容来确定访问的范围;如果该文件不存在,所有的爬虫将能够访问网站上所有没有被口令保护的页面。

我们使用搜索引擎,经常会看到“由于该网站的robots.txt文件存在限制指令(限制搜索引擎抓取),系统无法提供该页面的内容描述”,就是源于这个协议。

值得注意的是:Robots协议是国际互联网界通行的道德规范,并没有强制性约束力。

一些“没有道德”的爬虫同样会爬取有robots.txt限制指令的网站,这时候就需要一些技术来实现反爬虫了。

最常见的有三种方式:

1. 网站会根据IP地址访问的频率确定是不是爬虫

每个电脑都有唯一的IP地址,每个爬虫也有唯一的IP地址,当电脑或者爬虫访问网站的时候,网站会记录这个IP地址。如果同一个IP短时间多次访问同一个网站,这个网站可能会倾向于认为这是个爬虫,会采取一些措施。

当然,这在反爬虫的同时,也会给用户带来一些不好的体验。

相比之下,一些比较优秀的网站或者APP,会根据用户点击频率、时间间隔等信息,判断是不是爬虫或者误点击,之后再确定是否需要验证。

更好的用户体验背后,是更大的开发成本,更长的开发周期。

2. 网站也可以根据用户请求的Headers来判断是不是爬虫

当我们使用浏览器访问网站的时候,浏览器会自动在访问请求上添加一些信息,比如:浏览器采用的编码方式、使用的操作系统、浏览器版本等信息放在访问请求的最开始,作为Headers,但爬虫一般不会附加这些信息。

网站会根据是否存在Headers信息以及Headers信息的内容,判断对方是不是爬虫,有必要的话,就拒绝访问。

3. 动态页面的反爬虫

之前将的HTML网页都是静态的,随着HTML代码生成,页面的内容和显示效果就不会发生变化了。而动态网页则不然,动态网站是脚本语言(比如PHP)生成的,一些内容不是直接可见的,而是要运行一些脚本,才能看到。

网址后缀为htm、html、shtml、xml的网页是静态网页,而动态网页是以·aspx、.asp、.jsp、.php、.perl、.cgi等形式为后缀,并且在动态网页网址中有一个标志性的符号“?”,这些不同的后缀基本代表了网页使用的语言。

访问静态网页,只需要直接访问链接就可以了,访问动态网站,还需要执行一些特定的操作(比如点击),才能显示更多的内容,这就增加了爬取的难度,一些简单的爬虫就被拒之门外了。

介绍完三种主流的反爬虫的方式,最后要说的是:反爬虫技术也不是一劳永逸的,在反爬虫的发展过程中,爬虫也进化出了一系列反“反爬虫”的方式。

针对反爬虫验证IP机制,爬虫“进化”出了IP代理池,这样,爬虫就可以不断变换自己的IP地址,迷惑反爬虫。针对Headers验证,爬虫也会生成一个Headers信息,甚至针对动态页面,也会模拟浏览器的行为。

虽然如此,反爬虫在一定程度上提高了爬虫的成本,降低了爬虫的效率,就可以将一大部分爬虫挡在门外。

从爬虫与反爬虫的例子也可以看出:大多数时候,没有绝对的有效方式。提高对方的成本,让对方觉得代价太大,得不偿失,就是很好的解决问题的办法。

六、爬虫实现冷启动——胜利即正义?

上面讲了爬虫是怎么运行的,常见的反爬虫机制。最后,我们再讲一个爬虫的应用场景的例子,可以帮助我们更好理解爬虫。

冷启动是每一个产品经理、运营人员和创业者面临的重大问题。没有优质的内容,就吸引不了用户,没有大量的用户,就无法吸引优质的内容,就陷入了先有鸡还是先有蛋的悖论。

爬虫,低成本、快速地解决了这个问题!

“我们不生产新闻,我们只是新闻的搬运工”,通过爬虫,低成本、快速地爬取整个互联网的优质内容,并凭借海量数据,利用算法实现内容分类和个性推荐(个性推荐系统会在后序章节详细介绍),吸引大量的用户,最终通过广告变现。

事实证明,这是个非常成功的商业模式。而媒体平台和新闻网站雇佣大量编辑人员,花费大量时间、金钱写成的高质量内容,连说一声都没有,就这样被拿走了,这不是侵犯人家版权嘛!

于是,多家媒体联合发起侵权诉讼或抗议声讨,最终迫使这家新闻巨头支付版权费,但无论法律上、道德上有多少问题,都不影响这家公司商业成功的既定事实。

类似的事情同样发生在其他垂直领域。

一家新成立的技术博客平台,爬取竞争对手上的文章,迅速实现优质内容的聚合。如果原博客主发现了自己的文章被盗用了,新的平台就移交账号并看情况给予少许补偿。如果对方不乐意,就注销账号,当一切都没有发生过。凭借这种运营方式,顺利实现了冷启动。

短视频APP的后来者,也可以通过类似的方式,实现用户的积累和优质内容的聚合。

胜利即正义?

这似乎是过于武断的一个评价。

上述的视频APP做得太过分,引起公愤,最终不得不关闭自己的平台。

对于通过爬虫获取内容的平台而言,内容的获取也只是万里长征的第一步,通过运营手段减小生产内容的成本,通过利益共享激励优质内容的产生,通过技术减小信息成本吸引用户,更加任重而道远。

而版权,也始终是悬于头顶的达摩克利斯之剑。

本文由@linghu 原创发布于人人都是产品经理,未经许可,禁止转载

题图来自Unsplash, 基于CC0协议

鸟学ASP 

学习目的:安装调试ASP的环境,并且调试第一个简单的程序。

  因为我们学ASP的目的就是想建立一个网站,那么一般习惯是建立的网站内所有文件应该同时放到一个文件夹中(当然这个文件夹中还可以按需要设置子文件夹!),所以在这里我在E盘建立一个myweb的文件夹。

  首先来安装Windows 2000/XP自带的IIS作为服务器。在这里因为我的机器是XP所以所有截图都是XP下完成的。

  控制面板-->>添加或删除程序。


 然后是添加删除windows组件-选中IIS组件前面的勾,之后等待安装,这里可以观看本站以前给大家做的视频教程,完成安装后。


之后在控制面板中双击“管理工具”。


 然后双击“internet信息服务”,这就是IIS。


选择“默认网站”,然后右键属性或者直接按快捷键


 在属性页面内只有三个页面需要修改,先是网站,如果用户有固定IP,可以分配一个IP

接下来在注主目录中选择一个自己硬盘上面的文件夹存放网站(这个目录就是我们上面说的在e盘建立的目录),选中读取、写入

 在文档中添加默认的页面,比如打http://www.liexiaow.com猎校网 调用了一个默认文档即是你添加的页面。


接下来也是最关键的:

  1、在刚才你定义的一个文件夹里面新建一个文件,可以用记事本建立一个文本文件然后把下面的内容复制到记事本中:

<%response.write "hello world!"%>

  保存为1.asp文件(注意:.asp不能省略啊,如果省略了你就保存了一个文本文件了)。

  最后打开IE,在地址栏内输入:http://127.0.0.1/1.asp(或者http://localhost/1.asp)回车后就可以看到效果了。

最后解释一下代码的含义。

  response.write就是显示的意思,前后的 <% 和 %> 是asp的标记符号,在这里面的信息都由服务器处理。127.0.0.1是地址,1.asp是文件名。第一天就结束了!

表单元素要放在一个表单域里面,建立一个表单域。然后修改动作里面的文件为要接受这个表单变量的ASP文件。方法有两种,一种是POST,这个方法传送的变量不会在浏览器的地址栏里面显示,可以大批量传送数据;GET则是会在浏览器地址栏里面显示的,等一会举例子。

  下面我们看一个表单元素。

  文本域,这个是最基本的,传送的是文本信息,一般用户名,密码都要用这个传送,不过要是密码的话要在类型里面选择密码,这样就会以*代替显示出来的字符,文本域的名字很重要,以后会用到这个名字所以一般不用默认的名字。

  现在举一个例子:如果文本域的名字是name的话,用来传送网上用户登记的名字,在表单域里面,传送到reg.asp,用POST方法,那么在reg.asp里面这样得到变量<%name=request.form("name")%>如果要显示变量再家加一句,response.write name,这样就形成了一个从客户端到浏览器再回到客户端的过程。如果方法用的是GET的话,那么就改为name=request.querystring("name")实际上两者可以统一为name=request("name")。下面看看按钮,按钮里面无非两种,一种是提交表单的按钮,一种是重新输入的按钮。单选按钮,一个按钮有一个值。在列表里面同样,添加列表选项和值。下面举一个例子,实际上各种表单元素都是差不多的。

  下面是具体的代码: (注:把下面代码保存为一个.htm的文件就可以了.我保存的1.htm)

<form name="form1" method="post" action="reg.asp"> 
姓名: 
<input type="text" name="name"> //文本域,名字叫name 
<br> 
密码: 
<input type="password" name="psw"> //文本域,用来输入密码,名字叫psw 
<br> 
<br> 
性别: 
<input type="radio" name="sex" value="男"> //单选,名字叫sex,数值是"男" 
男 
<input type="radio" name="sex" value="女"> //单选,名字叫sex,数值是"女" 
女 <br> 
<br> 
城市: 
<select name="city"> 
<option value="上海" selected>上海</option> //复选,大家自己分析一下 
<option value="北京">北京</option> 
</select> 
<br> 
<input type="submit" name="Submit" value="提交"> //提交按钮 
<input type="reset" name="Submit2" value="重置"> 
</form>
  下面是reg.asp的代码,用来显示出刚才受到的信息:
<% 
name=request.form("name") 
psw=request.form("psw") 
sex=request.form("sex") 
city=request.form("city") 
response.write name 
response.write psw 
response.write sex 
response.write city 
%>

  打开浏览器在地址栏内输入:http://localhost/1.htm如下图:(呵呵,因为是练习,我没有美化页面啊!)


下面我们要开始学数据库了!只要把表单和数据库相应的字段连接上就可以了。

学会ACCEES数据库的使用。

  首先,要安装OFFICE里面的ACCEES(我的演示是OFFICE 2003版本),安装过程这里就不说了,安装好以后会,打开Access.按空数据库,新建一个数据库,文件名字可以叫guestbook.mdb。(提示:为了安全期间,文件名复杂一点好,因为ACCESS数据库可以被下载的!!)



使用设计器创建新的表,一个数据库MDB文件里面可以建立多个表。双击“使用设计器创建表”


 填写字段名字然后选择字段类型,一条记录可以有很多字段,可以有很多字段类型,字段大小的意思就是这个字段最多可以容纳的字符数,当这个字段没有任何信息是,ACCEES会用默认值代替(没有任何信息不是空的意思),一般必填字段和允许空字符串分别设置为否、是,以防止出错

按照上图分别建立几个字段,在时间中默认值为=now()就是这个字段不需要填写,系统直接以当前时间代替。

所以的字段都建立以后,关闭这个窗口,按提示保存表,输入表的名字guest,最后出现下图的提示,选择是,主键是记录的标识,为了以后简单,大家可以把编号改为ID,还有在ACCEES里面尽量不要用中文表示字段名字

最后,双击打开这个表,观看表里面的记录


握ACCESS数据库的连接和读取记录

  首先还打开我们上一节课建立的数据库中的表,随便输入几条记录,如下图:


这节课学习的内容有一点枯燥,但是很重要。在这里不需要知道命令具体的运行情况,外面的很多书籍之所以不适合入门就是因为介绍了太多的理论知识,让初学者一头雾水。

  下面开门见山,看两行代码:

<% 
set conn=server.createobject("adodb.connection") 
conn.open "driver={microsoft access driver (*.mdb)};dbq="&server.mappath("data/guestbook.mdb") 
%>
  第一行定义了一个adodb数据库连接组件,第二行连接了数据库,大家只要修改后面的数据库名字就可以了。是不是很简单?
  下面再看三行:
<% 
exec="select * from guest" 
set rs=server.createobject("adodb.recordset") 
rs.open exec,conn,1,1 
%>
  这三行加在前面两行的后面,第一句:设置查询数据库的命令,select后面加的是字段,如果都要查询的话就用*,from后面再加上表的名字,我们前面建立的是不是一个gust表阿?第二句:定义一个记录集组件,所有搜索到的记录都放在这里面,第三句是打开这个记录集,exec就是前面定义的查询命令,conn就是前面定义的数据库连接组件,后面参数“1,1”,这是读取,后面讲到修改记录就把参数设置为1,3,好了接下来我们读取记录。
<table width="100%" border="0" cellspacing="0" cellpadding="0"> 
<%do while not rs.eof%><tr> 
<td><%=rs("name")%></td> 
<td><%=rs("tel")%></td> 
<td><%=rs("message")%></td> 
<td><%=rs("time")%></td> 
</tr><% 
rs.movenext 
loop 
%> 
</table>
//下面的三条语句是用于关闭数据库

 rs.close
set rs=nothing
conn.close
set conn=nothing

  在一个表格中,我们用4列分别显示了上次建立的表里面的四个字段,用do循环,not rs.eof的意思是条件为没有读到记录集的最后,rs.movenext的意思是显示完一条转到下面一条记录,<%=%>就等于<%response.write%>用于在html代码里面插入asp代码,主要用于显示变量。

  把上面三段代码依次复制粘贴到剪贴板中,一定要按顺序复制粘贴啊!,然后保存为duqu.asp就可以了!打开浏览器,在地址栏内输入:http://localhost/duqu.asp.

  下面是我机器上面的结果图片。


学会数据库的基本操作--写入记录

  数据库的基本操作无非是:查询记录,写入记录,删除记录,修改记录。

  今天我们先学习写入记录。先建立一个表单:(把下面文章保存为5.htm)

<form name="form1" method="post" action="exa5.asp">
name <input type="text" name="name"><br>
tel <input type="text" name="tel"><br>
message <input type="text" name="message" value=""><br>
<input type="submit" name="Submit" value="提交">
<input type="reset" name="Submit2" value="重置">
</form>

  表单提交到exa5.asp,下面是exa5.asp的代码: (把下面代码保存为5.asp)

<%
set conn=server.createobject("adodb.connection")
conn.open "driver={microsoft access driver (*.mdb)};dbq="&server.mappath("data/guestbook.mdb")
name=request.form("name")
tel=request.form("tel")
message=request.form("message")
exec="insert into guest(name,tel,message)values('"+name+"',"+tel+",'"+message+"')"
conn.execute exec
conn.close
set conn=nothing
response.write "记录添加成功!"
%>

  在这里前面两句我不说了,后面三句我也不说了,前面说过exec里面的是执行的命令,添加记录的比较繁,大家要仔细看。

  insert into后面加的是表的名字,后面的括号里面是需要添加的字段,不用添加的或者字段的内容就是默认值的可以省略。注意,这里的变量一定要和ACCESS里面的字段名对应,否则就会出错。values后面加的是传送过来的变量。exec是一个字符串,"insert into guest(name,tel,message)values('"是第一段,在ASP里面不能嵌双引号,所以可以用'代替双引号,放在双引号里面,连接两个变量用+或者&所以"',"又是一段,中间夹了一个name就是表单传来的变量,这样就可以在这个变量外面加两个'',表示是字符串了,后面的tel是数字型变量所以不需要外面包围'',大家慢慢分析这句话,如果用表单传来的数据代替变量名字的话这句话为(假设name="aaa",tel=111,message="bbb"):"insert into guest(name,tel,message)values('aaa',111,'bbb')"。

  接下来的conn.execute 就是执行这个exec命令,最后别忘记把打开的数据库关闭,把定义的组件设置为空,这样可以返回资源。

  下面几条语句用于关闭数据库的代码:

rs.close
set rs=nothing
conn.close
set conn=nothing

  记住,次序不可以颠倒! 可以到数据库里面去看一看,或者用duqu.asp读取看看是不是多了记录阿?下面是我利用上一节的文件读取数据库的图:


学会数据库的基本操作--查询记录

  在第四天中我们用到下面这样一个程序:

<% 
set conn=server.createobject("adodb.connection") 
conn.open "driver={microsoft access driver (*.mdb)};dbq="&server.mappath("data/guestbook.mdb") 
exec="select * from guest" 
set rs=server.createobject("adodb.recordset") 
rs.open exec,conn,1,1 
%>
  我们查询的是所有的记录,但是我们要修改、删除记录的时候不可能是所有记录,所有我们要学习检索合适的记录。先看一条语句:
a="aaa"
b=1111110
exec="select * from guestbook where name='"+a+"'and tel="+b
  where后面加上的是条件,与是and,或是or,我想=,<=,>=,<,>的含义大家都知道吧。这句话的意思就是搜索name是“aaa”的,并且电话是“1111110”的记录。还有一点就是如果要搜索一个字段里面是不是包含一个字符串就可以这么写:where instr(name,a)也就是搜索name里面有a(aaa)这个字符串的人。
  我这里的a,b,是常量,大家可以让a,b是表单提交过来的变量,这样就可以做一个搜索了。
  下面大家看看这个代码,理解一下:(把下面代码存为6.htm文件)
<form name="form1" method="post" action="exam6.asp">
搜索:<br>
name =
<input type="text" name="name">
and tel= 
<input type="text" name="tel">
<br>
<input type="submit" name="Submit" value="提交">
<input type="reset" name="Submit2" value="重置">
</form>
  exam6.asp(把下面代码存为exam6.asp)
<%
name=request.form("name")
tel=request.form("tel")
set conn=server.createobject("adodb.connection")
conn.open "driver={microsoft access driver (*.mdb)};dbq="&server.mappath("data/guestbook.mdb")
exec="select * from guest where name='"+name+"' and tel="+tel
set rs=server.createobject("adodb.recordset")
rs.open exec,conn,1,1
%>
<html>
<head>
<title>无标题文档</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head> 
<body bgcolor="#FFFFFF" text="#000000"> 
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<%
do while not rs.eof
%><tr>
<td><%=rs("name")%></td>
<td><%=rs("tel")%></td>
<td><%=rs("message")%></td>
<td><%=rs("time")%></td>
</tr>
<%
rs.movenext
loop
%>
</table>
</body>
</html>

  首先在浏览器中输入http://localhost/6.htm如下图所示:



学会数据库的基本操作--删除记录

  开门见山,大家直接看程序。

exec="delete * from guest where id="&request.form("id")

  上面这句话完成了删除记录的操作,不过锁定记录用了记录唯一的表示id,我们前面建立数据库的时候用的是系统给我们的主键,名字是编号,由于是中文的名字不是很方便,大家可以修改为id,我已经修改了啊!

  不修改的话就是用下面的代码了:

exec="delete * from guestbook where 编号="&request.form("id")

  下面我们看完整的代码:一个表单传给ASP文件一个ID,然后这个ASP文件就删除了这个ID。

  7.htm:

<form name="form1" method="post" action="exam7.asp">
请输入删除的ID号:
<input type="text" name="id">
<input type="submit" name="Submit" value="提交">
</form>

  exam7.asp:(可以先运行这个文件看一下所有记录的ID和想删除记录的ID,删除记录以后也可以通过这个文件复查。)
<%
set conn=server.createobject("adodb.connection")
conn.open "driver={microsoft access driver (*.mdb)};dbq="&server.mappath("data/guestbook.mdb")
exec="select * from guest"
set rs=server.createobject("adodb.recordset")
rs.open exec,conn,1,1 
%>
<html>
<head>
<title>无标题文档</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<%
do while not rs.eof
%><tr>
<td><%=rs("id")%></td>
<td><%=rs("name")%></td>
<td><%=rs("tel")%></td>
<td><%=rs("message")%></td>
<td><%=rs("time")%></td>
</tr>
<%
rs.movenext
loop
%>
</table>
</body>
</html>


学会数据库的基本操作--修改记录

  先来看代码:(存为exam8.asp就可以了)

<%
set conn=server.createobject("adodb.connection")
conn.open "driver={microsoft access driver (*.mdb)};dbq="&server.mappath("data/guestbook.mdb")
exec="select * from guest where id="&request.form("id")
set rs=server.createobject("adodb.recordset")
rs.open exec,conn
%>
<form name="form1" method="post" action="modifysave.asp">
<table width="748" border="0" cellspacing="0" cellpadding="0">
<tr> 
<td>name</td>
<td>tel</td>
<td>message</td>
</tr>
<tr> 
<td>
<input type="text" name="name" value="<%=rs("name")%>">
</td>
<td>
<input type="text" name="tel" value="<%=rs("tel")%>">
</td>
<td>
<input type="text" name="message" value="<%=rs("message")%>">
<input type="submit" name="Submit" value="提交">
<input type="hidden" name="id" value="<%=request.form("id")%>">
</td>
</tr>
</table>
</form>
<%
rs.close
set rs=nothing
conn.close
set conn=nothing
%>
  到现在应该分析这个代码没有什么问题了吧,这个代码的作用是接受前面一个页面的ID然后显示这条记录,文本框即是输入的地方也是显示的地方,如果需要修改的话修改以后按提交;如果不需要修改就可以直接按提交按钮。
  在这里因为本教程适合初学的,所以也把提交的表单内容给出来,把下面代码存为8.htm文件
<form name="form1" method="post" action="exam8.asp">
请输入要修改的记录的ID:

 <input type="text" name="id">
<br>
<input type="submit" name="submit" value="提交">
</form>

这里还有一个东西以前没有说,那就是隐藏的表单元素:hidden元素,里面的value是不用用户输入的,会随着表单一起提交,用于传递变量。
  下面是modifysave.asp的代码:
<%
set conn=server.createobject("adodb.connection")
conn.open "driver={microsoft access driver (*.mdb)};dbq="&server.mappath("data/guestbook.mdb")
exec="select * from guest where id="&request.form("id")
set rs=server.createobject("adodb.recordset")
rs.open exec,conn,1,3
rs("name")=request.form("name")
rs("tel")=request.form("tel")
rs("message")=request.form("message")
rs.update
rs.close
set rs=nothing
conn.close
set conn=nothing
%>

  在这里,rs.open exec,conn,1,3后面的参数是1,3,这我以前提过,修改记录就要用1,3。实际上修改记录很容易看懂,记录集是rs,rs("aa")就是当前记录aa字段的东西,让它等于新的数据request.form("aa")当然就修改了,不过最后别忘记保存,那就是rs.update!

  说到这里,记录的搜索,读取,修改,插入都说了,通过这最基本的东西就可以作出复杂的东西了,外面的大型数据库:新闻系统,留言簿就是字段多一点罢了。今天的示例中的代码是结合以前的数据库的,以后回去调试分析一下。

  大家测试的流程:首先运行8.htm文件



基本的SESSION组件,总结response,request组件。

  首先,有会员系统的任何程序都会用到检测是不是用户已经登陆这个步骤。这就用到了SESSION组件,下面我们 看一个代码来说明。

<%
session("login")="yes"
%>

  这句话的意思就是在session里面定义一个login字符串变量,值为"yes",直接可以赋值,不需要声明。是不是很简单?

  如果我们做管理员登陆系统的话,首先是一段检测是不是管理员:

if 是 then
session("isadmin")=yes"
else
session("isadmin")="no"
end if

  在每一个需要管理员才能看的页面最前面加上:

<%
if not session("isaadmin")="yes" then
response.redirect "login.htm"
%>

  这样一般用户就无法打开这个页面。解释一下response.redirect,它是转向的意思,后面的"login.htm"就是转向的文件。这样没有登陆的管理员是无法看到后面的内容的。

  response组件基本就是用到response.write(),response.redirect() 分别是写字符串和转向的作用。

  request基本就是request.form(),request.querystring() 分别是接受post,get方法传来的信息。

  最后我们一起来制作一个简单的后台登陆管理界面,首先在myweb目录下建立一个admin文件夹,然后我们建立一个数据库名字为admin.mdb,然后我们再建立一个表,表中设置两个字段name,password,类型都是文本型的!最后退出时设置主键,保存为表名check。然后可以输入一条记录用户名:admin,密码:admin。具体建立数据库的方法请看《菜鸟十天学会ASP教程之第三天:数据库的建立

  下面我们开始编写ASP程序,首先建立一个index.asp(管理主界面)程序,代码如下:

<%@language=vbscript%>
<%if not session("checked")="yes" then 
response.Redirect "login.asp"  
else
%>
<html>
<head>
<title>管理界面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<frameset cols="167,*" frameborder="YES" border="1" framespacing="1" rows="*" bordercolor="#666666"> 
  <frame name="leftFrame" scrolling="auto" noresize src="left.asp">
  <frame name="mainFrame" src="right.asp">
</frameset>
<noframes> 
<body bgcolor="#FFFFFF" text="#000000">
</body>
</noframes> 
</html>
<%end if%>
  在上面的代码中,大家可以看到用到login.asp,left.asp,right.asp程序
  login.asp://登陆系统程序
<html>
<head>
<title>管理员入口</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<style type="text/css">
<!--
.topic {  font-family: "宋体"; font-size: 11pt; font-weight: bold; color: #FFFFFF}
.font {  font-family: "宋体"; font-size: 10pt; font-weight: normal; color: #000000}
.table {  border-color: #666666 black; border-style: solid; border-top-width: 1pt; border-right-width: 0px; border-bottom-width: 1pt; border-left-width: 0px}
.text {  border: 1pt #999999 solid; height: 15pt}
-->
</style>
</head>
<body text="#000000" topmargin="0" bgcolor="#FFFFFF">
<table width="100%" border="0" cellpadding="0" cellspacing="0" align="center" height="100%">
  <tr> 
    <td height="129" valign="top" colspan="3">  </td>
  </tr>
  <tr> 
    <td width="230" height="170" valign="top"> </td>
    <td valign="top" width="277"> 
      <table width="100%" border="0" cellspacing="1" cellpadding="0" height="100%" bgcolor="#000000" align="center">
        <tr> 
          <td align="center" valign="middle" height="167">
            <form name="form1" method="post" action="check.asp">
              <table width="100%" border="0" cellspacing="0" cellpadding="0" height="100%">
              <tr bgcolor="#62892C"> 
                <td height="31" class="topic" colspan="2"> 
                  <div align="center">管理员入口<br>
                  </div>
                </td>
              </tr>
              <tr> 
                
                  <td bgcolor="#87bc3c" colspan="2" class="table"> 
                    <div align="center"> <span class="font"> 管理员:</span> 
                      <input type="text" name="name"  class="text" size="20" onMouseOver="this.focus()">
                      <br>
                      <span class="font">密 码:</span> 
                      <input type="password" name="password" class="text" size="20" onMouseOver="this.focus()"><%if session("check")="wrong" then response.Write "<br><span class='font'><font color=red>验证错误!</font></span>" end if%>
                    </div>
                  </td>
             
              </tr>
              <tr> 
                <td bgcolor="#87bc3c" width="52%"> 
                    <div align="center" class="font">
                      <input type="reset" name="Submit2" value="重置" class="text">
                    </div>
                </td>
                <td bgcolor="#87bc3c" width="48%"> 
                    <div align="center" class="font">
                      <input type="submit" name="Submit22" value="提交" class="text">
                    </div>
                </td>
              </tr>
            </table>   </form>
          </td>
        </tr>
      </table>
    </td>
    <td width="241" valign="top"> </td>
  </tr>
  <tr> 
    <td height="123" valign="top" colspan="3"> </td>
  </tr>
</table>
</body>
</html>
  在上面的程序中用到一个检查用户和密码是否正确的程序check.asp://核对输入的用户和密码是否正确
<%
dim name,password
name=request.form("name")
password=request.form("password")
dim exec,conn,rs
exec="select *from check where(name='"&name&"' and password='"&password&"')"
set conn=server.createobject("adodb.connection")
conn.open "driver={microsoft access driver (*.mdb)};dbq="&server.mappath("admin.mdb")
set rs=server.createobject("adodb.recordset")
rs.open exec,conn
if not rs.eof then
rs.Close
conn.Close
session("checked")="yes"
session("check")="right"
response.Redirect "index.asp"
else
session("checked")="no"
session("check")="wrong"
response.Redirect "login.asp"
end if
%>
  left.asp://管理导航
<%@language=vbscript%>
<%if not session("checked")="yes" then 
response.Redirect "login.asp"  
else
%>
<html>
<head>
<title>管理界面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body text="#000000" topmargin="0" bgcolor="#ffffff" leftmargin="10">
<div align="center"><a href="index.asp" target="_parent"><br>
  <br>
  管理界面首页</a> <a href="exit.asp" target="_parent">退出</a><br>  
  <br>
</div>
</body>
</html>
<%end if%>
  exit.asp://退出系统
<%@language=vbscript%>
<%
session("check")=""
session("checked")=""
response.redirect "login.asp"
%>
  right.asp://具体管理的内容
<%@language=vbscript%>
<%if not session("checked")="yes" then 
response.Redirect "login.asp"  
else
%>
<html>
<title>管理界面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body bgcolor="#ffffff" text="#000000" topmargin="20" class="title">
这里是网页教学网的管理系统示例!请大家多研究使用!

 </body>
</html> 
<%end if%>


  运行时首先运行index.asp程序,运行效果部分截图如下:

分页技术

  今天最后一天我们学习一下ASP里面稍微难一点地分页技术,毕竟当我们有N条记录的时候我们不可能把所有记录显示在一个页面里面吧。

<%
exec="select * from test"
set rs=server.createobject("adodb.recordset")
rs.open exec,conn,1,1
rs.PageSize=3
pagecount=rs.PageCount 
page=int(request.QueryString ("page"))
if page<=0 then page=1
if request.QueryString("page")="" then page=1
rs.AbsolutePage=page 
%>

  rs.pagesize设置一个页面里面显示的记录数,pagecount是我们自己定义的一个变量,rs.pagecount是记录的个数,page也是我们自己定义的一个变量,我们下一页的链接可以设置为list.asp?page=<%=page+1%>,下一页的链接可以设置为list.asp?page=<%=page-1%>,这样当按下链接的时候调用页面自己,page这个变量就+1或者-1了,最后我们让rs.absolutepage(当前页面)为第page页就可以了。

  if request.QueryString("page")="" then page=1,这句话的作用就是我们打开list.asp的时候没有跟随page变量,自动设置为page=1,防止出错,还有当我们if....then...放在一行的时候end if可以省略。是不是分页也不难?

  下面说一种特殊情况:

if page=1 and not page=pagecount,这个时候没有上一页,但是有下一页


elseif page=pagecount and not page=1,这个时候没有下一页,但是有上一页


elseif page<1,这个时候没有任何记录


elseif page>pagecount then,这个时候没有任何记录


elseif page=1 and page=pagecount,这个时候没有上一页,没有下一页


else,这个时候有上一页,也有下一页。


  下面看一段显示1到n页,且每一个数字点击以后就出现这个数在代表的页面的代码,很常见哦。

<%for i=1 to pagecount%>
<a href="list.asp?page=<%=i%>"><%=i%></a><%next%>

  for....next是循环从i=1开始,循环一次加1到pagecount为止。

  最后我的实例里面包含了一个最简单的ASP程序,但是功能样样有,是ASP的精髓,每一个ASP大型程序都包含了它。

add.htm增加记录页面


add.asp增加记录操作


conn.asp数据库链接


del.asp删除记录操作


modify.asp修改记录页面


modifysave.asp修改记录操作


list.asp这个是这个程序的核心,通过这个页面实现记录的添加、修改、删除。