Heim > Artikel > Backend-Entwicklung > Python verwendet Multithreading zum Crawlen von Webseiteninformationen
In diesem Artikel wird hauptsächlich die Multithread-Webseiten-Crawling-Funktion von Python vorgestellt. Er analysiert die zugehörigen Betriebstechniken und Vorsichtsmaßnahmen der Python-Multithread-Programmierung im Detail und enthält außerdem ein Demo-Beispiel, das die Multithread-Funktion darstellt -Threaded-Webseiten-Crawling-Methode. Freunde, die sie benötigen, können sich auf
beziehen. Dieser Artikel beschreibt das Beispiel der Python-Implementierung der Multithread-Webseiten-Crawling-Funktion. Teilen Sie es als Referenz mit allen. Die Details lauten wie folgt:
In letzter Zeit habe ich Dinge getan, die mit Webcrawlern zu tun haben. Ich habe mir den in Open Source C++ geschriebenen Larbin-Crawler angesehen und die Designideen und die Implementierung einiger Schlüsseltechnologien sorgfältig gelesen.
1. Die URL-Wiederverwendung ist ein sehr effizienter Bloom-Filter-Algorithmus.
2. Für die URL-Warteschlangenverarbeitung wird Folgendes verwendet: Strategie des teilweisen Zwischenspeicherns im Speicher und teilweisen Schreibens in Dateien.
4. Larbin hat viel an dateibezogenen Vorgängen gearbeitet
5. Es gibt einen Verbindungspool, der die GET-Methode im HTTP-Protokoll an die Zielseite sendet den Inhalt und analysiert dann die Header.
Eine große Anzahl von Deskriptoren, E/A-Multiplexing durch die Poll-Methode, sehr effizient
Eine große Anzahl Die vom Autor verwendeten Datenstrukturen sind seine eigenen. Ich habe von unten angefangen und im Grunde keine Dinge wie STL verwendet.
Es gibt noch viele weitere. Ich werde einen Artikel schreiben und sie zusammenfassen Ich habe Zeit in der Zukunft.
fetchPage.py
Verwenden Sie die Get-Methode des HTTP-Protokolls, um die Seite herunterzuladen und speichern Sie es. Für die Datei
''' Created on 2012-3-13 Get Page using GET method Default using HTTP Protocol , http port 80 @author: xiaojay ''' import socket import statistics import datetime import threading socket.setdefaulttimeout(statistics.timeout) class Error404(Exception): '''Can not find the page.''' pass class ErrorOther(Exception): '''Some other exception''' def __init__(self,code): #print 'Code :',code pass class ErrorTryTooManyTimes(Exception): '''try too many times''' pass def downPage(hostname ,filename , trytimes=0): try : #To avoid too many tries .Try times can not be more than max_try_times if trytimes >= statistics.max_try_times : raise ErrorTryTooManyTimes except ErrorTryTooManyTimes : return statistics.RESULTTRYTOOMANY,hostname+filename try: s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #DNS cache if statistics.DNSCache.has_key(hostname): addr = statistics.DNSCache[hostname] else: addr = socket.gethostbyname(hostname) statistics.DNSCache[hostname] = addr #connect to http server ,default port 80 s.connect((addr,80)) msg = 'GET '+filename+' HTTP/1.0\r\n' msg += 'Host: '+hostname+'\r\n' msg += 'User-Agent:xiaojay\r\n\r\n' code = '' f = None s.sendall(msg) first = True while True: msg = s.recv(40960) if not len(msg): if f!=None: f.flush() f.close() break # Head information must be in the first recv buffer if first: first = False headpos = msg.index("\r\n\r\n") code,other = dealwithHead(msg[:headpos]) if code=='200': #statistics.fetched_url += 1 f = open('pages/'+str(abs(hash(hostname+filename))),'w') f.writelines(msg[headpos+4:]) elif code=='301' or code=='302': #if code is 301 or 302 , try down again using redirect location if other.startswith("http") : hname, fname = parse(other) downPage(hname,fname,trytimes+1)#try again else : downPage(hostname,other,trytimes+1) elif code=='404': raise Error404 else : raise ErrorOther(code) else: if f!=None :f.writelines(msg) s.shutdown(socket.SHUT_RDWR) s.close() return statistics.RESULTFETCHED,hostname+filename except Error404 : return statistics.RESULTCANNOTFIND,hostname+filename except ErrorOther: return statistics.RESULTOTHER,hostname+filename except socket.timeout: return statistics.RESULTTIMEOUT,hostname+filename except Exception, e: return statistics.RESULTOTHER,hostname+filename def dealwithHead(head): '''deal with HTTP HEAD''' lines = head.splitlines() fstline = lines[0] code =fstline.split()[1] if code == '404' : return (code,None) if code == '200' : return (code,None) if code == '301' or code == '302' : for line in lines[1:]: p = line.index(':') key = line[:p] if key=='Location' : return (code,line[p+2:]) return (code,None) def parse(url): '''Parse a url to hostname+filename''' try: u = url.strip().strip('\n').strip('\r').strip('\t') if u.startswith('http://') : u = u[7:] elif u.startswith('https://'): u = u[8:] if u.find(':80')>0 : p = u.index(':80') p2 = p + 3 else: if u.find('/')>0: p = u.index('/') p2 = p else: p = len(u) p2 = -1 hostname = u[:p] if p2>0 : filename = u[p2:] else : filename = '/' return hostname, filename except Exception ,e: print "Parse wrong : " , url print e def PrintDNSCache(): '''print DNS dict''' n = 1 for hostname in statistics.DNSCache.keys(): print n,'\t',hostname, '\t',statistics.DNSCache[hostname] n+=1 def dealwithResult(res,url): '''Deal with the result of downPage''' statistics.total_url+=1 if res==statistics.RESULTFETCHED : statistics.fetched_url+=1 print statistics.total_url , '\t fetched :', url if res==statistics.RESULTCANNOTFIND : statistics.failed_url+=1 print "Error 404 at : ", url if res==statistics.RESULTOTHER : statistics.other_url +=1 print "Error Undefined at : ", url if res==statistics.RESULTTIMEOUT : statistics.timeout_url +=1 print "Timeout ",url if res==statistics.RESULTTRYTOOMANY: statistics.trytoomany_url+=1 print e ,"Try too many times at", url if __name__=='__main__': print 'Get Page using GET method'
''' Created on 2012-3-16 @author: xiaojay ''' import fetchPage import threadpool import datetime import statistics import urllib2 '''one thread''' def usingOneThread(limit): urlset = open("input.txt","r") start = datetime.datetime.now() for u in urlset: if limit <= 0 : break limit-=1 hostname , filename = parse(u) res= fetchPage.downPage(hostname,filename,0) fetchPage.dealwithResult(res) end = datetime.datetime.now() print "Start at :\t" , start print "End at :\t" , end print "Total Cost :\t" , end - start print 'Total fetched :', statistics.fetched_url '''threadpoll and GET method''' def callbackfunc(request,result): fetchPage.dealwithResult(result[0],result[1]) def usingThreadpool(limit,num_thread): urlset = open("input.txt","r") start = datetime.datetime.now() main = threadpool.ThreadPool(num_thread) for url in urlset : try : hostname , filename = fetchPage.parse(url) req = threadpool.WorkRequest(fetchPage.downPage,args=[hostname,filename],kwds={},callback=callbackfunc) main.putRequest(req) except Exception: print Exception.message while True: try: main.poll() if statistics.total_url >= limit : break except threadpool.NoResultsPending: print "no pending results" break except Exception ,e: print e end = datetime.datetime.now() print "Start at :\t" , start print "End at :\t" , end print "Total Cost :\t" , end - start print 'Total url :',statistics.total_url print 'Total fetched :', statistics.fetched_url print 'Lost url :', statistics.total_url - statistics.fetched_url print 'Error 404 :' ,statistics.failed_url print 'Error timeout :',statistics.timeout_url print 'Error Try too many times ' ,statistics.trytoomany_url print 'Error Other faults ',statistics.other_url main.stop() '''threadpool and urllib2 ''' def downPageUsingUrlib2(url): try: req = urllib2.Request(url) fd = urllib2.urlopen(req) f = open("pages3/"+str(abs(hash(url))),'w') f.write(fd.read()) f.flush() f.close() return url ,'success' except Exception: return url , None def writeFile(request,result): statistics.total_url += 1 if result[1]!=None : statistics.fetched_url += 1 print statistics.total_url,'\tfetched :', result[0], else: statistics.failed_url += 1 print statistics.total_url,'\tLost :',result[0], def usingThreadpoolUrllib2(limit,num_thread): urlset = open("input.txt","r") start = datetime.datetime.now() main = threadpool.ThreadPool(num_thread) for url in urlset : try : req = threadpool.WorkRequest(downPageUsingUrlib2,args=[url],kwds={},callback=writeFile) main.putRequest(req) except Exception ,e: print e while True: try: main.poll() if statistics.total_url >= limit : break except threadpool.NoResultsPending: print "no pending results" break except Exception ,e: print e end = datetime.datetime.now() print "Start at :\t" , start print "End at :\t" , end print "Total Cost :\t" , end - start print 'Total url :',statistics.total_url print 'Total fetched :', statistics.fetched_url print 'Lost url :', statistics.total_url - statistics.fetched_url main.stop() if __name__ =='__main__': '''too slow''' #usingOneThread(100) '''use Get method''' #usingThreadpool(3000,50) '''use urllib2''' usingThreadpoolUrllib2(3000,50)Experimentelle Analyse:
Experimentelle Daten:
Die 3000 von Larbin erfassten URLs werden vom Mercator-Warteschlangenmodell verarbeitet (ich habe es in C++ implementiert und werde in Zukunft einen Blog veröffentlichen, wenn ich die Gelegenheit dazu habe. Die URL-Sammlung erfolgt zufällig und repräsentativ). Verwenden Sie einen Thread-Pool mit 50 Threads.Experimentelle Umgebung: Ubuntu10.04, gutes Netzwerk, Python2.6
Speicherung: kleine Dateien, jede Seite, eine Datei zur SpeicherungPS: Da der Internetzugang der Schule auf basiert Traffic Für das Web-Crawling fallen Gebühren an, was eine Verschwendung von regulärem Traffic darstellt! ! ! In ein paar Tagen führen wir möglicherweise ein groß angelegtes URL-Download-Experiment durch und testen es mit Hunderttausenden URLs.
Experimentelle Ergebnisse:
Verwendungurllib2
, usingThreadpoolUrllib2(3000,50)Beginn am: 16.03.2012 22: 18: 20.956054 Ende bei: 2012-03-16 22: 22: 15.203018
Gesamtkosten: 0: 03: 54.246964 Gesamt-URL: 3001
Gesamtgeholt: 2442
Lo lo lo st-URL: 559
Physische Speichergröße der Download-Seite: 84088 KB
Ende am: 2012-03-16 22:26:26.843563
Gesamtkosten: 0:02:46.636833Gesamt-URL: 3002
Gesamtabgerufen: 2484
Verloren URL : 518
Fehler 404: 94
Fehler-Timeout: 312
Fehler Zu oft versuchen 0
Fehler Andere Fehler 112
Physische Speichergröße der Download-Seite: 87168 KB
Zusammenfassung: Das Programm zum Herunterladen von Seiten, das ich selbst geschrieben habe, ist sehr effizient und hat weniger verlorene Seiten. Wenn Sie darüber nachdenken, gibt es jedoch tatsächlich noch viele Stellen, die optimiert werden können. Beispielsweise führt die Erstellung und Freigabe zu vieler kleiner Dateien zu einem erheblichen Leistungsaufwand für das Programm Verwendet Hash-Benennung, was auch bei der Berechnung zu vielen Problemen führen kann. Wenn Sie eine gute Strategie haben, können diese Kosten tatsächlich weggelassen werden. Zusätzlich zu DNS müssen Sie nicht die mit Python gelieferte DNS-Auflösung verwenden, da die Standard-DNS-Auflösung ein synchroner Vorgang ist und die DNS-Auflösung im Allgemeinen zeitaufwändig ist, sodass sie asynchron mit mehreren Threads erfolgen kann In Verbindung mit entsprechendem DNS-Caching kann die Effizienz erheblich verbessert werden. Darüber hinaus gibt es während des eigentlichen Seiten-Crawling-Prozesses eine große Anzahl von URLs, die nicht gleichzeitig im Speicher gespeichert werden können. Stattdessen sollten sie gemäß einer bestimmten Strategie oder einem bestimmten Algorithmus sinnvoll zugewiesen werden. Kurz gesagt, es gibt noch viele Dinge, die auf der Sammlungsseite erledigt werden müssen und die optimiert werden können.
Das obige ist der detaillierte Inhalt vonPython verwendet Multithreading zum Crawlen von Webseiteninformationen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!