python 网络爬虫(3) 多线程,gzip加速,网页下载

python 网络爬虫(三) 多线程,gzip加速,网页下载

上一篇的 python 网络爬虫(二) BFS不断抓URL并放到文件中 其实还不够正常,很少看到不用多线程的爬虫。

本篇添加如下功能:

【1】维护一个公用队列,开启多线程,对这个队列进行获取URL和处理

【2】对页面的下载,放到特定的文件夹中

【3】下载请求用gzip形式下载到本地解压,降低网络资源负荷

python每一时刻只能处理一个线程,多线程有的时候因为切换线程而拖慢速度,但是为什么还要多线程呢?因为一个字“多”!

对于自动化的玩意,“多”很重要。

spider.py

# -*- coding: cp936 -*-
import urllib,urllib2,sgmllib,os
import time,threading,Queue,re,sys,StringIO,gzip

class URLList(sgmllib.SGMLParser):
    def reset(self):
        sgmllib.SGMLParser.reset(self)
        #maxsize < 1 表示无穷队列
        self.URLqueue = Queue.Queue(maxsize = -1)
        
    def start_a(self,attrs):
        href = [v for k,v in attrs if k == 'href']
        if href:
            for u in href:
                #判断URL是不是正确的
                pat = re.compile(r'http://(.+?)')
                if len(re.findall(pat,u)) == 0:
                    continue        
                self.URLqueue.put(u)

class spider(threading.Thread):
    def __init__(self,name,parser,dicPath = os.path.abspath(os.path.dirname(sys.argv[0]))+"\\page_downloads"):
        threading.Thread.__init__(self)
        self.name = name
        self.parser = parser
        self.pageCount = 0
        self.dicPath = dicPath+"\\"+name
        self.TIMEOUT = 10        
    def run(self):
        #创建一个downloads文件夹放置下载的pages
        if os.path.exists(self.dicPath) == False :
            os.mkdir(self.dicPath)
        opener = urllib2.build_opener()
        #共用URLList的URLqueue,如果队列为空有两种情况
        #一种是没有可抓取的URL了,第二种是在等待线程抓ing
        #这里默认为第二种情况,当队列为空超过TIMEOUT,就判定为结束    
        start = time.clock()
        end = time.clock()
        while True:
            if self.parser.URLqueue.empty() == False:
                start = time.clock()
                
                url = self.parser.URLqueue.get()
                print self.name + ": "+url
                #用gzip的方式下载网页,提高速度
                request = urllib2.Request(url)
                request.add_header('Accept-encoding','gzip')
                try:
                    page = opener.open(request)
                    if page.code == 200:
                        predata = page.read()
                        pdata = StringIO.StringIO(predata)
                        gzipper = gzip.GzipFile(fileobj = pdata)
                        try:
                            data = gzipper.read()
                        except:
                            #如果服务器不支持gzip,那么就直接下载网页
                            data = predata
                        try:
                            self.parser.feed(data)
                        except Exception as e:
                            print "页面分析不了: "+str(e)
                        try:
                            filePath = self.dicPath+"\\"+str(self.pageCount)+".html"
                            self.pageCount += 1
                            file = open(filePath,'w')
                            file.write(data)
                            file.close()
                        except:
                            print "文件写错误"
                except Exception as e:
                    print "请求错误: "+str(e)
            else:
                #如果空队列这种状态保持TIMEOUT秒,就退出
                end = time.clock()
                if end - start > self.TIMEOUT:
                    break
                                
thCnt = 2
thList = []
startURL = "http://www.baidu.com"
parser = URLList()
URLdata = urllib.urlopen(startURL)
parser.feed(URLdata.read())
URLdata.close()

for i in range(thCnt):
    th = spider('th'+str(i),parser)
    thList.append(th)
for t in thList:
    t.start()

for t in thList:
    t.join()
print "处理结束"


1楼rsj2176小时前
写得很好