查看: 7429|回复: 2
打印 上一主题 下一主题

如何使用Python进行Web数据处理

[复制链接]
跳转到指定楼层
1#
发表于 2007-10-10 08:58:17 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
台州网址导航
在上篇文章中,我简单介绍了Python语言。如果你对这门语言还不太熟悉,你需要复习前面的内容。假定我们现在已经掌握了Python语言的基本知识,现在让我们来使用Python语言做一些更有意义的工作。

几个星期以前,我收到来自web站点发送的一封e-mail,通知我的网络空间就要到期了,在他们删除空间上的文件之前,我还有一个月的时间来备份文件。我在这一站点上只存储了一些老照片,也不是十分重要,但我还是打算将它们保存下来。

我并没有一张张地保存,而是在网站上留言说要自己写东西处理这些文件。于是我和网站就着手开发一个命令行程序,这个程序能够处理Web页面,打印页面中的图片地址。在这篇文章结束之际,我们已经完成了通过HTTP打开和读取HTML数据,定义函数,能接收多样的用户输入,并且使用正规表达式来处理正文。


函数定义

首先我们需要掌握Python语言的一项基本特征:函数。我们将实现一定功能的代码模块称之为函数,并赋予该段代码一个函数名。以后使用这段代码时,不需要再输入全部的代码,而通过函数名直接调用这段代码。Python中函数的定义很简单,如下:

def hello(name):

print "hello " + name

Def关键字后面的字符就是函数名,双括号中的字符就是函数参数——也就是函数的输入值。调用函数也十分简单,如下:

>>> hello("world")

hello world

>>> hello("everyone")

hello everyone

在所有的程序设计中,使用函数是一个好主意。通过使用函数,我们不再需要拷贝和粘贴代码,从而也减轻了程序的维护工作。同时,根据代码的功能对程序代码进行组合,使得程序更易懂和易维护。

管理用户输入

在任何时候,程序都是依赖于用户的输入工作。如果用户的输入有误,就会导致程序出错。大多数时候,如果程序出错的时候能够给出错误提示并且关闭程序,那么程序已经做得不错了。当然,程序可以进一步的完善,例如更正用户的输入,使得程序能继续运行。在下面的程序中,用户需要提供一个网址作为函数参数,所以我们将检查用户输入是否是一个合法的网址——这一程序是为Web站点开发,所以我们只接受使用HTTP协议的地址。我们将会编写函数来检查这一点,如果没有给出协议名称,就自动添加HTTP协议标准。完整的函数代码如下。接下来我们将详细分析这段代码,所以现在不理解也不要担心:

def parseAddress(input):

if input[:7] != "http://":

if input.find("://") != -1:

print "Error: Cannot retrieve URL, protocol must be HTTP"

sys.exit(1)

else:

input = "http://" + input

return input

首先,我们定义一个parseAddress函数,该函数需要一个参数,我们称之为input。下一步我们需要判断是否得到一个正确的地址,我们检查字符串的起始字符(要记得input[:7]返回输入字符串的一部分,从开始字符直到第七个字符)是否为“http://”。如果是,就没有问题。反之,程序就会出错。但是,如果只是没有指定网络协议,我们可以假定用户提供的是没有指定网络协议的http地址。我们可以使用字符串函数find来检查是否指定了协议。find函数可以对字符串和子字符串进行操作,返回值是最先匹配的字符串所在位置或者-1。-1代表没有找到匹配的字符串,例如:

>>> "hello world".find("hello")

0

>>> "hello world".find("wor")

6

>>> "hello world".find("word")

-1

让我们来测试这一函数(注意:如果你在解释器里执行这个函数,则需要导入sys文件,因为parseAddress函数中还调用了exit函数):

>>> parseAddress("http://www.builderau.com.au")

'http://www.builderau.com.au'

>>> parseAddress("www.builderau.com.au")

'http://www.builderau.com.au'

>>> parseAddress("ftp://builderau.com.au")

Error: Cannot retrieve URL, protocol must be HTTP

打开浏览HTTP站点

Python的标准库中含有许多模块,从而使得复杂的任务变得很简单。在下面的例子中,我们将要使用urllib2模块来完成打开web页面。使用urllib2模块打开和浏览web站点就如同打开文本文件一样简单:

import urllib2

website = urllib2.urlopen(address)

website_html = website.read()
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 转播转播 分享分享 分享淘帖
台州维博网络(www.tzweb.com)专门运用PHP+MYSQL/ASP.NET+MSSQL技术开发网站门户平台系统等。
2#
 楼主| 发表于 2007-10-10 08:58:36 | 只看该作者
台州网址导航
和文件操作一样,你在访问网络站点的时候也会出错。可能是服务器关机了,也可能是你的Internet连接中断了,还有可能是你查询的文件不存在。不管是什么原因造成,你需要解决这些问题。在Python中正确处理这些问题的方法是使用异常。Urlopen函数能够抛出很多不同的例外,需要掌握的两个异常,一个是HTTPError,当抛出这个异常的时候,你所连接的服务器也同时返回一个错误代码。另一个要掌握异常是URLError,这个异常在其它网络或者协议发生错误时抛出。你可以通过下面的方法捕获异常:

try:

website = urllib2.urlopen(address)

except urllib2.HTTPError, e:

print "Cannot retrieve URL: HTTP Error Code", e.code

except urllib2.URLError, e:

print "Cannot retrieve URL: " + e.reason[1]

因此,当你尝试解析一个不存在的URL的时候,你将会看到类似下面的错误信息:

% python2.4 images.py www.google.com/doesnotexist

Cannot retrieve URL: HTTP Error Code 404

通过错误代码404,我们可能会获取更多的信息,因为我们已经遇到过很多次。但是你能够解释下面这些代码所代表的含义吗?例如,错误代码407表示需要指定代理服务器,错误代码503表示服务器正在超负荷运行,不能处理当前的请求。很明显,我们需要一种更友好的方式给用户反馈错误信息。同样,Python中也提供了这个功能。这是通过BaseHTTPServer模块中定义的字典来完成。字典是Python中的基本数据类型,在其它语言中也被称之为hash表或者map,我们以后将会详细介绍字典。现在我们就简单地将它认为是一个列表,只是它不仅能按照索引顺序返回条目,也可以按照指定的任意其它标示符按序返回。其中,字典BaseHTTPRequestHandler.responses提供了一个错误编码和解释之间的映射表——-如果你对这个映射表感兴趣,可以参照HTTP 1.1 规范 RFC,所以下面的代码:

import BaseHTTPServer

print BaseHTTPServer.BaseHTTPRequestHandler.responses[404]

产生如下的输出:

('Not Found', 'Nothing matches the given URI')

我们可以使用字典输出更详细的错误信息。

使用正规式查找信息

假设我们已经有了需要处理的Web页面的文本内容,现在我们要做的就是查找其中所有的图片标签。可能有些人没有注意到,事实上,在HTML文档中,页面中的图片是以类似如下的方式进行编码的(添加或者删除一些属性):

<img src="/path/to/image.png">

我们真正感兴趣的是引号中的内容,其它的部分可以忽略。我们将使用简单正规式和findall函数来完成复杂的工作。正规式是一种规定标识符串语法规则的方法(在这里,标识符指的是字符、数字、字母或者标点符号),所有种类的字符串都可以被识别,而不需要分别指定。关于正规式有专门的文章讨论,这里我们只看几个例子:

假设你有一些文本数据,并且需要识别其中的二进制字符串,也就是只由0和1组成的字符串。对应的正规式如下所示:

(0|1)+

圆括号用来将多个字母进行组合,中间的竖线("|")意味着选择,既可以匹配竖线左边的字符,也可以匹配竖线右边的字符。最后,"+"表示“要和前面的标识符至少匹配一次或者多次”。所以这一正规式匹配的字符应该是“一个或者多个重复的 0或者1”。对于正规式来说,我们还需要掌握一些常用的标号。例如,句点(".")代表任意的单个字符,星号"*"的用法和"+"类似,但是"*"表示匹配的次数是零次或者多次。所以,一个数目多于5的任意字符串的正规式是下面的形式:

......*

对于编写正规式来识别html中的图像来说,我们的知识已经足够了。为了运行表达式,我们需要使用函数sre.findall,函数的返回值是包含字符串中所有匹配组的列表。下面的代码就完成查找图片的功能:

matches = sre.findall('<img .*src="?(.*?)"?', website_text)

这里有几个问题需要注意:首先,我们将字符串放入单引号中,将它作为路径的起始和结束标记,因为我们还希望在字符串中能够使用双引号。其次,我们使用符号'(', ')'将我们感兴趣的内容组合起来, findall函数将会自动丢弃所有不在组合中的部分。最后,我们使用"?"来完成两项工作。如果"?"紧跟在表达式之后,则匹配0个或1个由前面的正则表达式定义的片段,如果"?"是紧跟在"*"之后,则意味表达式匹配置处于‘非贪婪’模式。默认情况下,正规式匹配是贪心的,也就是说,它会匹配尽可能多的文本内容,我们想做的正好相反,只匹配到第一个"被找到。如果我们现在打开了页面http://www.builderau.com.au并且运行了正规式,我们会得到下面的结果(你们的结果可能不同,这取决于首页的图片内容):

['http://ad.au.doubleclick.net/ad/popup.builderau.com.au/;sz=1x1;ord=123456789?',

'/i/s/cov/checklist170110.jpg', '/i/s/cov/perl170110.jpg', '/i/s/cov/atwork170110.jpg',

'/i/x/blogs/brendon_chase_52x52.gif', '/i/x/blogs/chris_duckett_52x52.gif',

'http://dw-eu.com.com/clear/c.gif?ts=1163740722&sId=75']

到了这一步,我们又遇到了新问题:Web页面中的图片路径既可以表示成绝对路径也可以表示成与页面的相对路径两种形式。从上面的数据中,我们就可以看到这点,一些图片是全路径的形式,并以"http:// "起始,而另外一些只含有目录名。我们的程序应该能够在相对链接的开始添加当前页面的路径。首先,我们需要得到当前页面的路径,我们之前已经完成了很多工作,这里就可以调用urllib2来确定当前的站点地址:

dir = website_handle.geturl().rsplit('/',1)[0]

if (dir == "http:/"):

dir = website_handle.geturl()

我们使用字符串函数rsplit移除页面自身的文件名。rsplit和其兄弟函数split可以按照某个特定的字符对字符串分段,例如:

>>> "Hello World".split(' ')

['Hello', 'World']

>>> "Hello World".rsplit('l')

['He', '', 'o Wor', 'd']

>>> "Hello World".split('l',1)

['He', 'lo World']

>>> "Hello World".rsplit('l', 1)

['Hello Wor', 'd']

唯一性和排序

我们已经得到了所有有效的图片链接,下面要做的就是将结果以最好的方式输出。在这个例子中,我们要删除结果中的重复数据,并对结果进行排序。在Python语言中,使得结果唯一的最简单方式就是将数据存储在一个容器中,例如set。Set类型和List类型很相似,它们之间的区别在于Set中的数据必须是唯一的并且并不是按照特定的顺序存储。这里我们只使用set的基本特性:

>>> x = ['set', 'contents', 'hello', 'world']

>>> y = set(x)

>>> y

set(['world', 'set', 'hello', 'contents'])

>>> y.add("new item")

>>> y

set(['new item', 'world', 'set', 'hello', 'contents'])

>>> list(y)

['new item', 'world', 'set', 'hello', 'contents']

这里需要注意的重要一点就是,可以随意地在list数据和set数据间转换进行,但是会丢失list中条目的顺序性。下一步我想在将结果呈现给用户之前,对数据排序。有很多不同的排序算法,但是我们不愿意在排序的时候再去编写一个程序。基于这样的原因,Python也提供了很多排序函数。并不是在所有的环境下,这些函数都是最好的,当你需要快速排序的时候,你可能要自己亲自编写一个排序函数,但是在大多数的情况下,这些函数已经能够满足我们的需要。list.sort函数对list进行原地排序,并不产生新的list。
台州维博网络(www.tzweb.com)专门运用PHP+MYSQL/ASP.NET+MSSQL技术开发网站门户平台系统等。
3#
 楼主| 发表于 2007-10-10 08:58:58 | 只看该作者
台州网址导航
>>> x = [1,5,3,6,7,2,8,9,4]

>>> x.sort()

>>> x

[1, 2, 3, 4, 5, 6, 7, 8, 9]

NB:感谢读者Jeremy提醒我sets是在Python中才引入的。如果你现在使用的是Python2.3,也可以通过字典完成同样的工作,下面的例子就是使用字典来完成上面的工作:

>>> x = ['set', 'contents', 'hello', 'world']

>>> y = dict.fromkeys(x)

>>> y

{'world': None, 'set': None, 'hello': None, 'contents': None}

>>> y['new item'] = None

>>> y

{'new item': None, 'world': None, 'set': None, 'hello': None, 'contents': None}

>>> y.keys()

['new item', 'world', 'set', 'hello', 'contents']

最终的程序代码

import sre, urllib2, sys, BaseHTTPServer



def parseAddress(input):

if input[:7] != "http://":

if input.find("://") != -1:

print "Error: Cannot retrive URL, address must be HTTP"

sys.exit(1)

else:

input = "http://" + input

return input

def retrieveWebPage(address):

try:

web_handle = urllib2.urlopen(address)

except urllib2.HTTPError, e:

error_desc = BaseHTTPServer.BaseHTTPRequestHandler.responses[e.code][0]

#print "Cannot retrieve URL: " + str(e.code) + ": " + error_desc

print "Cannot retrieve URL: HTTP Error Code", e.code

sys.exit(1)

except urllib2.URLError, e:

print "Cannot retrieve URL: " + e.reason[1]

sys.exit(1)

except:

print "Cannot retrieve URL: unknown error"

sys.exit(1)

return web_handle

if len(sys.argv) < 2:

print "Usage:"

print "%s url" % (sys.argv[0])

sys.exit(1)

match_set = set()

address = parseAddress(sys.argv[1])

website_handle = retrieveWebPage(address)

website_text = website_handle.read()

dir = website_handle.geturl().rsplit('/',1)[0]

if (dir == "http:/"):

dir = website_handle.geturl()

matches = sre.findall('<img .*src="(.*?)"', website_text)

for match in matches:

if match[:7] != "http://":

if match[0] == "/":

slash = ""

else:

slash = "/"

match_set.add(dir + slash + match)

else:

match_set.add(match)

match_set = list(match_set)

match_set.sort()

for item in match_set:

print item

做完这步之后,一个抓取Web页面图片地址的程序就完成了。这只是一个很小的例子,但是你以后开发的大部分程序都可以按照这样的模式进行。首先,检查用户输入。在这个例子中我们检查了参数的数目是否正确以及所给的网址是否正确。然后,收集处理数据。在这里我们下载了Web页面并提取其中的网址信息,并使用正规式表示。最后,组织数据,输出结果。
台州维博网络(www.tzweb.com)专门运用PHP+MYSQL/ASP.NET+MSSQL技术开发网站门户平台系统等。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

网站推广
关于我们
  • 台州朗动科技(Tzweb.com)拥有多年开发网站平台系统门户手机客户端等业务的成功经验。主要从事:政企网站,系统平台,微信公众号,各类小程序,手机APP客户端,浙里办微应用,浙政钉微应用、主机域名、虚拟空间、后期维护等服务,满足不同企业公司的需求,是台州地区领先的网络技术服务商!

Hi,扫描关注我

Copyright © 2005-2026 站长论坛 All rights reserved

Powered by 站长论坛 with TZWEB Update Techonolgy Support

快速回复 返回顶部 返回列表