9.WEB客户端和服务器
一、WEB客户端和服务器的一些基础概念:
客户端的请求以单个事件来划分,一旦完成一个客户端请求,这个服务事件就停止了。客户端随时可以发送新的请求,但是每个新的请求都会处理成独立的服务请求。
由于每个请求是独立的,并且缺乏上下文,如果在下次请求的时侯需要使用到客户端上次请求的状态信息,怎么办?
- 方式一:将客户端状态信息附加到下次请求的URL变量中
- 方式二:将客户端状态信息保存在客户端的cookie中
数据在internet网上的传输,默认是没有加密服务的。
如果要对传输数据进行加密,需要在普通的套接字上添加一个额外的安全层,此安全层称为安全套接字层(Secure SocketLayer, SSL)。
安全套接字层,用来创建一个套接字,加密通过该套接字传输的数据。开发者可以决定是否使用这个额外的安全层。
防火墙:通常,WEB服务器会封掉大部分端口,只保留WEB服务器和安全shell访问(SSH)。安全shell访问基于SSL。
正向代理服务器:作用一、可以只让一部分计算机访问网络,也可以更好地监控网络的数据传输;作用二、可以缓存数据。
示例:在某公司,linda访问一个代理服务器缓存过的WEB页面,她的同事heather后来再次访问该页面的时侯,网页加载速度会快很多,因为heather的浏览器无须与WEB服务器进行完整的交互,
而是从代理服务器获得所有信息;同时服务器管理员,可以知道至少有两个员工在何时访问了这个页面------这种方式,叫做正向代理。
正向代理用来缓存数据,更接近客户端;反向代理更接近后端服务器,扮演服务器端角色,如缓存服务器的数据、负载均衡等或者用来当作防火墙或加密数据。
URL: uniform Resource Local ,统一资源定位符。URL是URI(uniform resource identifier,统一资源标识符)的一部分。
URI 除了包含URL,还包括非URL之外的许多东西。如URN:uniform resource name,统一资源名称,在使用XML的时侯会用到URN。
URL由六部分组成:
prot_sch或scheme:网络协议,如http,ftp等
net_loc:网络地址,服务器所在地,如user:password@host:port
path:使用斜杠/分隔的文件或CGI应用的路径
params:标识符;之后,连接符&分割的一系统键值对。可选参数
query:标识符?之后,连接符&分割的一系统键值对
frang:指定文档内特定锚的部分
python内置的两个模块urlparse、urllib都可以用来处理URL。
二、urlparse和urllib模块
(一)、URLPARSE,包括urlparse()、urlunprase()、urljoin()
1.urlparse将URL字符串拆分成一个6元组(prot_sch,net_loc,path,params,query,frang)
urlparse(urlstr, defPortSch=None, allowFrag=None)
defPortSch,如果urlstr中没有指明网络协议,则使用此参数指定的网络协议作为默认值;allowFrag标识一个url是否允许使用片段。
import urlparse urlparse.urlparse("https://www.baidu.com/s?wd=python%E6%A0%B8%E5%BF%83%E7%BC%96%E7%A8%8B%20%E7%9D%A1%E7%9C%A0%E6%9C%8D%E5%8A%A1%E5%99%A8&pn=10&oq=python%E6%A0%B8%E5%BF%83%E7%BC%96%E7%A8%8B%20%E7%9D%A1%E7%9C%A0%E6%9C%8D%E5%8A%A1%E5%99%A8&ie=utf-8&rsv_idx=1&rsv_pq=f550e2e50002a00e&rsv_t=949adDFXs1RzvGzv0kYol9z4jX%2FwZkFOT3L8mmLdTTjJgtZjGpXyXZU0%2FiE&rsv_page=1") # ParseResult(scheme='https', netloc='www.baidu.com', path='/s', params='', query='wd=python&oq=python%E6%A0%&ie=utf-8&rsv_idx=1&rsv_pq=f550e2e50002a00e&rsv_t=949aiE&rsv_page=1', fragment='')
2.urlunparse,与urlparse刚好相反。它是将一个6元组的urltuple拼接成URL字符串。
urlparse.urlunparse(('https', 'www.baidu.com', '/s', '', 'wd=python&rsv_page=1', '')) # 'https://www.baidu.com/s?wd=python&rsv_page=1' urlparse.urlunparse(urlparse.urlparse('https://www.baidu.com/s?wd=python&rsv_page=1'))
3.urljoin,将URL1的根域+路径的目录部分 与NEWURL拼合成一个完整的URL
urlparse.urljoin('https://docs.python.org/2/library/functions.html', 'current/lib.html') # 'https://docs.python.org/2/library/current/lib.html'
(二)urllib 中的核心方法
httplib、ftplib是更底层的因特网模块,URLLIB是比之更高级的因特网通信模块。
python2有urlparse、urllib、urllib2模块,在python3中这些模块都合并到了urllib模块,urlparse整合到了urllib.parse中,urllib、urllib2整合到了urllib.request中。python3的urllib还包括了response、error、robotparse模块。
1.urlib2.urlopen(),打开给定url的连接,返回文件类型的对象
urllib.urlopen(urlstr, postQueryData=None) # 当使用post请求时,请求的字符串(编码过的)放在postQueryData
python2中,所有使用urllib.urlopen的请求,都建议使用urllib2.urlopen,同时因为python2.7中的urllib已弃用urllib.urlopen;pythone3中,使用urllib.request代替。
urllib2.urlopen(url, data=None, proxies=None) # 参数data(编码过的字符串)表示以post方式提交到url的数据;参数proxies用于设置代理;第三方包request,可以使用字典
返回的文件类型对象,就像操作文件一样操作它,如f.read(n)读取所有字节或n个字节,f.readline(),f.readlines()读所有行,以列表返回,f.close(),f.fileno(),f.info()返回MIME头文件,f.geturl返回f的真正URL。
如果要处理一些复杂的URL访问,如权限验证、重定位、cookie等,则使用urllib2中的urlopen方法。
from urllib2 import urlopen import json fd = urlopen("http://www.baidu.com") print fd.info() # 返回头部信息,字典类型 # Content-Type: text/html; charset=utf-8 # Transfer-Encoding: chunked .................... print fd.info().getheader("Content-Type") # 使用getheader方法或get方法获取头部信息中的某一个头部 # text/html; charset=utf-8 print fd.info().getr("Content-Type") # text/html; charset=utf-8 print fd.read() # 读取response中的所有字节,像操作文件一样 print json.loads(fd.read()) # 用json反序列化
fd.msg # 'OK'
第三方库request,提供了更加好用的包装,可以直接fd.json..fd.content...fd.text等等.......
2.urllib.urlretrieve(),用于下载完整的HTML,并存为文件在本地。因此不是返回文件对象,而是返回(filename, mime_hdrs)
urllib.urlretrieve(url, filename=None, reporthook=None, data=None) # filename保存在本地的文件名,reporthook显示下载进度,data是post的时侯传递的请求字符串
3.urllib.quote()、urllib.quote_plus():对字符串编码。用于获取URL数据,并将其编码,使其可以用于URL字符串中。
使用了quote,quote_plus进行编码,urllib2.urlopen(url)不可以访问了?!
urllib.quote(urldata, safe='/') #safe参数,指定不需要转换的字符,默认为/ # 说明:逗号、句号、下划线、字母、数字,这类符号是不会转换的。其它的均需转换,会在转换的字符前加上%,同时转换成十六进制
url = 'http://www.baidu.com?name=%s&num=%d' % ('python', 2.7) urllib.quote(url) # 'http%3A//www.baidu.com%3Fname%3Dpython%26num%3D2' # :转换成了3A ?转换成了3F =转换成了3D
urllib.quote_plus与urllib.quote基本上一样,区别:quote_plus可以将空格编码成+号;如果没有指明safe='/',那么'/'也会被转换
url = 'http://www.baidu.com?name=%s&num=%d ' % ('pyt hon', 2.7) urllib.quote(url) #'http%3A//www.baidu.com%3Fname%3Dpyt%20%20%20%20hon%26num%3D2%20%20%20' urllib.quote_plus(url) # 'http%3A%2F%2Fwww.baidu.com%3Fname%3Dpyt++++hon%26num%3D2+++'
urllib.unquote()、urllib.unquote_plus():解码成字符串,与编码刚好相向。将上面编码成%xxxxxxx的字符转换成ascii码。
encoded_url = 'http%3A//www.baidu.com%3Fname%3Dpyt%20%20%20%20hon%26num%3D2%20%20%20' urllib.unquote(encoded_url) # 'http://www.baidu.com?name=pyt hon&num=2 ' urllib.unquote_plus(encoded_url) # 'http://www.baidu.com?name=pyt hon&num=2 '
同样的,unquote_plus和unquote基本上一样,区别:quote_plus可以将+号解码成空格。无论是'/'还是2F,无论使用unquote还是unquote_plus,都会解码为'/'。
encoded_url1 = 'http%3A%2F%2Fwww.baidu.com%3Fname%3Dpyt++++hon%26num%3D2+++' urllib.unquote(encoded_url1) # 'http://www.baidu.com?name=pyt++++hon&num=2+++' urllib.unquote_plus(encoded_url1) # 'http://www.baidu.com?name=pyt hon&num=2 '
4.urllib.urlencode():对字典编码。编码为“键=值”,以连接符&分隔;且“会隐式调用quote_plus",对键值进行适当编码。
dct = {'name': u'pyt hon中文'.encode("utf8"), 'num': 222} # unicode必须编码成utf8
urllib.urlencode(dct) # 注意:urlencode()只能编码utf8或ascii,不能编码unicode
'num=222&name=pyt++hon%E4%B8%AD%E6%96%87'
url = 'http://www.baidu.com?' + 'num=222&name=pyt++hon'
urllib2.urlopen(url)
对于dct,如果觉得每个字符串都要编码成utf8 ,觉得麻烦,那么就要用到json.loads的hook函数解决
dct = {'name': u'pyt hon中文'.encode("utf8"), 'num': 222}
dct = json_loads_byteified(json.dumps(dct))
dct
# {'name': 'pyt hon\xe4\xb8\xad\xe6\x96\x87', 'num': 222}
def json_loads_byteified(json_text):
return _byteify(
json.loads(json_text, object_hook=_byteify),
ignore_dicts=True
)
def _byteify(data, ignore_dicts = False):
# if this is a unicode string, return its string representation
if isinstance(data, unicode):
return data.encode('utf-8')
# if this is a list of values, return list of byteified values
if isinstance(data, list):
return [ _byteify(item, ignore_dicts=True) for item in data ]
# if this is a dictionary, return dictionary of byteified keys and values
# but only if we haven't already byteified it
if isinstance(data, dict) and not ignore_dicts:
return {
_byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
for key, value in data.iteritems()
}
# if it's anything else, return it in its original form
return data
说明:urllib模块通过SSL支持开放的HTTP连接。httplib支持使用https协议,当然也支持SSL。
(三)、URLLIB2,可以处理更复杂的URL,如基本验证、重定位、COOKIE等
WEB验证有基本验证和摘要式存取验证两种。
通过基本验证(用户名和密码验证),最简单的方法就是直接在url中加入用户名和密码:http://user:password@host:port/path?querystring,但它不具有可编程性。
具有可编程性的基本验证实现,urllib2有两种方式可以实现。
具有可编程性的基本验证方式一:建立一个基础验证处理程序(urllib2.HTTPBasicAuthHandler),同时在根域名和域上注册一个登录密码,相当于在WEB站点上定义了一个安全区域。当完成了这个处理程序后,安装URL开启器opener,通过这个处理程序打开所有的URL。
# 域来自WEB站点的安全部分定义的.htaccess文件 AuthType basic AuthName "Secure Archive" # AuthName列出的字符串就是域 AuthUserFile /www/htdocs/.htpasswd # 通过htpasswd命令创建用户名和加密的密码,并安装在.htpasswd文件中 require valid-user
具有可编程性的基本验证方式二:另一个创建开启器的办法就是当浏览器提示的时侯,通过验证处理程序模拟用户输入用户名和密码,这样就发送了一个带有适当用户请求的授权头。
具有可编程性的基本验证两种方式示例:
# urlopen_auth.py #!/usr/bin/env python # coding: utf-8 import urllib2 USER = 'jerry' PASSWD = 'yourpassword' URL = "http://localhost" REALM = "Secure Archive" # 方式一:创建基本处理程序、添加验证信息到处理程序中、使用处理程序创建URL开启器opener、安装URL开启器opener,返回原始url def handler_version(url): from urlparse import urlparse handler = urllib2.HTTPBasicAuthHandler() # 1.创建基本处理程序类 handler.add_password(REALM, urlparse(url)[1], USER, PASSWD) # 2.为处理程序类添加验证信息:urlparse.urlparse(url)[1]获取URL的HOST部分 opener = urllib2.build_opener(handler) # 3.使用该处理程序类创建一个URL开启器opener urllib2.install_opener(opener) # 4.安装URL开启器opener,作用:让所有已打开的URL都能用到2中的验证信息 return url # 方式二:创建request对象,将验证信息添加到request对象的头部信息中,返回request对象 def request_version(url): from base64 import encodestring req = urllib2.Request(url) # 1.创建Request对象 b64str = encodestring('%s:%s' % (USER, PASSWD))[:-1] # 2.对用户名和密码进行base64编码,并去掉最后一个字符\n req.add_header('Authorization', 'Basic %s' % b64str) # 3.使用以上base64编码的用户名和密码,作为Authorization(专用于验证的头部key)的值,添加到头部信息。 return req # 返回request对象 url = handler_version(URL) f = urllib2.urlopen(url) # 如果是方式一,此时的url和原始url一样;如果是方式二,此时的url就是request对象 f.readlines() # req = request_version(URL) # f = urllib2.urlopen(req) # f.readlines()
来自以下网址的补充:
http://www.voidspace.org.uk/python/articles/urllib2.shtml
# create a password manager password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() # Add the username and password. # If we knew the realm, we could use it instead of ``None``. top_level_url = "http://example.com/foo/" password_mgr.add_password(None, top_level_url, username, password) handler = urllib2.HTTPBasicAuthHandler(password_mgr) # create "opener" (OpenerDirector instance) opener = urllib2.build_opener(handler) # use the opener to fetch a URL opener.open(a_url) # Install the opener. # Now all calls to urllib2.urlopen use our opener. urllib2.install_opener(opener)
三、浏览器之外的WEB客户端:如爬虫
1.爬虫示例:
import os import urllib # 处理url请求
import urlparse # 解析url import httplib # 使用此模块的异常 from htmllib import HTMLParser import cStringIO import formatter
# cStringIO、formatter、HTMLParser:用于解析HTML
class Retriever(object):
u"""该类用于获取并解析每个下载到的WEB页面""" __slots__ = ("url", "file") def __init__(self, url): self.url, self.file = self.get_file(url) def get_file(self, url, default='index.html'): u"""创建一个与url相关联的本地文件""" parsed = urlparse.urlparse(url) # 将url解析成六元组 host = parsed.netloc.split('@')[-1].split(':')[0] # 获取host filepath = '%s%s' % (host, parsed.path) # 构造filepath: host + path(包括文件名) if not os.path.splitext(parsed.path)[1]: # 将路径的(目录及文件名),和(文件的扩展名)拆分,如果扩展名不存在,则说明文件名不存在,则使用默认的文件名构造成filepath filepath = os.path.join(filepath, default) linkdir = os.path.dirname(filepath) # 取出目录 if not os.path.isdir(linkdir): # 如果linkdir不是目录,则创建此目录 if os.path.exists(linkdir): # linkdir不是目录,但是它以文件存在,则删除此文件 os.unlink(linkdir) # 删除文件,不能删除目录 os.makedirs(linkdir) return url, filepath def download(self): u"""下载HTML,并存为self.file""" try: retval = urllib.urlretrieve(self.url, self.file) except (IOError, httplib.InvalidURL) as e: retval = (("*** ERROR: bad URL '%s': %s" % (self.url, e)),) return retval def parse_links(self): u"""解析HTML""" with open(self.file, 'r') as f: data = f.read() # 使用HTMLParser的方法进行处理,StringIO是从内存中读取数据,DumbWriter将事件流转换为存文本文档 parser = HTMLParser(formatter.AbstractFormatter(formatter.DumbWriter(cStringIO.StringIO()))) # StringIO和cStringIO用于向内存读写数据;cStringIO.StringIO()使用默认参数,则只向内存读数据,不可写 # DumbWriter 将事件流转换为存文本文档,如果DumbWriter没有参数,则标准输出;如果有参数,则写入此文件 # 使用AbstractFormatter 类进行格式化. 它会根据不同的格式化事件调用 writer 对象的方法. # HTMLParser是python用来解析html的模块。它可以分析出html里面的标签、数据等等,是一种处理html的简便途径。 # HTMLParser采用的是一种事件驱动的模式,当HTMLParser找到一个特定的标记时,它会去调用一个用户定义的函数,以此来通知程序处理。 parser.feed(data) # #给分析器喂食.在由完整元素构成的情况下工作;不完整数据情况下,会进行缓冲知道更多数据加进来或者 close() 被调用 parser.close() # 处理所有缓冲数据 return parser.anchorlist
class Crawler(object): u"""此类用于管理整个爬虫进程""" count = 0 def __init__(self, url): self.q = [url] #待下载的url队列 self.seen = set() #已下载的url集合 parsed = urlparse.urlparse(url) host = parsed.netloc.split('@')[-1].split(':')[0] self.dom = '.'.join(host.split('.')[-2:]) # 域名 def get_page(self, url, media=False): u"""下载HTML,分析links ,入队""" r = Retriever(url) fname = r.download()[0] if fname[0] == '*': # 如果download失败 print fname, '...skipping parse' return Crawler.count += 1 print '\n(', Crawler.count, ')' print 'URL: ', url print 'FILE: ', fname self.seen.add(url) # download成功的url加入self.seen集合 ftype = os.path.splitext(fname[1]) if ftype not in ('.html', '.htm'): # 如果不是html页面,则跳过后续处理 return for link in r.parse_links(): # 对页面分析结果进行遍历,查看此页面的所有url,判断是否向self.q中添加这些url链接 if link.startswith('mailto: '): # 如果链接以mailto开头,则不添加进队列,跳过 print '...discarded, mailto link' continue if not media: # 如果是media文件,同样跳过 ftype = os.path.splitext(fname[1]) if ftype in ('.mp3', '.mp4', '.m4v', '.wav'): print '....dicarded, media file' continue if not link.startswith('http://'): link = urlparse.urljoin(url, link) print '*', link if link not in self.seen: # 如果此url没有被下载过 if self.dom not in link: print '...discarded, not in domain' else: # 如果此url的域名与self.url中的域名相同,才会被处理 if link not in self.q: # 如果此url也不在待下载的队列,则加入队列 self.q.append(link) # 未下载过的,也未在队列里的、符合要求的web页面,则此url加入队列。 print '...new, added to Q' else: print '...discarded, already in Q' else: print 'discarded, already processed' def go(self, media=False): u"""处理Queue里的下一页""" while self.q: url = self.q.pop() self.get_page(url, media)
def main(): if len(sys.argv) > 1: url = sys.argv[1] else: try: url = raw_input('enter starting URL: ') except (KeyboardInterrupt, EOFError): url = '' if not url: return if not url.startswith('http://') and not url.startswith('ftp://'): url = 'http://%s/' % url robot = Crawler(url) robot.go() if __name__ == '__main__': main()
2.WEB解析器:BeautifulSoup、html5lib、HTMLParse
htmllib.HTMLParse已废弃,使用标准库HTMLParse代替。
#!/usr/bin/env python # coding: utf-8 from HTMLParser import HTMLParser from cStringIO import StringIO from urllib2 import urlopen from urlparse import urljoin from BeautifulSoup import BeautifulSoup, SoupStrainer from html5lib import parse, treebuilders URLs = ('http://pythonr.org', 'https://www.baidu.com') def output(x): # x是一个含有链接的迭代器 print '\n'.join(sorted(set(x))) def simpleBS(url, f): u"方式一:使用simple BS,解析所有的tags标签,得到anchors" # parsed = BeautifulSoup(f) # 使用simple BS,查找出来的是所有标签 # tags = parsed.findAll('a') # 查找所有a标签 # links = [urljoin(url, x['href'])for tag in tags] # 遍历所有a标签,将a标签的href值和url组合成完整的url链接 # output(links) output(urljoin(url, x['href']) for x in BeautifulSoup(f).findAll('a')) def fasterBS(url, f): u"方式二:使用faster BS,仅仅解析parseOnlyThrese指定的标签,得到anchors" output(urljoin(url, x['href']) for x in BeautifulSoup(f, parseOnlyThese=SoupStrainer('a'))) def htmlparser(url, f): u"方式三:使用HTMLParser解析anchor的标签。虽然HTMLParser更低层,但代码量比BS大、且执行效率比BS慢" class AnchorParser(HTMLParser): # 自定义解析器,继承自HTMLParser def handle_startendtag(self, tag, attrs): # 需重写handle_startendtag方法 if tag != 'a': return if not hasattr(self, data): self.data = [] for attr in attrs: if attr[0] == 'href': self.data.append(attr[1]) parser = AnchorParser() parser.feed(f.read()) output(urljoin(url, x) for x in parser.data) def html5libparse(url, f): u"方式四:使用html5lib解析anchor的标签" # 使html5lib最简单的方法是:对要处理的内容调用它的parse方法,并构建和输出一棵自定义格式的树。最简单最常用的就是simpletree简单树 # 除了simpletree,还其它流行的minidom、ElementTree、lxml或BeautifulSoup。要使用其它格式的树,需要将格式的名称作为treebuilder参数传递给parse output(urljoin(url, x.attributes['href']) for x in parse(f) if isinstance(x, treebuilders.simpletree.Element) and x.name == 'a') def process(url, data): print '\n*** Simple BS' simpleBS(url, data) data.seek(0) # 重置stringIO内置对象 print '\n*** faster BS' fasterBS(url, data) data.seek(0) # 重置stringIO内置对象 print '\n*** HTMLParser' htmlparser(url, data) data.seek(0) # 重置stringIO内置对象 print '\n*** html5lib' html5libparse(url, data) data.seek(0) # 重置stringIO内置对象 def main(): for url in URLs: f = urlopen(url) data = StringIO(f.read()) f.close() process(url, data) if __name__ == '__main__': main()
4.可编程的WEB浏览
在以下示例中,使用第三方包Mechanize来模拟浏览器浏览网站。
#!/usr/bin/env python # coding: utf-8 from BeautifulSoup import BeautifulSoup as BS, SoupStrainer from mechanize import Browser import httplib br = Browser() url = "https://us.pycon.org/2011/home/" # 1.home page rsp = br.open(url) print '\n***', rsp.geturl() print u'确认页面 "Log in" 链接; 点击上面的url' page = rsp.read() # <a href="/2011/account/login/">Log in</a> or <a href="/2011/account/signup/">Sign Up</a> assert 'Log in' in page, u'页面上没有Log in' # 判断page是否含有'Log in',据此判断用户是否已经登录 rsp = br.follow_link(text_regex='Log in') # 获取'Log in'链接的url地址 # 2.login page:一旦确认了位于登录页面(这个页面至少有一个表单),选择第一个(也是唯一一个)表单,填写验证匿名字段(但除非登录名和密码都是'xxx'),并提交。 print '\n***', rsp.geturl() assert len(list(br.forms())) > 1, 'no forms on this page' br.select_form(nr=0) br.form['username'] = 'xxx' br.form['password'] = 'xxx' rsp = br.submit() # 3.login page, with error print '\n***', rsp.geturl() print 'Error duo to invalid creds; resubmit w/valid creds.' assert rsp.geturl() == "https://us.pycon.org/2011/account/login/", rsp.geturl() page = rsp.read() print page err = str(BS(page).find("div", {"id": "errorMsg"}).find("ul").find("li").string) assert err == "The username and/or password you specified are not correct.", err br.select_form(nr=0) br.form['username'] = '' br.form['password'] = '' rsp = br.submit() # 4.login successful, home page redirect print '\n***',rsp.geturl() print 'Logged in properly on home page; click Account link' assert rsp.geturl() == "http://us.pycon.org/2011/home/", rsp.geturl() page = rsp.read() assert 'Logout' in page, 'Logout not in page' rsp = br.follow_link(text_regex='Account') # 5. account page print '\n***',rsp.geturl() print 'Email address parseable on Account page; go back' assert rsp.geturl() == "http://us.pycon.org/2011/account/email/", rsp.geturl() page = rsp.read() assert 'Email Address' in page, 'Missing email address' print ' Primary e-mail: %r' % str(BS(page).find('table').find('tr').find('td').find('b').string) rsp = br.back() # back to home page print '\n***',rsp.geturl() print 'Back workds, on home page again; click Logout link' assert rsp.geturl() == 'http://us.pycon.org/2011/home/', rsp.geturl() rsp = br.follow_link(url_regex='logout') # logout page print '\n***',rsp.geturl() print 'Confirm on Logout page and Log in link at the top' assert rsp.geturl() == 'http://us.pycon.org/2011/account/logout', rsp.geturl() page = rsp.read() assert 'Log in ' in page, 'Log in not in page' print '\n*** DONE'
以上二、三章节都是WEB客户端知识
四、WEB服务器:Apache、ligHTTPD、Microsoft IIS、LiteSpeed、ACME Laboratories thttpd
用python实现简单的WEB服务器,此服务器不用于生产环境,但可以用来做开发服务器。
要建立一个WEB服务器,必须建立一个基本的服务器和一个处理程序。
- 基础服务器:用于在客户端和服务器端完成必要的HTTP交互。可以使用标准库中的BaseHTTPSever.HTTPServer。
- 处理程序:是一些重主要WEB服务的简单软件。用于处理客户端的请求,返回适当的文件。最简单的是继承BaseHTTPSever.BaseHTTPRequestHandler,但是它只实现了获得客户端的请求,除此之外没有任何实现
SimpleHTTPServer.SimpleHTTPRequestHandler处理程序,是在BaseHTTPRequestHandler的基础上,添加了标准的GET和HEAD请求。
CGIHTTPServer.CGIHTTPRequestHandler处理程序,是在SimpleHTTPRequestHandler的基础上,添加了POST请求,并可以调用CGI脚本完成请求处理过程。
总结:
- BaseHTTPServer:提供基本上WEB服务器和处理程序类,分别是HTTPServer和BaseHTTPRequestHandler
- SimpleHTTPServer:在BaseHTTPRequestHandler基础上,含有SimpleHTTPRequestHandler,用于处理GET和HEAD请求
- CGIHTTPServer:在SimpleHTTPServer基础上,含用CGIHTTPRequet类,用于处理POST请求并执行CGI
在python3中,已将以上三个Server模块整合到http模块中;并将httplib(处理客户端的模块)整合到http.client
实现一个简单的WEB服务器,示例:
#!/usr/bin/env python # coding: utf-8 import BaseHTTPServer class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): # 自定义了一个方法,在基础服务器接收到GET请求时调用该方法去处理。使用到的方法都来自BaseHTTPRequestHandler try: f = open(self.path[1:], 'r') # 打开客户端传来的路径;移除self.path[0],即前导"/" self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(f.read()) # 通过wfile管道将用于下载的WEB页面传递给用户,否则返回404 f.close() except IOError: self.send_error(404, 'File not Found: %s' % self.path) def main(): try: server = BaseHTTPServer.HTTPServer(('', 8020), MyHandler) # 实例化(创建)服务器,参数:地址和处理程序 print 'Welcome to myserver...' print 'Press ^C once or twice to quit.' server.serve_forever() # 启动服务器 except KeyboardInterrupt: print '^c recieved, shutting down server.' server.socket.close() # 关闭服务器 if __name__ == '__main__': main()
BaseHTTPServer非常基础,它不能处理CGI请求。
SimpleHTTPServer,提供了do_HEAD()和do_GET()方法,无须自行定义这两个方法。
CGIHTTPServer,除了提供do_HEAD()和do_GET()方法,还提供do_POST()主法,可用于处理表单数据。
用CGIHTTPServer实现一个简单的WEB服务器,示例:
import CGIHTTPServer CGIHTTPServer.test() #它已经实现了do_HEAD(), do_GET(), 供do_POST()方法,直接使用即可
以上,是使用服务器模块创建WEB服务器,这与使用什么WEB框架或应用无关。
总结:WEB编程相关模块
一、WEB应用程序
模块/包:描述
CGI:标准网关接口
cgi:从标准网关接口(CGI)获取数据
cgitb:处理CGI返回数据
HTMLParser:HTML、XHTML解析器,不基于SGML
htmlentitydefs:一些HTML普通实体定义
Cookie:用于HTTP状态管理的服务器端cookie
cookielib:用于HTTP客户端的cookie处理类
webbrowser:控制器--向浏览器加载WEB文档
sgmllib:解析简单的SGML文件
robotparser:解析robots.txt文件,对URL做“可获得性”分析
httplib:用来创建HTTP客户端
urllib:能过URL或相关工具访问服务器。在python2中,urllib.urlopen()被urllib2.urlopen()代替;在python3中,整合到了urllib.request.urlopen()
urllib2:用于打开URL的类和函数,在python3中位于urllib.request和urllib.error
urlparse:用于解析URL字符串的工具,在python3中重命名为:urllib.parse
二、XML处理
xml:包含许多不同解析器的XML包
xml.sax:简单的API,适用于兼容SAX2的XML(SAX)解析器
xml.dom:文档对象模型(DOM)XML解析器
xml.etree:树形的XML解析器,基于Element灵活容器对象
xml.parsers.expat:非验证型Expat XML解析器的接口
xmlrpclib:通过HTTP提供XML远程过程调用(RPC)客户端
SimpleXMLRPCServer:python XM-RPC服务器的基本框架
DocXMLRPCServer:自描述XML-RPC服务器的框架
三、WEB服务器
BaseHTTPServer:用来开发WEB服务器的抽象类
SimpleHTTPServer:处理最简单的HTTP请求(GET、HEAD)
CGIHTTPServer:不仅能像SimpleHTTPServer一样处理WEB文件,还能处理CGI(POST)请求
http.server:python3中的模块,将以上三个包整合到一起的包
http.client:python3中的模块,将httplib客户端处理模块整合到此
wsgiref:定义WEB服务器和python WEB应用程序间标准接口的包
四、第三方开发包:
HTMLgen:协助CGI将python对象转换成可用的HTML
BeautifulSoup:HTML、XML解析器及转换器
Mechanize:基于万维网的WEB浏览包
posted on 2018-04-29 14:43 myworldworld 阅读(1213) 评论(0) 收藏 举报
浙公网安备 33010602011771号