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脚本完成请求处理过程。

总结:

  1. BaseHTTPServer:提供基本上WEB服务器和处理程序类,分别是HTTPServer和BaseHTTPRequestHandler
  2. SimpleHTTPServer:在BaseHTTPRequestHandler基础上,含有SimpleHTTPRequestHandler,用于处理GET和HEAD请求
  3. 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)    收藏  举报

导航