网络爬虫Web开始
一、介绍
该程序主体是《Python核心编程第二版》例20.2。本篇会修改部分代码及添加了相关注释。
ps:该书该例程不能直接运行,需要修改。

二、功能
网络爬虫crawl.py抓取web的开始页面地址,下载该页面和其他后续链接页面,但是仅限于那些与开始页面有着相同域名页面。

三、程序
crawl.py

#coding=utf-8

#20170628 why
'''

本程序执行成功后会在本地产生文件名太长或文件夹路径太深无法删除的情况,解决方法如下:
在要删除的目录新建t1
在该目录下的cmd中执行 D:\it\Python\HTTP_Client>robocopy t1 www.runoob.com /MIR
接着删除t1www.runoob.com文件夹
'''

from sys import argv
from os import makedirsunlinksep
from os.path import dirnameexistsisdirsplitext
from string import replacefindlower
from htmllib import HTMLParser
from urllib import urlretrieve
from urlparse import urlparseurljoin
from formatter import DumbWriterAbstractFormatter
from cStringIO import StringIO

class Retriever(object):
    def __init__(selfurl):
        self.url url
        self.file self.filename(url)

    def filename(selfurldeffile 'index.html'):
        '''
        该函数将输入的网址作为相对路径并创建。若该路径存在则删除后重新创建;若该路径不存在则新建。返回本地保存的路径。
        :param url:地址
        :param deffile:要爬的主页名
        :return:返回路径
        '''
        parsedurl urlparse(url'http:'0)
        print parsedurl[0]  #http
       print parsedurl[1]  #www.null.com
       print parsedurl[2]  #/home/index.html
        path parsedurl[1parsedurl[2]
        print path          #www.null.com/home/index.html
        ext splitext(path)
        print ext           #('www.null.com/home/index', '.html')
       print ext[1]        #.html
       print path[-1]      #l
        if ext[1== '':
            if path[-1== '/':
                path += deffile
                print path
            else:
                path += '/' deffile
                print path
        ldir dirname(path)
        print ldir      #www.null.com/home
       print sep       #\
        if sep != '/':
            ldir replace(ldir'/'sep)
        if not isdir(ldir):     #判断是否是文件夹,若不是文件夹isdir(ldir)False;若是文件夹isdir(ldir)True
            if exists(ldir):    #判断文件夹是否存在,若存在exists(ldir)True;若不存在exists(ldir)False;
                unlink(ldir)    #unlink() 方法用于删除文件,如果文件是一个目录则返回一个错误。
            makedirs(ldir)  #修改错误处 makedirs() 方法用于递归创建目录。若之前存在或不存在都需要新建。

        print path  #www.null.com/home/index.html
        return path #返回路径

    def download(self):
        '''
        该函数直接将远程数据下载到本地。
        urlretrieve(url, filename, reporthook);
        url:外部或者本地url
        filename:指定了保存到本地的路径(如果未指定该参数,urllib会生成一个临时文件来保存数据);
        reporthook:是一个回调函数,当连接上服务器、以及相应的数据块传输完毕的时候会触发该回调。我们可以利用这个回调函数来显示当前的下载进度。
        :return:返回网址
        '''
        try:
            print self.url  #http://www.null.com/home/index.html
           print self.file #www.null.com/home/index.html
            retval urlretrieve(self.urlself.file)   #直接将远程数据下载到本地。
        except IOError:
            retval ('*** Error: invaild URL "%s"' self.url)
        return retval   #修改错误处

    def parseAndGetLinks(self):
        #HTMLParser实现HTML文件的分析。
        #StringIO是从内存中读取数据;DumbWriter将事件流转换为存文本文档
        self.paraser HTMLParser(AbstractFormatter(DumbWriter(StringIO())))
        print self.paraser
        self.paraser.feed(open(self.file).read())   #feed方法将接收数据
        self.paraser.close()
        print self.paraser.anchorlist
        return self.paraser.anchorlist #返回地址和日期


class Crawler(object):
    count 0

    def __init__(selfurl):
        self.q [url]
        self.seen []
        self.dom urlparse(url)[1]

    def getPage(selfurl):
       Retriever(url)
        retval r.download()
        #print retval #('www.null.com/home/index.html', )
        if retval[0== '*':
            print retval'... skipping parase'
            return
        Crawler.count += 1
        print '\n('Crawler.count,')'
        print 'URL:'url
        print 'FILE:'retval[0]
        self.seen.append(url)   #['http://www.null.com/home/index.html'] append() 方法用于在列表末尾添加新的对象
        print self.seen

        links r.parseAndGetLinks()
        print links
        for eachLink in links:
            print eachLink[:4]
            print find(eachLink'://')
            if eachLink[:4!= 'http' and find(eachLink'://'== -1#find() 方法检测字符串中是否包含子字符串 str
                eachLink urljoin(urleachLink)
            print '*'eachLink

            if find(lower(eachLink)'mailto'!= -1:
                print '... discarded, mailto link'
                continue

            if eachLink not in self.seen:
                if find(eachLinkself.dom) == -1:
                    print '... discared, not in domain'
                else:
                    if eachLink not in self.q:
                        self.q.append(eachLink)
                        print '... new, added to Q'
                    else:
                        print '... discared, already in Q'
            else:
                print '... discarded, alraedy processed'

    def go(self):
        while self.q:
            url self.q.pop() #pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
            self.getPage(url)


def main():
    if len(argv) 1:
        url argv[1]

    else:
        try:
            url raw_input('Enter starting URL:')
        except (KeyboardInterruptEOFError):
            url ''
    if not url: return
    robot Crawler(url)
    robot.go()

if __name__ == '__main__'#使用http://www.runoob.com/w3cnote/index.html
    main()
posted on 2017-07-02 14:06  在路上,赢在自己  阅读(303)  评论(0)    收藏  举报