Python研究学习--20--web客户端和服务器(下)

版权声明:本文为博主原创文章,转载本站文章请注明作者和出处,请勿用于任何商业用途。 https://blog.csdn.net/wutianxu123/article/details/82560643

16.5 python web服务器

实现读取GET请求,获取web页面,并将其返回呈现。

#-*-coding: utf-8-*- 
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        try:
            f = open(self.path[1:], 'r')
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(f.read())
            f.close()
        except IOError:
            self.send_error(404,u'没有发现文件: %s' % self.path)

def main():
    try:
        server = HTTPServer(('', 80), MyHandler)
        print u'欢迎访问'
        print u'请按ctrl+c退出'
        server.serve_forever()
    except KeyboardInterrupt:
        print u'已退出并关闭了服务器'
        server.socket.close()

if __name__ == '__main__':
    main()

16.6 web客户端

Web爬虫:通过起始web地址,下载该页面和其它后续链接页面。但仅限于与起始页面有相同域名的页面,否则耗尽整个磁盘空间。

#-*-coding: utf-8-*- 
import cStringIO
import formatter
import htmllib
import httplib
import os
import sys
import urllib
import urlparse

class Retriever(object):                                 #任务是从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)
        host = parsed.netloc.split('@')[-1].split(':')[0]
        filepath = '%s%s' % (host, parsed.path)
        if not os.path.splitext(parsed.path)[1]:
            filepath = os.path.join(filepath, default)
        linkdir = os.path.dirname(filepath)
        if not os.path.isdir(linkdir):                   #要么路径不存在,要么是普通文件
            if os.path.exists(linkdir):
                os.unlink(linkdir)
            os.makedirs(linkdir)
        return url, filepath

    def download(self):
        u'将URL下载到指定的命名文件'
        try:
            retval = urllib.urlretrieve(self.url, self.file)
        except (IOError, httplib.InvalidURL) as e:
            retval = ((u'错误!不可用的URL:"%s":%s' % (self.url,e)),)
        return retval

    def parse_links(self):
        u'解析下载下来的HTML文件中是否有额外的链接'
        f = open(self.file, 'r')
        data = f.read()
        f.close()
        parser = htmllib.HTMLParser(formatter.AbstractFormatter(formatter.DumbWriter(cStringIO.StringIO())))
        parser.feed(data)
        parser.close()
        return parser.anchorlist

class Crawler(object):                                   #管理一个web站点的完整抓爬过程
    count = 0
    def __init__(self, url):
        self.q = [url]                                   #待下载链接集合
        self.seen = set()                                #已下载链接集合
        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'下载页面和分析链接'
        r = Retriever(url)
        fname = r.download()[0]
        if fname[0] == '*':
            print fname, u'跳过解析'
            return
        Crawler.count += 1
        print '\n(', Crawler.count, ')'
        print 'URL:', url
        print 'FILE:', fname
        self.seen.add(url)
        ftype = os.path.splitext(fname)[1]
        if ftype not in ('.htm', '.html'):
            return

        for link in r.parse_links():
            if link.startswith('mailto:'):
                print u'丢弃!无效连接'
                continue
            if not media:
                ftype = os.path.splitext(link)[1]
                if ftype in ('.mp3', '.mp4', '.m4v', '.wav'):
                    print u'不可用的媒体文件'
                    continue
            if not link.startswith('http://'):
                link = urlparse.urljoin(url, link)
            print '*', link,
            if link not in self.seen:
                if self.dom not in link:
                    print u'丢弃!不在域名中'
                else:
                    if link not in self.q:
                        self.q.append(link)
                        print u'新的!添加到下载'
                    else:
                        print u'丢弃!已经下载过了'
            else:
                print u'丢弃!已经处理过了'

    def go(self, media=False):
        u'处理队列中的下一页(如果有的话)'
        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(u'输入起始URL: '.encode('gb2312'))
        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()

解析web页面:给定一个URL后,脚本会提取所有链接,尝试进行必要的调整,生成完整的URL,对这些URL进行排序并输出。这段脚本会使用2个不同的解释器来从HTML标签中提取链接,分别是:HTMLParser、BeautifulSoup。

#-*-coding: utf-8-*- 
from HTMLParser import HTMLParser
from cStringIO import StringIO
from urllib2 import urlopen
from urlparse import urljoin

#顺序:先导入标准模块/包(以上),后导入第三方模块/包(以下)
from BeautifulSoup import BeautifulSoup,SoupStrainer

URLS = ('http://www.baidu.com',)                        #可以自由添加、移除。注意必须有逗号!
def output(x):
    print '\n'.join(sorted(set(x)))                     #按字母顺序排序呈现出来

def simpleBS(url, f):
    u'simpleBS()函数-使用BeautifulSoup解析所有的标签来获得锚'
    output(urljoin(url,x['href']) for x in BeautifulSoup(f).findAll('a'))

def fasterBS(url, f):
    u'fasterBS()函数-使用BeautifulSoup解析过滤,只处理含有锚的标签'
    output(urljoin(url,x['href']) for x in BeautifulSoup(f,parseOnlyThese=SoupStrainer('a')))

def htmlparser(url, f):
    u'htmlparser()函数-使用htmlparser解析锚标签'
    class AnchorParser(HTMLParser):
        def handle_starttag(self, tag, attrs):
            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 process(url, data):
    print u'\n***simpleBS()函数解析结果-所有标签'
    simpleBS(url, data)
    data.seek(0)
    print u'\n***fasterBS()函数解析结果-只含有锚的标签'
    fasterBS(url, data)
    data.seek(0)
    print u'\n***htmlparser()函数解析结果-解析锚标签'
    htmlparser(url, data)
    data.seek(0)

def main():
    for url in URLS:
        f = urlopen(url)
        data = StringIO(f.read())
        f.close()
        process(url, data)

if __name__ == '__main__':
    main()

可编程的web浏览:一款批处理类型的脚本。该脚本分为7个部分,每部分浏览特定站点的一个页面。使用Mechanize浏览web站点,使用BeautifulSoup来进行解析。

注:可能是因为网络的原因,也可能是代码的原因,该程序未成功运行。

#-*-coding: utf-8-*-  
from BeautifulSoup import BeautifulSoup as BS
from mechanize import Browser

br = Browser()

#访问站点主页面
rsp = br.open('http://us.pycon.org/2011/home/')
print '\n***', rsp.geturl()
print u"确认主页有“登录”链接并单击它"
page = rsp.read()
assert 'Log in' in page, 'Log in not in page'           #断言函数。以布尔值为判断,当我们不知道程序在哪里会出错,与其让它在运行最崩溃,不如在出现错误条件时就崩溃
rsp = br.follow_link(text_regex='Log in')

#访问站点登录页面
print '\n***', rsp.geturl()
print u'确认至少提交一个有效的登录表单'
assert len(list(br.forms())) > 1, 'no forms on this page'
br.select_form(nr=0)
br.form['username'] = 'admin1'                          #错误的用户名
br.form['password'] = 'password1'                       #错误的密码
rsp = br.submit()

#站点登录错误页面,需重新提交
print '\n***', rsp.geturl()
print u'由于登录凭据无效,请提交有效登录凭据'
assert rsp.geturl() == 'http://us.pycon.org/2011/account/login/', rsp.geturl()
page = rsp.read()
err = str(BS(page).find("div",{"id": "errorMsg"}).find('ul').find('li').string)
assert err == u'您的用户名或密码不正确', err
br.select_form(nr=0)
br.form['username'] = 'admin'                           #正确的用户名
br.form['password'] = 'password'                        #正确的密码
rsp = br.submit()

#登录成功,返回主页面
print '\n***', rsp.geturl()
print u'恭喜您!登录成功!'
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')

#注册页面
print '\n***', rsp.geturl()
print u'使用电子邮箱地址进行注册'
assert rsp.geturl() == 'http://us.pycon.org/2011/account/email/', rsp.geturl()
page = rsp.read()
assert 'Email Addresses' in page, 'Missing email addresses'
print 'Primary e-mail: %r' % str(BS(page).find('table').find('tr').find('td').find('b').string)
rsp = br.back()

#返回主页面
print '\n***', rsp.geturl()
print u'返回主页面'
assert rsp.geturl() == 'http://us.pycon.org/2011/home/', rsp.geturl()
rsp = br.follow_link(url_regex='logout')

#退出登录
print '\n***', rsp.geturl()
print u'确认已经退出登录'
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'

猜你喜欢

转载自blog.csdn.net/wutianxu123/article/details/82560643