python核心编程学习笔记-2016-09-10-03-Web编程(三)

        20.3 高级Web客户端

        本节演示网络爬虫。它是按照一定的规则,自动地抓取万维网信息的程序或者脚本。

        在本节的演示程序中,抓取Web的开始页面地址,下载与开始页面相同域名的后续链接页面。

#-*-coding: utf-8-*-

from sys import argv
from os import makedirs, unlink, sep
from os.path import dirname, exists, isdir, splitext
from string import replace, find, lower
from htmllib import HTMLParser # HTMLParser是用来解析html页面的,解析html页面中的链接?
from urllib import urlretrieve
from urlparse import urlparse, urljoin
from formatter import DumbWriter, AbstractFormatter # DumbWriter将事件流转换为存文本文档?AbstractFormatter?
from cStringIO import StringIO # StringIO是指在内存中读写字符串

# 类Retrieve负责从web下载页面
class Retrieve(object): # download Web pages
    
    def __init__(self, url):
        self.url = url
        self.file = self.filename(url)

    # filename()方法使用给定的url找出安全、有效的相关文件名并储存在本地
    def filename(self, url, deffile='index.html'):
        parsedurl = urlparse(url, 'http:', 0) # 解析路径
        path = parsedurl[1] + parsedurl[2] # web页面(服务器位置+文件路径)
        ext = splitext(path) # 返回(文件名,扩展名)
        if ext[1] == '': # 没有扩展名,使用默认的deffile,即path变为'...(服务器路径)/index.html'
            if path[-1] == '/':
                path += deffile
            else:
                path += '/' + deffile
        ldir = dirname(path) # path所在的目录路径
        if sep != '/': # path路径分隔符?猜测可能是考虑到不同操作系统平台
            ldir = replace(ldir, '/', sep) # 统一采用linux系统的格式
        if not isdir(ldir): # 路径不存在时,存档。
            if exists(ldir): unlink(ldir) # 删除ldir下原有的文件?
            makedirs(ldir)
        return path

    # download()方法连接网络,下载给定链接的页面
    def download(self): # download web page
        try:
            retval = urlretrieve(self.url, self.file) # 下载url页面成功,并保存在filename中,retval是一个元组,第一项是文件名(包括路径),第二项是服务器的响应头,?
        except IOError:
            retval = ('*** ERROR: invalid URL "%s"' % self.url,) # 下载页面失败,retval是一个包含字符串的单元组
        return retval

    # 如果上面的的处理没有发现任何错误,就会调用parseAndGetLinks()对新下载的主页进行分析,确定对那个web页面上的每一个连接应该采取什么样的行动。
    def parseAndGetLinks(self): # 解析HTML,保存链接
        self.parser = HTMLParser(AbstractFormatter(DumbWriter(StringIO()))) # 解析器?
        self.parser.feed(open(self.file).read()) # open(self.file).read()就是下载的页面,feed()方法是提取其中的链接,自动存入anchorlist列表。
        self.parser.close() # 关闭解析器?
        return self.parser.anchorlist # anchorlist里储存解析出来的html页面的链接

class Crawler(object): # 管理爬虫进程
    
    count = 0 # 已下载web页面计数器
    
    def __init__(self, url):
        self.q =[url] # 待下载链接队列,页面处理完毕变短,如果页面中发现新的链接,则会变长。
        self.seen = [] # 已下载队列
        self.dom = urlparse(url)[1] # 存储主链接域名,用于判断后续链接是否是该域的一部分。

    # 核心
    def getPage(self, url):
        r = Retrieve(url) # 实例化Retrieve对象
        retval = r.download() # 下载web页面
        if retval[0] == '*': # 下载出错,不进行解析
            print retval, '... skipping parse'
            return
        Crawler.count += 1 # 下载web页面没有问题,则已下载web页面计数器count加1,表示已经下载了一个web页面
        print '\n(', Crawler.count, ')'
        print 'URL:', url
        print 'FILE:', retval[0] 
        self.seen.append(url) # 将url添加到已下载队列
        
        links = r.parseAndGetLinks() # links包含了url指向的HTML页面的所有链接
        for eachlink in links:
            if eachlink[:-4] != 'http' and find(eachlink, '://') == -1:
                eachlink = urljoin(url, eachlink) 
            print '* ', eachlink,

            if find(lower(eachlink), 'mailto:') != -1:
                print '... discarded, mailto link'
                continue # 如果url指向的HTML页面有mailto链接(自动发送电子邮件的链接),则其将被忽略

            if eachlink not in self.seen:
                if find(eachlink, self.dom) == -1:
                    print '... discarded, not in domain' # 不在主链接域名的链接也会被忽略,比如说很多网站上都会有另外一个网站(不同域名)的链接,这类链接也会被忽略。
                else:
                    if eachlink not in self.q:
                        self.q.append(eachlink)
                        print '... new, added to Q' # 将链接加入到待下载链接队列中
                    else:
                        print '... discarded, already in Q' # 忽略已在待下载链接队列中的链接
                        
            else:
                print '... discarded, already processed' # 忽略已经下载过的链接

    # 启动Crawler(),不停地推进处理过程,直到队列为空。
    def go(self): # 处理待下载链接队列里的链接
        while self.q:
            url = self.q.pop()
            self.getPage(url)

def main():
    if len(argv) > 1:
        url = argv[1] # 就是在命令行以 python crawl.py url 启动程序
    else:
        try:
            url = raw_input('Enter starting URL: ')
        except (KeyboardInterrupt, EOFError):
            url = ''
 
        if not url: return
        robot = Crawler(url)
        robot.go()

if __name__ == "__main__":
    main()

        但是按照书中给出的url运行程序,会出问题,就是url打不开,后续的链接都不是同域名下的链接,可以尝试其他网页。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值