2017-12-27 9 views
2

Ich habe eine Frage in Bezug auf die Leistung von ThreadPoolExecutor vs Thread Klasse für sich, die mir scheint, dass mir ein grundlegendes Verständnis fehlt.ThreadPoolExecutor vs threading.Thread

Ich habe einen Web-Scrapper in zwei Funktionen. Zuerst die Links für jedes Bild von einer Internet-Homepage und der zweiter zu analysieren, ein Bild von dem Link zu laden analysiert:

import threading 
import urllib.request 
from bs4 import BeautifulSoup as bs 
import os 
from concurrent.futures import ThreadPoolExecutor 

path = r'C:\Users\MyDocuments\Pythom\Networking\bbc_images_scraper_test' 
url = 'https://www.bbc.co.uk' 

# Function to parse link anchors for images 
def img_links_parser(url, links_list): 
    res = urllib.request.urlopen(url) 
    soup = bs(res,'lxml') 
    content = soup.findAll('div',{'class':'top-story__image'}) 

    for i in content: 
     try: 
      link = i.attrs['style'] 
      # Pulling the anchor from parentheses 
      link = link[link.find('(')+1 : link.find(')')] 
      # Putting the anchor in the list of links 
      links_list.append(link) 
     except: 
      # links might be under 'data-lazy' attribute w/o paranthesis 
      links_list.append(i.attrs['data-lazy']) 

# Function to load images from links 
def img_loader(base_url, links_list, path_location): 
    for link in links_list: 
     try: 
      # Pulling last element off the link which is name.jpg 
      file_name = link.split('/')[-1] 
      # Following the link and saving content in a given direcotory 
      urllib.request.urlretrieve(urllib.parse.urljoin(base_url, link), 
      os.path.join(path_location, file_name)) 
     except: 
      print('Error on {}'.format(urllib.parse.urljoin(base_url, link))) 

Der folgende Code wird in zwei Fälle aufgeteilt:

Fall 1: I verwende mehrere Threads:

threads = [] 
t1 = threading.Thread(target = img_loader, args = (url, links[:10], path)) 
t2 = threading.Thread(target = img_loader, args = (url, links[10:20], path)) 
t3 = threading.Thread(target = img_loader, args = (url, links[20:30], path)) 
t4 = threading.Thread(target = img_loader, args = (url, links[30:40], path)) 
t5 = threading.Thread(target = img_loader, args = (url, links[40:50], path)) 
t6 = threading.Thread(target = img_loader, args = (url, links[50:], path)) 

threads.extend([t1,t2,t3,t4,t5,t6]) 
for t in threads: 
    t.start() 
for t in threads: 
    t.join() 

Der obige Code macht seine Arbeit auf meiner Maschine für 10 Sekunden.

Fall 2: Ich bin mit ThreadPoolExecutor

with ThreadPoolExecutor(50) as exec: 
    results = exec.submit(img_loader, url, links, path) 

Die obigen Code führt zu 18 Sekunden.

Mein Verständnis war, dass ThreadPoolExecutor einen Thread für jeden Arbeiter erstellt. Wenn ich also max_workers auf 50 setzen würde, würde das 50 Threads ergeben und hätte daher den Job schneller erledigt.

Kann mir bitte jemand erklären, was ich hier vermisse? Ich gebe zu, dass ich hier einen dummen Fehler mache, aber ich verstehe es einfach nicht.

Vielen Dank!

+0

als @hansaplast bemerkte ich wurde mit nur einem Arbeiter. Also habe ich einfach meine 'img_loader' Funktion geändert, um einen einzelnen Link zu akzeptieren und dann eine' for' Schleife unter dem Kontextmanager hinzugefügt, um jeden Link in der Liste zu verarbeiten. Und es reduzierte die Zeit auf 3,8 Sekunden. – Vlad

Antwort

2

In Fall 2 senden Sie alle Links an einen Mitarbeiter. Statt

exec.submit(img_loader, url, links, path) 

müssen Sie zu:

for link in links: 
    exec.submit(img_loader, url, [link], path) 

ich es nicht versuchen, mich, das ist nur von reading the documentation of ThreadPoolExecutor

+1

ja, du hast absolut recht. Ich weiß nicht, warum ich das nicht selbst versucht habe, obwohl ich auch darüber nachgedacht habe. Vielen Dank für das Zurückkommen zu mir! Das Ergebnis ist 3,8 Sekunden, was cool ist! :) – Vlad

Verwandte Themen