日志存档:01, 2008

(转)在程序运行的状态下 用鼠标移动窗体上的控件

2008-01-22,星期二 | 分类:编 程|VisualBasic | 160 views

Option Explicit

Dim a As Boolean     ‘ 这个变量记录了是否在按下状态
Dim oldx As Integer ‘ 这个变量记录了 未移动的X 位置
Dim oldy As Integer ‘ 这个变量记录了 未移动的Y 位置

Private Sub Frame1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
a = True        ‘鼠标在 框架上按下时候 记录已经按下
oldx = X        ‘同时记录当前位置 X
oldy = Y        ‘同时记录当前位置 Y
End Sub

Private Sub Frame1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
‘ 若按下 就移动 当前位置-移动位置=移动值
‘具体可以参考move 函数
If a = True Then Frame1.Move Frame1.Left + X - oldx, Frame1.Top + Y - oldy

End Sub

Private Sub Frame1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
a = False ‘鼠标放开后 记录
End Sub

来源:CSDN

在程序运行的状态下 用鼠标移动窗体上的控件

2008-01-22,星期二 | 分类:编 程|VisualBasic | 134 views

Option Explicit

Dim a As Boolean     ‘ 这个变量记录了是否在按下状态
Dim oldx As Integer ‘ 这个变量记录了 未移动的X 位置
Dim oldy As Integer ‘ 这个变量记录了 未移动的Y 位置

Private Sub Frame1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
a = True        ‘鼠标在 框架上按下时候 记录已经按下
oldx = X        ‘同时记录当前位置 X
oldy = Y        ‘同时记录当前位置 Y
End Sub

Private Sub Frame1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
‘ 若按下 就移动 当前位置-移动位置=移动值
‘具体可以参考move 函数
If a = True Then Frame1.Move Frame1.Left + X - oldx, Frame1.Top + Y - oldy

End Sub

Private Sub Frame1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
a = False ‘鼠标放开后 记录
End Sub

来源:CSDN

SQL Server 2005关于数据类型最大值

2008-01-21,星期一 | 分类:数据库|SQLServer | 119 views

事情开始得很简单。MegaWare公司市场部门想要一个新的网站来发布文档,开发 团队觉得使用SQL Server 2000数据库作为文档存储仓库会使事情变得简单。Steve是MegaWare的数据库管理员,没有看出这有什么大问题;在数据库中存储文档,而不是使 用文件系统,意味着服务器需要多做一些工作,但是它也会使得备份和管理容易得多。数据库与文件系统变得不同步也应该是不可能的。

  市 场部门想要存储的许多文档都超过了8000个字节,那么很明显VARCHAR不是适合这项工作的数据类型。作为替代,TEXT数据类型被用来定义存放数据 的字段。因为每个TEXT都能容纳2GB的内容,TEXT要存放市场部门的同事们扔进数据库的最大的文件也是没有问题的。

  数月过去了,市场用大量的无聊拷贝填满了整个数据库。但是这还不是Steve真正关心的问题。数据库愉快地嗡嗡作响地运转着,每个人对项目的结果都很满意。

   直到公司的标语改变的那个重大的日子。市场部的团队认为“MegaWare: It’s really cool!”要比原来的“It’s MegaWare’s Way or the Highway!” 听起来更好。因为市场部团队已经将原来的标语嵌入了仓库中每个文档的页脚上,现在Steve的工作就是更改所有这些文档的页脚。

  “没有问题,” Steve想,打开SQL Server 查询分析器工具,执行了如下的T-SQL批处理:

  UPDATE MarketingDocuments

  SET Document =

  REPLACE(Document,

  ’It’’s MegaWare’’s Way or the Highway!’,

  ’MegaWare: It’’s really cool!)

  当他看到出现的错误消息的时候,Steve的轻松的微笑很快消失了,“替换函数的参数1,text数据类型无效。”

   替换函数在编写出来的时候,就对TEXT数据类型不起作用。同样也对CHARINDEX或者SUBSTRING不起作用??或者至少是他们在超过8千个 字符的情况下不起作用。更进一步地讲,开发人员忘了处理TEXT或者IMAGE类型的本地变量;实际上不支持任何操作。即使是简单地更新一个文档中的一个 子字符串都需要用到晦涩的东西,以及难以使用的类似READTEXT和WRITETEXT的函数。而不是开发人员或者忙碌的数据库管理员因为想要弄清如何 正确使用而采用了不同类型的函数消耗了时间。

  SQL Server的开发人员很幸运,他们将会拨开乌云见蓝天。SQL Server 2005引入了一系列新的被称为MAX的数据类型。这是VARCHAR,NVARCHAR和VARBINARY类型的扩展,这几种类型以前被限制在 8000字节以下。MAX可以容纳高达2GB的数据,与TEXT和IMAGE一样??并且完全兼容所有的SQL Server内置的字符串函数。

  用MAX关键字定义一个某种MAX类型的变量与替代字符串的尺寸(为VARCHAR/NVARCHAR的时候)或者字节(为VARBINARY的时候)一样简单。

  DECLARE @BigString VARCHAR(MAX)

  SET @BigString = ‘abc’

  虽然这个变量可以自由地操纵,并且可以传递给任何的内置的字符串函数,兼容性仍然不是没有问题。首先,开发人员不能期望指定了尺寸的VARCHAR和VARBINARY变量在达到8000个字节的极限的时候可以自动“升级”到MAX版本。例如,如下的批处理:

  DECLARE @String1 VARCHAR(4001)

  DECLARE @String2 VARCHAR(4001)

  SET @String1 = REPLICATE(’1′, 4001)

  SET @String2 = REPLICATE(’2′, 4001)

  SELECT LEN(@String1 + @String2)

   4001+4001=8002,但是指定了尺寸的VARCHAR的极限是8000。因为这两个变量中没有一个是MAX类型,LEN函数的结果就是 8000,不是8002。在将两个变量连接的时候,一种简单的修正方法就是声明这两个变量中的一个为VARCHAR(MAX)或者将其中的一个变量进行转 换。与一个规定了尺寸的类型进行连接的时候,优先考虑MAX类型,最终结果是MAX类型。所以,以下批处理的结果是8002,正如我们期望的一样:

  DECLARE @String1 VARCHAR(4001)

  DECLARE @String2 VARCHAR(4001)

  SET @String1 = REPLICATE(’1′, 4001)

  SET @String2 = REPLICATE(’2′, 4001)

  SELECT LEN(CONVERT(VARCHAR(MAX), @String1) + @String2)

  在传递给字符串函数的时候,开发人员意识到字符串的原意在默认情况下是规定了尺寸的,而不是MAX类型,也是至关重要的。例如,以下查询的结果就很令人惊奇:

  SELECT LEN(REPLICATE(’1′, 8002))

因为字符串‘1’是被作为规定了尺寸的VARCHAR对待,而不是VARCHAR(MAX),结果就是8000??但是在SQL Server 2005中,REPLICATE函数能够产生高达2GB的字符串。要修正这个问题,可以将字符串转换为VARCHAR(MAX),这样函数就会输出同样的 类型了:

  SELECT LEN(REPLICATE(CONVERT(VARCHAR(MAX), ‘1′), 8002))

这个查询现在将会返回期望的结果:8002。记住,总是要对采用了新特性编写的代码进行非常仔细的测试;隐藏的问题,例如上面描述的问题,可能并且毫无疑问地会在最坏的时间里造成灾难性的后果。
本贴来自ZDNetChina中文社区 http://bbs.zdnet.com.cn ,本贴地址:http://bbs.zdnet.com.cn/viewthread.php?tid=99851

除了变量之外,MAX类型也可以用于定义表的字段:

  CREATE TABLE BigStrings

  (

  BigString VARCHAR(MAX)

  )

   当用于表的时候,意识到MAX类型具有与TEXT和IMAGE类型稍微不同的行溢出行为是非常重要的。在SQL Server中,最大的行尺寸是8060字节。要超过这个限制,并且仍然管理每个都拥有高达2GB的存储,用TEXT和IMAGE类型存储的数据会被存储 引擎自动地断行,在行里只留下一个16字节的指针。这意味着行的尺寸是减少了,这对性能有好处。然而,检索大数据是昂贵的,因为它不是与同一行的数据存放 在同一个位置。

  MAX数据类型在默认情况下,使用TEXT/IMAGE溢出行为和正常尺寸的VARCHAR/VARBINARY类型的 行为的混合方式。如果一个字段的数据,加上表中所有其他字段的数据,总量少于8060字节,数据就存放在行内。如果数据超过8060字节,MAX字段的数 据就会存放在行外。对于大字符串的表,以下的行将会与表中的其他数据存储在同一个数据页内:

  INSERT BigStrings (BigString)

  VALUES (REPLICATE(’1′, 8000))

  But the following row will result in an overflow:

  INSERT BigStrings (BigString)

  VALUES (REPLICATE(CONVERT(VARCHAR(MAX), ‘1′), 100000))

   你可以更改MAX数据类型在每个表的基础上的默认的行为,它们会表现得和TEXT和IMAGE类型一样。这是通过使用sp_tableoption 存储过程中的“大数值类型在行外”选项实现的。为了修改大字符串表以将MAX类型的处理方式变得与TEXT和IMAGE数据类型的处理方式相同,可以使用 如下的T-SQL:

  EXEC sp_tableoption

  ’BigStrings’,

  ’large value types out of row’,

  ’1′

  看看定义一个MAX数据类型有多容易,与他们提供的灵活性一样,一些数据设计师将会被引诱以下列的方式开始定义表:

  CREATE TABLE Addresses

  (

  Name VARCHAR(MAX),

  AddressLine1 VARCHAR(MAX),

  AddressLine2 VARCHAR(MAX),

  City VARCHAR(MAX),

  State VARCHAR(MAX),

  PostalCode VARCHAR(MAX)

  )

  设计师要注意了:不要这样做!一个企业中的数据模型既应该包含有具有实际限制的数据,还要给用户接口设计师有关字段尺寸的大致的指导。像这样的表又该创建什么样的用户接口呢?

  除了数据整合和用户接口含义之外,如果设计师这样不必要地使用这些类型还会带来性能上的损害。记住,查询优化器使用字段的尺寸作为判断优化查询计划的众多标准之一。对于这个表,优化器几乎没有任何选择。

所以,现在你知道了MAX数据类型为SQL Server 2005处理大数据增加了很大部分的灵活性。但是MegaWare的那个不幸的数据库管理员,Steve会发生什么变化?还在坚持使用SQL Server 2000,他开始更新简历,想象着如果更新表失败了话,他的工作也就失去了。但是他也是幸运的??还有世界各地的MegaWare产品的拥护者??用 GOOGLE的搜索可以很快地找到这篇文章《在TEXT字段中查找并替代》,这篇文章告诉他如何正确的进行更新。他花了整晚的时间来学习资料;再过几个月 之后,TEXT和IMAGE数据类型就仅仅是一段不愉快的记忆了

(转)SQL Server 2005对海量数据处理

2008-01-21,星期一 | 分类:数据库|SQLServer | 117 views

超大型数据库的大小常常达到数百GB,有时甚至要用TB来计算。而单表的数据量往往会达到上亿的记录,并且记录数会随着时间而增长。这不但影响着数 据库的运行效率,也增大数据库的维护难度。除了表的数据量外,对表不同的访问模式也可能会影响性能和可用性。这些问题都可以通过对大表进行合理分区得到很 大的改善。当表和索引变得非常大时,分区可以将数据分为更小、更容易管理的部分来提高系统的运行效率。如果系统有多个CPU或是多个磁盘子系统,可以通过 并行操作获得更好的性能。所以对大表进行分区是处理海量数据的一种十分高效的方法。本文通过一个具体实例,介绍如何创建和修改分区表,以及如何查看分区 表。

  1 SQL Server 2005

  SQL Server 2005是微软在推出SQL Server 2000后时隔五年推出的一个数据库平台,它的数据库引擎为关系型数据和结构化数据提供了更安全可靠的存储功能,使用户可以构建和管理用于业务的高可用和 高性能的数据应用程序。此外SQL Server 2005结合了分析、报表、集成和通知功能。这使企业可以构建和部署经济有效的BI解决方案,帮助团队通过记分卡、Dashboard、Web Services和移动设备将数据应用推向业务的各个领域。无论是开发人员、数据库管理员、信息工作者还是决策者,SQL Server 2005都可以提供出创新的解决方案,并可从数据中获得更多的益处。

  它所带来的新特性,如T-SQL的增强、数据分区、服务代理和与.Net Framework的集成等,在易管理性、可用性、可伸缩性和安全性等方面都有很大的增强。

  2 表分区的具体实现方法

  表分区分为水平分区和垂直分区。水平分区将表分为多个表。每个表包含的列数相同,但是行更少。例如,可以将一个包含十亿行的表水平分区成 12 个表,每个小表表示特定年份内一个月的数据。任何需要特定月份数据的查询只需引用相应月份的表。而垂直分区则是将原始表分成多个只包含较少列的表。水平分 区是最常用分区方式,本文以水平分区来介绍具体实现方法。

  水平分区常用的方法是根据时期和使用对数据进行水平分区。例如本文例子,一个短信发送记录表包含最近一年的数据,但是只定期访问本季度的数据。在这种情况下,可考虑将数据分成四个区,每个区只包含一个季度的数据。

  2.1 创建文件组

  建立分区表先要创建文件组,而创建多个文件组主要是为了获得好的 I/O 平衡。一般情况下,文件组数最好与分区数相同,并且这些文件组通常位于不同的磁盘上。每个文件组可以由一个或多个文件构成,而每个分区必须映射到一个文件 组。一个文件组可以由多个分区使用。为了更好地管理数据(例如,为了获得更精确的备份控制),对分区表应进行设计,以便只有相关数据或逻辑分组的数据位于 同一个文件组中。使用 ALTER DATABASE,添加逻辑文件组名:

  ALTER DATABASE [DeanDB] ADD FILEGROUP [FG1]

  DeanDB为数据库名称,FG1文件组名。创建文件组后,再使用 ALTER DATABASE 将文件添加到该文件组中:

  ALTER DATABASE [DeanDB] ADD FILE ( NAME = N’FG1′, FILENAME = N’C:DeanDataFG1.ndf’ , SIZE = 3072KB , FILEGROWTH = 1024KB ) TO FILEGROUP [FG1]

  类似的建立四个文件和文件组,并把每一个存储数据的文件放在不同的磁盘驱动器里。

  2.2 创建分区函数

  创建分区表必须先确定分区的功能机制,表进行分区的标准是通过分区函数来决定的。创建数据分区函数有RANGE “LEFT | / RIGHT”两种选择。代表每个边界值在局部的哪一边。例如存在四个分区,则定义三个边界点值,并指定每个值是第一个分区的上边界 (LEFT) 还是第二个分区的下边界 (RIGHT)[1]。代码如下:

  CREATE PARTITION FUNCTION [SendSMSPF](datetime) AS RANGE RIGHT FOR VALUES (’20070401′, ‘20070701′, ‘20071001′)

  2.3 创建分区方案

  创建分区函数后,必须将其与分区方案相关联,以便将分区指向至特定的文件组。就是定义实际存放数据的媒体与各数据块的对应关系。多个数据表可以 共用相同的数据分区函数,一般不共用相同的数据分区方案。可以通过不同的分区方案,使用相同的分区函数,使不同的数据表有相同的分区条件,但存放在不同的 媒介上。创建分区方案的代码如下:

  CREATE PARTITION SCHEME [SendSMSPS] AS PARTITION [SendSMSPF] TO ([FG1], [FG2], [FG3], [FG4])

  2.4 创建分区表

  建立好分区函数和分区方案后,就可以创建分区表了。分区表是通过定义分区键值和分区方案相联系的。插入记录时,SQL SERVER会根据分区键值的不同,通过分区函数的定义将数据放到相应的分区。从而把分区函数、分区方案和分区表三者有机的结合起来。创建分区表的代码如 下:

  CREATE TABLE SendSMSLog

  ([ID] [int] IDENTITY(1,1) NOT NULL,

  [IDNum] [nvarchar](50) NULL,

  [SendContent] [text] NULL

  [SendDate] [datetime] NOT NULL,

  ) ON SendSMSPS(SendDate)

  2.5 查看分区表信息

  系统运行一段时间或者把以前的数据导入分区表后,我们需要查看数据的具体存储情况,即每个分区存取的记录数,那些记录存取在那个分区等。我们可以通过$partition.SendSMSPF来查看,代码如下:

  SELECT $partition.SendSMSPF(o.SendDate)

  AS [Partition Number]

  , min(o.SendDate) AS [Min SendDate]

  , max(o.SendDate) AS [Max SendDate]

  , count(*) AS [Rows In Partition]

  FROM dbo.SendSMSLog AS o

  GROUP BY $partition.SendSMSPF(o.SendDate)

  ORDER BY [Partition Number]

在查询分析器里执行以上脚本,结果如图1所示:

  图1 分区表信息

   

  2.6 维护分区

  分区的维护主要设计分区的添加、减少、合并和在分区间转换。可以通过ALTER PARTITION FUNCTION的选项SPLIT,MERGE和ALTER TABLE的选项SWITCH来实现。SPLIT会多增加一个分区,而MEGRE会合并或者减少分区,SWITCH则是逻辑地在组间转换分区。

  3 性能对比

  我们对2650万数据,存储空间占用约4G的单表进行性能对比,测试环境为IBM365,CPU 至强2.7G*2、内存 16G、硬盘 136G*2,系统平台为Windows 2003 SP1+SQL Server 2005 SP1。测试结果如表1:

  表1:分区和未分区性能对比表(单位:毫秒)

  测试项目 分区 未分区

  1 16546 61466

  2 13 33

  3 20140 61546

  4 17140 61000

  说明:

  1:根据时间检索某一天记录所耗时间

  2:单条记录插入所耗时间

  3:根据时间删除某一天记录所耗时间

  4:统计每月的记录数所需时间

  从表1可以看出,对分区表进行操作比未分区的表要快,这是因为对分区表的操作采用了CPU和I/O的并行操作,检索数据的数据量也变小了,定位数据所耗时间变短。

  4 结束语

  对海量数据的处理一直是一个令人头痛的问题。分离的技术是所有设计者们首先考虑的问题,不管是分离应用程序功能还是分离数据访问,如果加以了合 理规划,都能十分有效的解决大数据表的运行效率低和维护成本高等问题。SQL Server 2005新增的表分区功能,可以对数据进行合理分区,当用户在访问部分数据时,SQL Server最佳化引擎可以根据数据的实体存放,找出最佳的执行方案,而不至于大海捞针。

来源: 尘封の觉醒 技术博客

用Google作Web代理,突破连接障碍畅游互联网

2008-01-21,星期一 | 分类:综合分类|经典收藏 | 110 views

通常,当你在查看一个网页时,你计算机上的浏览器软件就会连接到目标服务器,下载页面的数据,接着再将它呈现在你的面前。

  一般情况下,这确实是访问网站的优先方式,但有的时候,当你在办公室或学校里使用计算机时,你会发现这些场所的连接可能会阻止你连接到某些网站。  

         如果你碰到这样的情况,那么你就需要一个代理。代理是这样一种服务器,它能够连接到你那方列入黑名单服务器,并反馈给你所得结果。

  然而,你仍然需要直接访问代理服务器,因此,确保代理服务器本身不再黑名单之列是很重要的。有趣的是,Google作为一个不太可能被列入黑名单的主机,它本身就有一些工具能够有效地扮演一个web代理的角色。

  Google代理技巧1

  第一个工具就是Google的翻译功能。这项服务能够动态地下载和翻译任何你所要求的网页,当你在指定翻译的语 言之后,Google翻译就能够将目标文档呈现出来,就像使用一个简易的代理一样。过去人们想浏览一些无法访问的英文网页时,也许会将需要翻译的语言也设 置成英语,但这项功能现在似乎已经被Google移除了。但如果你设定的是“英语到中文(简体)”,那么即使你要翻译的页面原本就是中文的,你也能够得到 确切地文字。当然,如果是需要查看英语的网页也可以用同样的方法。如果你使用的是中文的过滤器,那么当你将鼠标移动在文本上方的时候,它就能呈现出原先语 言的文本。

  你可以将“http://66.249.93.104/translate_c?hl=zh-CN& langpair=en%7Czh-CN&u=http://www.myspace.com/”中的“www.myspace.com”替换成 其它因受阻而无法访问的站点。

  使用这个方法有一个缺陷,那就是它不能为你代理任何图像。这些仍然是直接从目标服务器读取,因此它们仍然会被过滤阻截,你只能看到一个具有文本的页面。

  Google代理技巧2

  第二个可被用作代理服务的Google工具就是Google Wireless Transcoder,这项功能的开发旨在让移动电话的浏览器能够查看网页。它能够下载目标站点的网页,也包括其中的图片,并对整个页面重新定制,以适应 一般移动电话的屏幕大小。而这一切过程都可以即时地完成。

  要使用它的话,你可以进入“http://www.google.com/gwt/n”这个页面,输入你想要查 看的网页。很快,你就可以看到你想要查看的页面的格式已经不再是原先的样子,只留下了非常简单的单一列的页面。你也能够看到,所有的图像都被调整成了适应 移动电话屏幕的大小。也许这样的页面略微有些质量下降,但这毕竟是Google从它的服务器上下载并提交出的调节后的版本。因此,如果你要访问的是一个列 入黑名单的站点,你也仍然能够查看到其中的图像,只不过它们要比正常的情况下尺寸要缩小一些罢了。

  此外,我发现大多数的网站在使用了Google Wireless Transcoder之后,看起来效果都还好。

(转)vb与网站进行交互,从应用程序提交数据到网站

2008-01-18,星期五 | 分类:编 程|VisualBasic | 195 views

程序思路:利用ASP的GET方法提交数据,并用INI文件方式获取数据和提交状态
实现方法:利用相关函数获取特定html源代码,从而也调用了此页面
利用获取http://123.com/123.asp?id=123
这样既获取了 http://123.com/123.asp?id=123的代码 又在内存中打开了http://123.com/123.asp?id=123这个网页 从而现实了GET方式提交数据

在窗体上添加一个 按钮和2个textbox
代码:
Option Explicit
‘强制声明变量

Private Declare Function GetPrivateProfileString Lib “kernel32″ _
Alias “GetPrivateProfileStringA” (ByVal lpApplicationName As String, _
ByVal lpKeyName As Any, ByVal lpDefault As String, _
ByVal lpReturnedString As String, ByVal nSize As Long, _
ByVal lpFileName As String) As Long
‘读取INI文件函数,作用:获取数据提交结果

Dim httpurl As String ‘用来存放提交地址
Dim htmlcode As String ‘用来存放提交结果与获取HTML代码

Private Sub Command1_Click()
Dim urlstr As String

If Trim(Text1.Text) = “” Then MsgBox “数据1不能为空”: Exit Sub
If Trim(Text2.Text) = “” Then MsgBox “数据2不能为空”: Exit Sub
‘判断数据是否为空

urlstr = httpurl & “?date1=” & Text1.Text & “&date2=” & Text2.Text & “&md5=” & _
MD5.MD5(Text1.Text & “_” & Text2.Text)
‘MD5是为了验证数据,防止非应用软件来源数据 可以改成    MD5.MD5(”111111111″ & Text1.Text & “_” & Text2.Text) 格式从而不可破解
‘用ASP   GET 方式提交表单

htmlcode = gethtm.getHTTPPage(urlstr)
htmlcode = Trim(htmlcode)

If Left(htmlcode, 7) = “连接服务器失败” Then MsgBox htmlcode, 16, “数据提交程序”:   Exit Sub
‘当无法连接服务器时,程序给出处理

Dim savName As String, savText As String, filename As String
savName = App.Path & “/temp.ini”
Open savName For Output As #1
savText = htmlcode
‘如果用write写文件,文本的内容会有双引号
Print #1, savText
Close #1
‘将获取的网页HTMK文件(含提交状态)写入临时文件

Dim ret As Long
Dim buff As String
‘——————————
buff = String(255, 0)
ret = GetPrivateProfileString(”数据提交程序”, “state”, “发生未知错误,或无法连接到指定地址!”, buff, 256, App.Path & “/temp.ini”)
buff = del34(buff)
MsgBox buff, vbInformation, “『数据提交程序』数据提交程序”
‘提示提交状态

Kill App.Path & “/temp.ini”
‘删除临时文件

If buff = “数据提交成功!” Or buff = “数据提交成功!” Then
Text1.Text = “”
Text2.Text = “”
End If
‘如果提交成功,则清空textbox中的数据

End Sub

Private Sub Form_Load()
httpurl = “http://127.0.0.1/123.asp”               ‘指定提交地址
End Sub
Function del34(a As String) As String          ‘函数作用:去除INI获取的无效字符
Dim i As Integer
del34 = “”
For i = 1 To Len(a)
If Asc(Mid(a, i, 1)) <> 0 Then del34 = del34 & Mid(a, i, 1)
Next

End Function

gethtm模块函数代码:
Function getHTTPPage(url)
On Error GoTo e:
Dim Http
Set Http = CreateObject(”MSXML2.XMLHTTP”)
Http.Open “GET”, url, False
Http.send
If Http.ReadyState <> 4 Then
Exit Function
End If
getHTTPPage = BytesToBstr(Http.responseBody, “GB2312″)
Set Http = Nothing
If Err.Number <> 0 Then Err.Clear

Exit Function
e:
If Err.Number = -2146697211 Then
getHTTPPage = “连接服务器失败,请检查网络连接!”
Else
getHTTPPage = “未预期的错误!”
End If
End Function

Function BytesToBstr(body, Cset)
Dim objstream
Set objstream = CreateObject(”adodb.stream”)
objstream.Type = 1
objstream.Mode = 3
objstream.Open
objstream.Write body
objstream.Position = 0
objstream.Type = 2
objstream.Charset = Cset
BytesToBstr = objstream.ReadText
objstream.Close
Set objstream = Nothing
End Function
MD5模块代码和ASP代码请自主下载

(转)VB写WebBrowser捕捉信息(三)

2008-01-06,星期天 | 分类:编 程|VisualBasic | 142 views

第三部分   自动拨号、自动挂断以及自动处理中途掉线

     一个出色的“自动上网机器人”程序应能按照既定的时间准时开始拨号、并当所需任务已完成后立即挂断。而且仅做到这些还不够,它还应在发出拨号指令后跟踪拨号操作是否真的成功、上网速度如何、是否需要挂断后重新拨号、自动浏览过程中是否出现掉线、以及最终的挂断操作是否真的成功完成,等等。

     因此,“机器人”程序应定时检查在线状况,以保证浏览时一定在在线状态、浏览完毕后一定不在在线状态。同时还要检查浏览进度,当浏览速度过慢时尝试挂断后重新拨号。

     本部分讨论了实现“自动拨号”、“检查在线状况”、以及“自动挂断”这三个功能的若干方法,比较了诸方法各自的优劣,并总结给出了使用建议。本部分的示例程序将这三个功能的诸方法集成在一起,以便于大家对比使用(见下图)。

1. 自动拨号

    方法1A:使用rnaui.dll

     rnaui.dll是微软的“拨号网络用户接口”程序集,一般在“\Windows\System”目录下。其中的RnaDial程序用于启动拨号。该程序可在命令行执行(在“开始”->“运行”中键入):

    rundll32.exe rnaui.dll,RnaDial <拨号网络连接名>

     其中的“RnaDial”和“<拨号网络连接名>”是区分大小写的。

     但由于上述命令仅启动拨号窗口而未立即开始拨号,因此在程序中使用时还应再

送出模拟“回车”的按键:

ret = Shell("rundll32.exe rnaui.dll,RnaDial " + 连接名, 1)
     SendKeys "{enter}", True

    方法1B:使用wininet.dll

     wininet.dll是微软的Internet扩充函数集,一般在“\Windows\System”目录下。其中的InternetAutodial、InternetAutodialHangup和InternetGetConnectedState三个函数分别可完成自动拨号、自动挂断和判断在线状态等任务。InternetAutodial的定义为:

Private Declare Function InternetAutodial Lib "wininet.dll" _
            (ByValdwFlags As Long, ByValdwReserved As Long) As Long

         若将第一个参数(dwFlags)的值设为2,该函数无需用户干预就可自动拨号。但使用该函数有一个前提:即必须将“Internet 属性”->“连接”设成“始终拨打默认连接”(见下图)。

     用InternetAutodial函数自动拨号的情况可参见下图。从图中可以看出,该方法可自动重试多次。具体的重试次数在默认连接的“设置”->“高级”中定义:

 

    方法1C:使用RAS

     RAS 是微软的远程访问服务(Remote Access Service)API集合。其中的 API函数RasDial可完成拨号任务。但由于该函数在使用上略显复杂而不太常用,故示例程序中未采纳。

     自动拨号方法小结:rnaui方法使用起来最简单,又由于它不一定非要使用默认连接,因此也最灵活。但这种灵活恰恰又给它带来了弱点,即如果不提供连接名,该方法不会自动调用默认连接。此外,这种方法还有两个最大的缺点:一是仅拨号一次,若出现占线或没有响应等情况时不会自动重试;二是调用程序不容易得到拨号是否成功的返回值。相比之下,wininet方法虽仅能拨打默认连接(无默认连接时,使用第一个连接),但它可多次试拨,并且InternetAutodial函数等待拨号成功或所有试拨结束以便给调用程序返回拨号是否成功的值,因此,在“自动上网机器人”的环境中wininet方法是最适宜的。

2. 检查在线状况

    方法2A:wininet方法

     若InternetGetConnectedState函数返回True,则为在线状态。该方法最大的缺点是:若当前连接不是用wininet方法建立的,则返回值可能不准确。

    方法2B:查找窗口法

     拨号连接成功后,下图所示的窗口一定存在(不管它是最小化在任务栏的最右端,或是开启为下图所示的状态):

       用FindWindow API函数找到该窗口即意味着当前在线。此外,查找窗口法的另一个用处是查找“重新连接”窗口:当中途掉线时,操作系统往往会询问你是否重新连接,找到该窗口并发出模拟“回车”按键即可实现再拨号。

     查找窗口法的缺点是:由于找窗口时需要提供窗口标题,因此即使使用的是默认连接也必须事先知道默认连接名。

    方法2C:RAS 方法

     先用RasEnumConnections函数返回整个RAS集合,再用RasGetConnectStatus函数判断第一个 RAS连接的状态。RAS方法的最大优点是:不管当前连接是否是用wininet建立的,RAS 方法均可对在线状态做出正确判断。

    方法2D:注册表法

     在线时,注册表的“\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\RemoteAccess”处有键值“Remote Connection”,且其值不为零;不在线时,该处无“Remote Connection”键值(当本次系统启动后从未拨号成功时),或者其值为零(表明曾拨号成功,但现在已断掉)。

  检查在线状况之方法小结:由于wininet方法的局限性,一般我们应避免使用之;查找窗口法是可靠的,只是要知道连接名;因此我们推荐使用RAS 方法和注册表法。

3. 自动挂断

    方法3A:wininet法

  使用InternetAutodialHangup函数。同样地,若当前连接不是用wininet方法建立的,则返回值可能不准确(即不能成功挂断)。

    方法3B:窗口查找法

  找到图九所示的窗口,然后用ShowWindow API函数使之成为当前窗口,最后发出模拟<Alt>+C的按键操作(从图九中可以看出,<Alt>+C是“断开连接”按键的快捷方式)。

    方法3C:RAS 法

  用RasHangUp函数执行挂断。不管用何种方法建立的连接,RAS 法均能可靠地完成任务。

  自动挂断方法小结:相比之下,窗口查找法和RAS 法是可以信赖的。

4. 本部分总结

  综上所述,对于“自动拨号”、“检查在线状况”、以及“自动挂断”的各种方法,我们推荐“1A-2C-3C”组合。当然各方法可综合使用(如加入2D、3B等),以确保万无一失。在具体编程时还应注意:拨号后判断结果,如不成功应重新拨号;任务进行过程中定时检查在线状态,出现掉线后应及时处理;最后的挂断操作后应再查在线状态,以确保挂断成功。

     下面是实例程序的完整代码。源代码中的全局定义已按照wininet、RAS、注册表等进行分类,各具体方法也均按序排列,以便于大家挑选使用。该程序的执行情况在本部分的开始处已给出(图六)。

‘ 程序三:自动拨号、自动挂断以及自动处理中途掉线

Option Explicit
‘ 有关 wininet 的全局定义
Private Const INTERNET_AUTODIAL_FORCE_UNATTENDED = 2
Private Const INTERNET_CONNECTION_MODEM = 1
Private Declare Function InternetAutodial Lib "wininet.dll" _
(ByVal dwFlags As Long, ByVal dwReserved As Long) As Long
Private Declare Function InternetAutodialHangup Lib _
"wininet.dll" (ByVal dwReserved As Long) As Long
Private Declare Function InternetGetConnectedState Lib _
"wininet.dll" (ByRef lpdwFlags As Long, ByVal _
dwReserved As Long) As Long
‘ 有关“窗口查找”的全局定义
Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function ShowWindow Lib "user32" _
(ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Private Const SW_SHOW = 5
‘ 有关 RAS 的全局定义
Private Const RASCS_DONE = &H2000&
Private Const RAS_MaxEntryName = 256
Private Const RAS_MaxDeviceType = 16
Private Const RAS_MaxDeviceName = 128
Private Type RASCONN
dwSize As Long
hRasConn As Long
szEntryName(RAS_MaxEntryName) As Byte
szDeviceType(RAS_MaxDeviceType) As Byte
szDeviceName(RAS_MaxDeviceName) As Byte
End Type
Private Type RASCONNSTATUS
dwSize As Long
RasConnState As Long
dwError As Long
szDeviceType(RAS_MaxDeviceType) As Byte
szDeviceName(RAS_MaxDeviceName) As Byte
End Type
Private Ras_Buf(255) As RASCONN
Private Ras_Status As RASCONNSTATUS
Private lpcb As Long
Private lpcConnections As Long
Private Declare Function RasEnumConnections Lib _
"rasapi32.dll" Alias "RasEnumConnectionsA" (lprasconn _
As Any, lpcb As Long, lpcConnections As Long) As Long
Private Declare Function RasGetConnectStatus Lib _
"rasapi32.dll" Alias "RasGetConnectStatusA" (ByVal _
hRasConn As Long, lpRASCONNSTATUS As Any) As Long
Private Declare Function RasHangUp Lib "rasapi32.dll" _
Alias "RasHangUpA" (ByVal hRasConn As Long) As Long
‘ 有关“注册表”的全局定义
Private Const HKEY_LOCAL_MACHINE = &H80000002
Private Declare Function RegOpenKey Lib "advapi32.dll" Alias _
"RegOpenKeyA" (ByVal hKey As Long, ByVal lpSubKey As _
String, phkResult As Long) As Long
Private Declare Function RegQueryValueEx Lib "advapi32.dll" _
Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal _
lpValueName As String, ByVal lpReserved As Long, lpType _
As Long, lpData As Any, lpcbData As Long) As Long
Private Declare Function RegCloseKey Lib "advapi32.dll" _
(ByVal hKey As Long) As Long
Dim ret As Long
‘自动拨号
Private Sub wininet拨号测试_Click()
If InternetAutodial(INTERNET_AUTODIAL_FORCE_UNATTENDED, 0) _
Then MsgBox "已连接(wininet法)"
End Sub
Private Sub rnaui拨号测试_Click()
ret = Shell("rundll32.exe rnaui.dll,RnaDial " + Text1, 1): DoEvents
SendKeys "{enter}", True: DoEvents
End Sub
‘检查是否断线
Private Sub wininet方法_Click() ‘ wininet法检查是否断线
If InternetGetConnectedState(INTERNET_CONNECTION_MODEM, 0) Then
MsgBox "在线."
Else
MsgBox "当前未连接。"
End If
End Sub
Private Sub 查找窗口法_Click() ‘ 查找窗口法检查是否断线
ret = FindWindow("#32770", "重新连接")
If ret <> 0 Then
Call ShowWindow(ret, SW_SHOW)
SendKeys "{enter}", True: Exit Sub
End If
ret = FindWindow("#32770", "连接到 The95963")
If ret <> 0 Then
MsgBox "在线."
Else
MsgBox "当前未连接。"
End If
End Sub
Private Sub RAS方法_Click() ‘ RAS方法检查是否断线
Ras_Buf(0).dwSize = Len(Ras_Buf(0)) + 1
lpcb = 256 * Ras_Buf(0).dwSize
ret = RasEnumConnections(Ras_Buf(0), lpcb, lpcConnections)
If ret Then
MsgBox "出错!": Exit Sub
End If
Ras_Status.dwSize = Len(Ras_Status) + 2
ret = RasGetConnectStatus(Ras_Buf(0).hRasConn, Ras_Status)
If ret = 0 And Ras_Status.RasConnState = RASCS_DONE Then
MsgBox "在线."
Else
MsgBox "当前未连接。"
End If
End Sub
Private Sub 注册表法_Click() ‘ 注册表法检查是否断线
Dim SubKey As String, ValueName As String
Dim Data As Long, Result As Long
SubKey = "System\CurrentControlSet\Services\RemoteAccess"
ret = RegOpenKey(HKEY_LOCAL_MACHINE, SubKey, Result)
If ret = 0& Then
ValueName = "Remote Connection"
ret = RegQueryValueEx(Result, ValueName, 0&, 0&, ByVal Data, 0&)
ret = RegQueryValueEx(Result, ValueName, 0&, 0&, Data, Len(Data))
If ret = 0& And Data <> 0 Then
MsgBox "在线!"
Else
MsgBox "当前未连接。"
End If
RegCloseKey (Result)
End If
End Sub
‘自动挂断
Private Sub wininet法_Click() ‘ wininet法自动挂断
If InternetAutodialHangup(0) Then MsgBox "已挂断(wininet法)"
End Sub
Private Sub 窗口查找法_Click() ‘ 窗口查找法自动挂断
ret = FindWindow("#32770", "连接到 The95963")
If ret <> 0 Then
Call ShowWindow(ret, SW_SHOW)
SendKeys "%c", True
MsgBox "已挂断(窗口查找法)"
End If
End Sub
Private Sub RAS法_Click() ‘ RAS法自动挂断
Ras_Buf(0).dwSize = Len(Ras_Buf(0)) + 1
lpcb = 256 * Ras_Buf(0).dwSize
ret = RasEnumConnections(Ras_Buf(0), lpcb, lpcConnections)
If ret Then
MsgBox "出错!": Exit Sub
End If
Ras_Status.dwSize = Len(Ras_Status) + 2
ret = RasGetConnectStatus(Ras_Buf(0).hRasConn, Ras_Status)
If ret = 0 And Ras_Status.RasConnState = RASCS_DONE Then
If RasHangUp(Ras_Buf(0).hRasConn) = 0 Then _
MsgBox "已挂断(RAS法)"
End If
End Sub

来源:肥菜刀的专栏

(转)VB写WebBrowser捕捉信息(二)

2008-01-06,星期天 | 分类:编 程|VisualBasic | 318 views

第二部分   将网页上的二维表导入数据库

     在上一部分中,我们讨论了让程序自动在网上浏览并将所需的数据准确、快速地存储下来的方法。现在,我们将迎接更大的挑战:将网页上以表格形式存在的二维数据提取出来,并存成可直接导入数据库的“Microsoft Excel 逗号分隔值文件”(即.csv文件)。

     用手工在网页上直接提取类似上图中所示的表格数据是非常困难的。如果这样的表格有数十页甚至上百页之多,手工提取工作将是不可想象的,而且非常容易出错。

     本部分的实例是:将沪深两市全部约1100家个股的财务评分表数据(共54页,每页20家,如上图所示)快速、准确地转换成“.csv”文件。

1. 自动设置CheckBox的值

     由于只有注册用户才能访问上述财务评分表,因此实例程序首先演示了自动注册的功能。下图显示的是注册前以及自动注册后的画面。

http://www.ccidnet.com/tech/guide/2001/08/20/image/pic5.jpg

     我们在上一部分中已讨论了自动填写输入区以及自动点击按钮等的方法。对于自动设置CheckBox值,其方法完全类似:首先要搜索到该CheckBox的名字,然后将该对象的Checked属性置为True或False即可。

2. 将网页上的二维表导入数据库

    首先定义一个IHTMLElementCollection对象用于收集网页上所有的 Table,然后用getElementsByTagName方法执行收集工作:

Dim Tables AsIHTMLElementCollection
    Set Tables = WebBrowser1.Document.getElementsByTagName("Table")

 

 

     一个网页上往往有多个 Table。我们用HTMLTable对象来处理每个Table:

Dim Table1 AsHTMLTable
     For Each Table1 In Tables
     Next

     HTMLTable对象的innerText属性记录了整个 Table的全部信息,包括字段名。因此我们可以根据字段名判断出哪个 Table是我们需要的。

     为了逐行逐列地提取数据,我们还需要HTMLTableRow对象和HTMLTableCell对象:

Dim Row AsHTMLTableRow, Cell As HTMLTableCell
     For i = 1 To Table1.rows.length - 1       ‘ 逐行处理
        Set Row = Table1.rows(i)
        j = 0
        For Each Cell In Row.cells       ‘ 逐列处理
           ‘ Row.cells(j).innerText即为当前行及当前列上的单元数据
           Text1 = Text1 + Trim(Row.cells(j).innerText) + ","
           j = j + 1
        Next
        ‘ 一行处理完毕后,去除行尾的逗号并加上回车
        Text1 = Left(Text1, Len(Text1) - 1) + vbCrLf
     Next

     至此,当前网页上的二维表已转换成“.csv”格式。

3. 自动浏览时的页面控制技巧

     我们从上个例子中就已经清晰地看到,自动浏览程序的主体是WebBrowser控件的DocumentComplete事件。只有在当前页面已被完全调入后,我们才能开始对当前页面进行数据处理,然后再根据当前在哪个页面来决定下一步的浏览方向。

     需要指出的是,DocumentComplete事件的发生并不一定意味着当前页面已被全部调入。如果页面上没有其它子框架(frames),发生DocumentComplete事件即表明当前页面(即主框架)已完成调入;若页面上有多个框架,则每个框架完成时都会发生DocumentComplete事件;当所有子框架都完成后,主框架最后产生一次DocumentComplete事件。为了判断出这最后一次DocumentComplete事件,需要比较每次事件发生时的对象(pDisp)是否是WebBrowser控件对象本身:

Private Sub WebBrowser1_DocumentComplete(ByValpDisp As Object, _
                                              URL As Variant)
       If (pDisp Is WebBrowser1.Object) Then
         Debug.Print "Document is finished loading."
       End If
     End Sub

    下面是实例程序的完整代码(运行该程序可得到完整的1061行“.csv”格式的数据,分别代表1061个上市公司的财务信息。该文件可直接导入Access数据库或 Excel中。):

‘ 程序二:将网页上的二维表导入数据库

‘ 为运行本程序,应在“菜单->工程->部件”中添加“Microsoft Internet Controls”
‘ 并在“菜单->工程->引用”中添加“Microsoft HTML Object Library”

Option Explicit
Dim Page As Long
Private Sub Form_Load()
Form1.MousePointer = 11
WebBrowser1.Navigate "www.stockstar.com.cn" ‘ 起始网址
End Sub
Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant)
Dim Table1 As HTMLTable, Tables As IHTMLElementCollection
Dim Row As HTMLTableRow, Cell As HTMLTableCell
Dim i, j, tmp
Text2 = WebBrowser1.LocationURL ‘ 显示当前网址
‘ 判断当前网页是否全部调入完毕
If Not (pDisp Is WebBrowser1.Object) Then Exit Sub
On Error Resume Next
Select Case Text2
Case "http://www.stockstar.com.cn/home.htm" ‘ 当进入主页面时执行以下程序
‘ 用户注册登录
For i = 0 To WebBrowser1.Document.Forms(0).length - 1
‘ 找到 CheckBox 后,将其值改为 False,以防止用户名及密码被存储
If WebBrowser1.Document.Forms(0)(i).Name = "checkSavePW" Then _
WebBrowser1.Document.Forms(0)(i).Checked = False
If WebBrowser1.Document.Forms(0)(i).Name = "userId" Then _
WebBrowser1.Document.Forms(0)(i).Value = "kompass_china"
If WebBrowser1.Document.Forms(0)(i).Name = "passwd" Then _
WebBrowser1.Document.Forms(0)(i).Value = "kompass1"
‘ 此处是按名字访问按钮(上例中是按值访问按钮)
If WebBrowser1.Document.Forms(0)(i).Name = "continue" Then _
WebBrowser1.Document.Forms(0)(i).Click
Next
Case "http://my.stockstar.com/scripts/mystockstar.dll?login"
‘ 当用户登录完成后,准备打开表格的第一页
WebBrowser1.Navigate "http://finance.stockstar.com/scripts/finance.dll?" + _
"showstkdfpm&begin=0&ret=1&index=2&concode=01"
Page = 1
Case Else ‘ 当进入数据页面(表格的第一页至最后一页)时执行以下程序
Set Tables = WebBrowser1.Document.getElementsByTagName("Table")
For Each Table1 In Tables
If Left(Table1.innerText, 2) = "名次" Then ‘ 找到需要的Table
‘ 将表格转换成“.csv”格式
For i = 1 To Table1.rows.length - 1
Set Row = Table1.rows(i)
j = 0
For Each Cell In Row.cells
Text1 = Text1 + Trim(Row.cells(j).innerText) + ","
j = j + 1
Next
Text1 = Left(Text1, Len(Text1) - 1) + vbCrLf
Next
‘ 数据存盘
Open "C:\Data.csv" For Append As #1
Print #1, Left(Text1, Len(Text1) - 2): Text1 = "": Close #1
Exit For
End If
Next
‘ 准备打开下一页
Page = Page + 1
tmp = "http://finance.stockstar.com/scripts/finance.dll?showstkdfpm&ret=" + _
Trim(Str(Page)) + "&index=2&concode=01"
If Page <= 54 Then ‘ 判断是否浏览结束
WebBrowser1.Navigate tmp
Else
‘ 上网任务完成后,应在此调用自动挂断过程。
Form1.MousePointer = 0
MsgBox "Finished!!": End
End If
End Select
End Sub

    以下给出的是上述程序所存数据文件的片段:

1,乐凯胶片,600135,材料,81.493,18.445,23.165,8.850,20.717,10.315
2,歌华有线,600037,传播娱乐,80.553,13.009,22.256,12.141,20.304,12.844
3,外运发展,600270,仓储运输,80.326,17.331,23.005,8.829,19.900,11.261
4,东方钽业,0962,有色金属,80.312,15.160,22.483,11.648,21.290,9.730
5,双汇发展,0895,食品,79.772,15.428,20.673,11.508,20.235,11.930
6,四川美丰,0731,化肥,79.361,15.795,23.235,11.323,16.921,12.088
… … …
1059,轮胎橡胶,600623,车类,7.167,8.265,10.973,-34.411,14.120,8.219
1060,PT吉轻工,0546,日用轻工产品,-11.895,5.740,-49.149,7.999,14.136,9.379
1061,广船国际,600685,机械仪器,-57.452,9.824,-1.528,-89.648,14.366,9.533

来源:肥菜刀的专栏

(转)VB写WebBrowser捕捉信息(一)

2008-01-06,星期天 | 分类:编 程|VisualBasic | 166 views
       在大多数情况下,上网冲浪是件令人愉快的事情。但若是数百上千的超链接摆在你面前,而你又不得不一一点击这些链接、进入相应的网页、手工筛选出每页里你需要的信息、最后再将这些信息编进数据库中、….,你将做何感想?如果每天都从事这种繁杂、枯燥的工作会不会让你发疯?
     “自动上网机器人”或许可救你出“苦海”:你可以喝着咖啡、听着音乐、看着“机器人”辛勤地替你工作,那感觉是不是棒极了!
      本文结合实例详尽讨论了用VB实现“上网机器人”的技术细节。我们知道,搜集和下载资料是人们使用互联网的最主要的目的之一,但有些信息资源过于庞大,用手工摘取的方法是困难的或根本就是行不通的。例如,你需要搜集欧洲进口机械设备的公司名录以便给他们发信邀请其参加博览会,在网上找到这些信息并不难,但出于数据安全等方面的考虑,几乎所有提供类似信息的网站都没有提供直接下载数据的功能。
      要想搜集齐想要的数据,唯一可用的方法就是一页一页地浏览每个公司的信息页,摘取其中有用的数据并存入数据库。但当公司总数超过数千时,巨大的工作量会让任何人望而却步!其实,这浩大的工作完全可以由程序来完成,因为这些任务完全是机械的重复性工作。而且,用程序完成比用手工要快得多。本文涉及的技术细节是通用的,即对实例程序稍加修改就可完成任何“自动上网冲浪”任务。

     自动拨号上网、自动处理中途掉线、任务完成后自动挂断,这些都是“上网机器人”的最基本的功能之一。它还能给你带来明显的经济回报:如果你让“机器人”在晚间至凌晨的上网费优惠期内拨号上网去自动冲浪,那真可称得上是典型的“一石三鸟”—-你睡觉、它工作、还省钱!有关这方面的细节将在本文的第三部分里讨论。该部分提供了实现上述各功能的若干方法,并比较了这些方法各自的优劣。

     本文的第一和第二部分分别以两个实例讨论了自动浏览的技术细节:在网页上的输入区内自动填入数据以便完成诸如用户登录等的操作、自动更新CheckBox、自动选择下拉式列表(ComboBox)的值、自动点击网页上的按钮、从网页上精确提取有用的数据并存盘、将网页上二维表(Table)内的数据一一提取出来并转换且存储成可直接导入数据库或 Excel的格式,以及控制浏览进程的技巧等等。

第一部分   从网页上精确提取数据

     本部分的实例是:下载沪深两市全部约1100家个股的基本信息及财务数据。若用手工操作,如上图所示,需要在股票代码区内分别输入1100个股票代码,在下拉式列表(ComboBox)中分别选择“个股资料”和“财务数据解读”,算下来约是2200次操作!这样的工作当然是由程序来完成划算得多。况且手工提取数据(先选中、再使用Ctrl+C拷贝)极容易出错(多选或漏选),又很费眼神。

1. 在输入区内自动填入数据

     为使程序能高效地自动浏览,需引入一些最基本的功能,如在输入区内自动填入数据、自动点击按钮等等。虽然用变换 URL地址的方法有时也能完成任务,但往往过于费力,尤其当网页上的输入区较多时更是如此。

     为了在输入区内输入数据,需要先搜索到该对象的名字,然后将该对象的值置为要填入的数据即可。搜索名字的工作可编程完成,亦可用 FrontPage轻松获得。

2. 自动在下拉式列表(ComboBox)中进行选择

     同样地,首先要获得下拉式列表的名字。然后根据下拉式列表的元素总数(length属性)在列表中搜索要设置的值(列表的 Options集合中元素的Text属性),找到后,将该元素设为选中元素(元素的Selected属性)。

3. 自动点击按钮

     对于按钮来讲,可根据其名字访问,亦可根据其值访问。按钮的值就是显示在按钮上的文字。一个按钮可能没有名字,但一定有值。本例的程序就是根据值来访问按钮。执行按钮的 Click方法就相当于点击了该按钮。

 

 

     图二中红色箭头所指即为程序自动填入输入框、自动在ComboBox中选择以及自动点击按钮的情况。

4. 精确提取数据

     仅将有用的数据存储下来才是有意义的。必须研究网页,找出有效数据所在的Tag区(可用文本编辑器或 FrontPage),然后用该对象的innerText属性获得最终的文本。本例中要存储的数据如下图所示,其所用的Tag为“PRE”。

     下面给出的是实例程序的完整代码:

‘ 程序一:从网页上精确提取数据

‘ 为运行本程序,应在“菜单->工程->部件”中添加“Microsoft Internet Controls”
‘ 并在“菜单->工程->引用”中添加“Microsoft HTML Object Library”

‘ 为了简洁,程序仅下载九只个股的基本信息
Option Explicit
Private Const Form_ID = 1
Dim Code(9) As String
Dim Current As Long
Private Sub Form_Load()
   Form1.MousePointer = 11
   ‘ 以下是个股代码
   ‘ 为了程序简洁,这里仅使用九只代码。
   ‘ 而在真实环境中,应从数据文件中读入全部个股代码。
   Code(0) = "600001": Code(1) = "600002": Code(2) = "600003"
   Code(3) = "600005": Code(4) = "600006": Code(5) = "600007"
   Code(6) = "600008": Code(7) = "600009": Code(8) = "600010"
   Current = 0
   WebBrowser1.Navigate "www.stockstar.com.cn"    ‘ 起始网址
End Sub

Private Sub WebBrowser1_DocumentComplete(ByValpDisp As Object, URL As Variant)
   Dim i, k
   Text2 = WebBrowser1.LocationURL     ‘ 显示当前网址
   ‘ 判断当前网页是否全部调入完毕
   If Not (pDisp Is WebBrowser1.Object) Then Exit Sub
   On Error Resume Next
   Select Case Text2
   Case "http://www.stockstar.com.cn/home.htm"   ‘ 当进入主页面时执行以下程序
    For i = 0 To WebBrowser1.Document.Forms(Form_ID).length - 1
       ‘ 找到代码输入框后填入个股代码
       If WebBrowser1.Document.Forms(Form_ID)(i).Name = "code" Then _
         WebBrowser1.Document.Forms(Form_ID)(i).Value = Code(Current)
       ‘ 在下拉式列表中进行选择
       If WebBrowser1.Document.Forms(Form_ID)(i).Name = "target" Then
         For k = 0 To WebBrowser1.Document.Forms(Form_ID)(i).length - 1
            If WebBrowser1.Document.Forms(Form_ID)(i).Options(k).Text _
                      = "个股资料" Then
              WebBrowser1.Document.Forms(Form_ID)(i).Options(k).Selected = True
              Exit For
            End If
         Next k
       End If
       ‘ 点击按钮
       If WebBrowser1.Document.Forms(Form_ID)(i).Value = " 查询 " Then _
         WebBrowser1.Document.Forms(Form_ID)(i).Click
    Next
   Case Else    ‘ 当进入数据页面时执行以下程序
    For i = 0 To WebBrowser1.Document.All.length - 1
       If WebBrowser1.Document.All(i).tagName = "PRE" Then
         ‘ 精确提取数据
         Text1 = Text1 + Code(Current) + vbCrLf + _
                 WebBrowser1.Document.All(i).innerText + vbCrLf
         Exit For
       End If
    Next
    ‘ 数据存盘
    Open "C:\Data2.Txt" For Append As #1
    Print #1, Text1: Text1 = "": Close #1
    ‘ 换下一只股票
    Current = Current + 1
    If Current >= 9 Then
      ‘ 上网任务完成后,应在此调用自动挂断过程。
      Form1.MousePointer = 0: MsgBox "Finished!": End
    End If
    ‘ 回退到主页面,查询下一只股票的信息
    WebBrowser1.GoBack
   End Select
End Sub

来源:肥菜刀的专栏

利用WebBrowser获得页面部分数据

2008-01-06,星期天 | 分类:编 程|VisualBasic | 152 views

Internet Explorer 不只是一个程序,更是许多可重复使用组件的集合与容器。在拆取 Web 页时,最有意思的两个组件是 shdocvw.dll 和 mshtml.dll。第一个组件 shdocvw.dll,包含称为 WebBrowser 的 Microsoft(R) ActiveX(R) 控件,它真实地显示 Web 页。在运行 Internet Explorer 时,显示 Web 页的主窗口就是这样的控件。第二个组件 mshtml.dll,含有能分析 WebBrowser 控件中所包含文档的 HTML 分析器。

可能有这种情况,在您的应用程序内部,已经用 WebBrowser 控件来驻留 Web 页,但仍需要重新创建一个小浏览器来启动 Web 页的拆取。

在文件菜单上,请单击新建工程,以创建“标准 EXE”,然后在工程菜单上单击部件,以添加 Microsoft HTML Object Library 和 Microsoft Internet Controls。

在工具箱中,可看见 WebBrowser 组件。拖动其中之一,文本框和主窗体上的命令按钮。将此文本框的 Text 属性设置为 “http://www.asp001.net/test.htm”,将此命令按钮的 Caption 属性设置为“浏览(&B)”。

双击该命令按钮,然后在事件处理器中放入下列代码,导航至文本框中命名的 Web 站点:
Private Sub Command1_Click()
     WebBrowser1.Navigate Text1.Text
End Sub

保存并运行该程序。试着按浏览按钮,导航到文本框中指定的站点。您已经创建了一个基本的 Web 浏览器 — 就其本身而言没什么用,甚至没什么意义,但它却是迈向 Web 拆取技术的第一步。

回到工程中,在代码窗口中选择 WebBrowser1 对象,然后选择 DocumentComplete 的事件处理器。一旦整个 Web 页下载到此浏览器中,即触发该事件:
Private Sub WebBrowser1_DocumentComplete_
(ByVal pDisp As Object, URL As Variant)

End Sub

传递到该事件中的 URL 就是我们导航所至的位置,它在日后确定浏览器所在的页面时将更为有用。WebBrowser 控件有一个属性称为 Document(文档),可将其视为 IHTMLDocument 来处理:

Private Sub WebBrowser1_DocumentComplete(_ ByVal pDisp As Object, URL As Variant)
     Dim Doc As IHTMLDocument2
     Set Doc = WebBrowser1.Document
     //下一步:分析该文档
End Sub

较新的 IHTMLDocument2 具有 IHTMLDocument 中无法使用的特性。可对系统使用 IHTMLDocument 替代老版本的 Internet Explorer,如果您有勇气的话,甚至可以使用 IHTMLDocument3。补充说明一下,我们假设您已经导航到 Word 文档或 XML 文档,而非 HTML 文档。不要将变量 doc 声明为 IHTMLDocument2,可将其声明为 Word 的文档或 XML 的 DOMDocument。

在进行下一步之前,理解 HTML 文档的结构是非常重要的。和 XML 不一样,HTML 文档的组合有一定的自由度。例如,您会遇到未关闭标记的 HTML 文档。HTML 文档确实有某种结构。结构好的 HTML 文档通常具有下列元素:

<HTML>
   <HEAD>
       header information like the <TITLE>
   </HEAD>
   <BODY>
       elements like <TABLE> and <A> and <IMG>
   </BODY>
</HTML>

请注意 HTML 的树状结构。标记包含标记又包含标记,如此等等。特别是,每一个标记元素都包含一个 0 到 n 个标记元素的集合。<TABLE> 标记可以包含 <TR> 标记。每个 <TR> 标记可以包含 <TD> 标记,后者又可以包含其他标记如锚或图像等。

现在,分析整个 http://moneycentral.msn.com/,并在带 MSFT 符号的页填上第二个 <INPUT> 标记。然后,调用此窗体上的提交:
Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant)
     Dim doc As IHTMLDocument2
     Set doc = WebBrowser1.Document
    
     If URL = _
     "http://moneycentral.msn.com/home.asp" Then
         ’填充带输入标记的元素集合
         Dim Inputs As IHTMLElementCollection
         Set Inputs = doc.All.tags("INPUT")
         ’选择第一个输入标记
         Dim Element As IHTMLElement
         Set Element = Inputs.Item(1, 1)
        
         ’使用正确的界面
         Dim InputElement As IHTMLInputElement
         Set InputElement = Element
         InputElement.value = Text1.Text
        
         ’调用此页第一个窗体上的提交
         doc.Forms.Item(0, 0).submit
End Sub

在此您会看到,标记集合如何包含可视为其特定类型的标记。每一个标记都可用 IHTMLElement 界面表示,或用指定为该标记类型的界面表示。例如,<TABLE> 标记可用 IHTMLTableElement 或 IHTMLElement 表示。

标记的集合都包含下列重要的方法和属性:

长度。可将其理解为计数,或集合中项目的数量。

项目。用于选择集合中的特殊元素。“项目”有两个参数,第二个参数即命名的标记。

标记。将要过滤的元素传递给标记。标记 ("A") 将返回集合内所有锚的集合。要想有效地拆取页,就需要学会使用标记集合。
现在可能您会问,“为什么不直接转到 http://moneycentral.msn.com/scripts/webquote.dll?ipage=qd&Symbol=msft?” 当然是可以的,但这个例子告诉大家如何在更复杂的情况下操纵 HTML 窗体。

如果您未做进一步的改动即运行该程序,就会注意到它将陷入无休止的循环,没完没了地下载同一个页面。程序不断地寻找要填充的窗体,并反复调用 DocumentComplete。要修正这个缺陷,应在 DocumentComplete 中置入一些逻辑,告诉分析器,只有在正确的页面上才提交窗体。

接下来,让我们放入这个逻辑,并引入实际的股票报价。另外,我们不捕获文本框中的 URL,而是捕获股票符号:
Private Sub Command1_Click()
     WebBrowser1.Navigate _
     "http://moneycentral.msn.com/home.asp"
End Sub
Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object, URL As Variant)
     Dim doc As IHTMLDocument2
     Set doc = WebBrowser1.Document
    
     If URL = "http://moneycentral.msn.com/home.asp" Then
         ’填充带输入标记的元素集合
         Dim Inputs As IHTMLElementCollection
         Set Inputs = doc.All.tags("INPUT")
         ’选择第一个输入标记
         Dim Element As IHTMLElement
         Set Element = Inputs.Item(1, 1)
        
         ’使用正确的界面
         Dim InputElement As IHTMLInputElement
         Set InputElement = Element
         InputElement.value = Text1.Text
        
         ’调用该页第一个窗体上的提交
         doc.Forms.Item(0, 0).submit
     ElseIf URL = _
     "http://moneycentral.msn.com/scripts/webquote.dll?ipage=qd&Symbol=" _
                 & Text1.Text Then
         Dim Tables As IHTMLElementCollection
         Set Tables = doc.All.tags("TABLE")
         ’获得第 14 个表的第二个项目(基于 0)
         Dim Quote As IHTMLElement
         Set Quote = _
         Tables.Item(14, 14).All.tags("TD").Item(2, 2)
         ’显示开始标记和结束标记之间的文本
         MsgBox Quote.innerText
     End If
End Sub

到了这最后一步,自定义的浏览器已被转入有效的 Web 拆取器。重要的是,要注意有了 IHTMLElement 之后获得文本的可用选项。有 4 个属性:

innerText:开始标记和结束标记之间的文本。

innerHTML:开始标记和结束标记之间的文本和 HTML。

outerText:对象的文本。

outerHTML:对象的文本和 HTML。
还要注意从第 4 个表(基于 0)的第 11 个元素中检索到的最终报价字符串。如果 MoneyCentral? 决定重新调整该页怎么办?您最好的策略是根据合理的假定来查询页面。如果您知道报价几乎总是放在新闻标题的前面,那么就从新闻标题往回查询那个表。还有一种策略是,当更改页面的格式时,有一种简单的方法来更新分析器。一种方法就是将分析的职能细分为较小的组件。每个组件可以实现一个预定义的界面,接受要分析的 IHTMLDocument。与实际的 Web 页失去同步的分析组件可被替换。这样带来的好处是,多个编程人员都可以编写分析器,只需给定要实现的界面和要拆取的 Web 站点即可。

为了避免复杂,将 IHTMLDocument 从 DocumentComplete 函数传递 COM DLL,后者可以分析 IHTMLDocument 并返回想要的有效负载。这有利于程序的模块化,并易于更新与 Web 站点失去同步的分析部分。它还使多个开发者能同时处理这个项目,因为他们有一个干净的界面来编写分析器。

在把新的程序推向市场以前,还有几个实际问题要考虑。首先,很可能 MoneyCentral 和其他许多站点不愿意别人下载他们的内容,也不喜欢看广告。您可能得与摘取其内容的站点签订一份协议。

还有很重要的一点要注意,即如果您是 Web 站点的操作员,那么还有更好的办法将您的内容提供给其他系统。虽然可以让其他人来拆取您的 Web 页,但这仍很笨拙。还有一个更好的方法是,提供 XML 来表现内容。并且,随着 XML 被广泛采用,Web 站点开始提供其数据的 XML 表现形式以及 HTML 界面,也不值得大惊小怪。在这样的时刻到来之前,您也许还得拆取 Web 页。Web 页的拆取往往失之笨拙,但 Microsoft HTML 分析器可令其稍微好一些。

Pages: 1 2 Next