2010-06-28 37 views
1

Je suis en train de traiter 100k noms de domaine dans un fichier CSV basé sur les résultats de Siteadvisor en utilisant urllib (pas la meilleure méthode, je sais). Cependant, mon script actuel crée trop de threads et Python se heurte à des erreurs. Existe-t-il un moyen de "morceler" ce script pour faire X nombre de domaines à la fois (par exemple, 10-20) pour éviter ces erreurs? Merci d'avance.Comment puis-je diviser ce script python multithread en "morceaux"?

import threading 
import urllib 

class Resolver(threading.Thread): 
    def __init__(self, address, result_dict): 
     threading.Thread.__init__(self) 
     self.address = address 
     self.result_dict = result_dict 

    def run(self): 
     try: 
      content = urllib.urlopen("http://www.siteadvisor.com/sites/" + self.address).read(12000) 
      search1 = content.find("didn't find any significant problems.") 
      search2 = content.find('yellow') 
      search3 = content.find('web reputation analysis found potential security') 
      search4 = content.find("don't have the results yet.") 

      if search1 != -1: 
       result = "safe" 
      elif search2 != -1: 
       result = "caution" 
      elif search3 != -1: 
       result = "warning" 
      elif search4 != -1: 
       result = "unknown" 
      else: 
       result = "" 

      self.result_dict[self.address] = result 

     except: 
      pass 


def main(): 
    infile = open("domainslist", "r") 
    intext = infile.readlines() 
    threads = [] 
    results = {} 
    for address in [address.strip() for address in intext if address.strip()]: 
     resolver_thread = Resolver(address, results) 
     threads.append(resolver_thread) 
     resolver_thread.start() 

    for thread in threads: 
     thread.join() 

    outfile = open('final.csv', 'w') 
    outfile.write("\n".join("%s,%s" % (address, ip) for address, ip in results.iteritems())) 
    outfile.close() 

if __name__ == '__main__': 
    main() 

Modifier: nouvelle version, basée sur les suggestions de andyortlieb.

import threading 
import urllib 
import time 

class Resolver(threading.Thread): 
    def __init__(self, address, result_dict, threads): 
     threading.Thread.__init__(self) 
     self.address = address 
     self.result_dict = result_dict 
     self.threads = threads 
    def run(self): 
     try: 
      content = urllib.urlopen("http://www.siteadvisor.com/sites/" + self.address).read(12000) 
      search1 = content.find("didn't find any significant problems.") 
      search2 = content.find('yellow') 
      search3 = content.find('web reputation analysis found potential security') 
      search4 = content.find("don't have the results yet.") 

      if search1 != -1: 
       result = "safe" 
      elif search2 != -1: 
       result = "caution" 
      elif search3 != -1: 
       result = "warning" 
      elif search4 != -1: 
       result = "unknown" 
      else: 
       result = "" 

      self.result_dict[self.address] = result 

      outfile = open('final.csv', 'a') 
      outfile.write(self.address + "," + result + "\n") 
      outfile.close() 
      print self.address + result 

      threads.remove(self) 
     except: 
      pass 


def main(): 
    infile = open("domainslist", "r") 
    intext = infile.readlines() 
    threads = [] 
    results = {} 

    for address in [address.strip() for address in intext if address.strip()]: 
     loop=True 
     while loop: 
      if len(threads) < 20: 
       resolver_thread = Resolver(address, results, threads) 
       threads.append(resolver_thread) 
       resolver_thread.start() 
       loop=False 
      else: 
       time.sleep(.25) 


    for thread in threads: 
     thread.join() 

# removed so I can track the progress of the script 
# outfile = open('final.csv', 'w') 
# outfile.write("\n".join("%s,%s" % (address, ip) for address, ip in results.iteritems())) 
# outfile.close() 

if __name__ == '__main__': 
    main() 

Répondre

2

Cela pourrait être une sorte de rigide, mais vous pouvez passer des fils à résolveur, de sorte que lorsque résolveur .run est terminé, il peut appeler threads.remove (auto)

Ensuite, vous pouvez imbriquer certaines conditions afin que les threads ne sont créés que s'il y a de la place pour eux, et s'il n'y a pas de place, ils attendent jusqu'à ce qu'il y ait .

for address in [address.strip() for address in intext if address.strip()]: 
     loop=True 
     while loop: 
      if len(threads)<20: 
       resolver_thread = Resolver(address, results, threads) 
       threads.append(resolver_thread) 
       resolver_thread.start() 
       loop=False 
      else: 
       time.sleep(.25) 
+0

Merci pour votre aide jusqu'à présent. J'ai mis en place vos modifications. Cependant, le script n'atteint que 20 domaines. J'ai mis mon script ci-dessus. Sais-tu quel est le problème? – Tom

+0

Je crois que tout ce dont vous avez besoin est de changer de threads.remove (self) à self.threads.remove (auto-) – andyortlieb

+0

Facepalm. Je n'ai pas vu ça. Merci de votre aide. – Tom

2

Votre code existant fonctionnera très bien - il suffit de modifier votre méthode __init__ à l'intérieur Resolver à prendre dans une liste supplémentaire d'adresses au lieu d'un à la fois, au lieu d'avoir un fil pour chaque adresse, vous avez un fil pour tous les 10 (par exemple). De cette façon, vous ne surchargez pas le filetage.

De toute évidence, vous devrez également modifier légèrement run pour qu'il boucle le tableau d'adresses au lieu de self.address.

Je peux vous donner un exemple rapide si vous le souhaitez, mais à cause de la qualité de votre code, j'ai l'impression que vous pourrez le manipuler assez facilement.

Espérons que cela aide!

EDIT Exemple ci-dessous selon les besoins. Notez que vous devrez modifier main pour envoyer vos listes d'adresses d'instance Resolver au lieu d'une seule adresse - je ne pourrais pas gérer cela pour vous sans en savoir plus sur le format de votre fichier et comment les adresses sont stockées. Remarque - vous pouvez faire la méthode run avec une fonction d'aide, mais je pensais que cela pourrait être plus facile à comprendre comme un exemple

class Resolver(threading.Thread): 
    def __init__(self, addresses, result_dict): 
     threading.Thread.__init__(self) 
     self.addresses = addresses # Now takes in a list of multiple addresses 
     self.result_dict = result_dict 

    def run(self): 
     for address in self.addresses: # do your existing code for every address in the list 
      try: 
       content = urllib.urlopen("http://www.siteadvisor.com/sites/" + address).read(12000) 
       search1 = content.find("didn't find any significant problems.") 
       search2 = content.find('yellow') 
       search3 = content.find('web reputation analysis found potential security') 
       search4 = content.find("don't have the results yet.") 

       if search1 != -1: 
        result = "safe" 
       elif search2 != -1: 
        result = "caution" 
       elif search3 != -1: 
        result = "warning" 
       elif search4 != -1: 
        result = "unknown" 
       else: 
        result = "" 

       self.result_dict[address] = result 
      except: 
       pass 
+0

Pouvez-vous poster un exemple, s'il vous plaît? – Tom

+0

Je viens d'éditer ma réponse avec un exemple rapide - vous devrez éditer votre méthode 'run' pour passer des listes d'adresses au lieu d'une seule adresse, mais je vous ai laissé cela car je ne sais pas comment votre fichier d'entrée , etc. est formaté et je ne veux pas transmettre le code cassé. Comme vous pouvez le voir, c'est un changement mineur. – nearlymonolith

+0

Donc, dans ce cas, diviseriez-vous votre liste d'adresses par le nombre de threads que vous autorisez à créer, et en passant ces sections de la liste d'adresses à Resolver? Si c'est le cas, alors vous voudrez peut-être déplacer tout le strip() dans Main et Resolver. – andyortlieb