2011-05-24 3 views
2

J'essaie d'écrire mon premier script Python, et avec beaucoup de Google, je pense que je suis à peu près terminé. Cependant, j'aurai besoin d'aide pour franchir la ligne d'arrivée.Scraper Web multi-thread utilisant urlretrieve sur un site compatible avec les cookies

Je dois écrire un script qui se connecte à un site compatible avec les cookies, racler un tas de liens, puis générer quelques processus pour télécharger les fichiers. J'ai le programme fonctionnant en single-thread, donc je sais que le code fonctionne. Mais, quand j'ai essayé de créer un groupe de travailleurs de téléchargement, j'ai couru dans un mur.

#manager.py 
import Fetch # the module name where worker lives 
from multiprocessing import pool 

def FetchReports(links,Username,Password,VendorID): 
    pool = multiprocessing.Pool(processes=4, initializer=Fetch._ProcessStart, initargs=(SiteBase,DataPath,Username,Password,VendorID,)) 
    pool.map(Fetch.DownloadJob,links) 
    pool.close() 
    pool.join() 


#worker.py 
import mechanize 
import atexit 

def _ProcessStart(_SiteBase,_DataPath,User,Password,VendorID): 
    Login(User,Password) 

    global SiteBase 
    SiteBase = _SiteBase 

    global DataPath 
    DataPath = _DataPath 

    atexit.register(Logout) 

def DownloadJob(link): 
    mechanize.urlretrieve(mechanize.urljoin(SiteBase, link),filename=DataPath+'\\'+filename,data=data) 
    return True 

Dans cette révision, le code échoue parce que les témoins ont pas été transférés au travailleur pour urlretrieve à utiliser. Pas de problème, j'ai pu utiliser la classe .cookiejar de mechanize pour enregistrer les cookies dans le manager, et les transmettre au worker.

#worker.py 
import mechanize 
import atexit 

from multiprocessing import current_process 

def _ProcessStart(_SiteBase,_DataPath,User,Password,VendorID): 
    global cookies 
    cookies = mechanize.LWPCookieJar() 

    opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) 

    Login(User,Password,opener) # note I pass the opener to Login so it can catch the cookies. 

    global SiteBase 
    SiteBase = _SiteBase 

    global DataPath 
    DataPath = _DataPath 

    cookies.save(DataPath+'\\'+current_process().name+'cookies.txt',True,True) 

    atexit.register(Logout) 

def DownloadJob(link): 
    cj = mechanize.LWPCookieJar() 
    cj.revert(filename=DataPath+'\\'+current_process().name+'cookies.txt', ignore_discard=True, ignore_expires=True) 
    opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cj)) 

    file = open(DataPath+'\\'+filename, "wb") 
    file.write(opener.open(mechanize.urljoin(SiteBase, link)).read()) 
    file.close 

Mais, cela ne fonctionne pas parce que ouvre (je pense) veut déplacer le fichier binaire au gestionnaire pour le traitement, et je reçois un message d'erreur « Impossible à l'objet cornichon », se référant à la page Web qu'il essaie de lire dans le fichier. La solution évidente est de lire les cookies du pot de cookie et de les ajouter manuellement à l'en-tête lors de la demande d'urlretrieve, mais j'essaie d'éviter cela, et c'est pourquoi je suis à la recherche de suggestions.

Répondre

4

Après avoir travaillé pendant la majeure partie de la journée, il s'avère que Mechanize n'était pas le problème, il ressemble plus à une erreur de codage. Après de nombreux ajustements et maudits, j'ai obtenu le code pour fonctionner correctement.

Pour les futurs Googlers comme moi, je suis fournir le code mis à jour ci-dessous:

#manager.py [unchanged from original] 
def FetchReports(links,Username,Password,VendorID): 
    import Fetch 
    import multiprocessing 

    pool = multiprocessing.Pool(processes=4, initializer=Fetch._ProcessStart, initargs=(SiteBase,DataPath,Username,Password,VendorID,)) 
    pool.map(Fetch.DownloadJob,_SplitLinksArray(links)) 
    pool.close() 
    pool.join() 


#worker.py 
import mechanize 
from multiprocessing import current_process 

def _ProcessStart(_SiteBase,_DataPath,User,Password,VendorID): 
    global cookies 
    cookies = mechanize.LWPCookieJar() 
    opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) 

    Login(User,Password,opener) 

    global SiteBase 
    SiteBase = _SiteBase 

    global DataPath 
    DataPath = _DataPath 

    cookies.save(DataPath+'\\'+current_process().name+'cookies.txt',True,True) 

def DownloadJob(link): 
    cj = mechanize.LWPCookieJar() 
    cj.revert(filename=DataPath+'\\'+current_process().name+'cookies.txt',True,True) 
    opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cj)) 

    mechanize.urlretrieve(url=mechanize.urljoin(SiteBase, link),filename=DataPath+'\\'+filename,data=data) 

Parce que je suis simplement dans le téléchargement des liens à partir d'une liste, la nature non-threadsafe de mécaniser ne semble pas être un problème [divulgation complète: j'ai exécuté ce processus exactement trois fois, donc un problème peut apparaître lors d'autres tests]. Le module de multitraitement et son pool de travailleurs font tout le travail. Maintenir les cookies dans les fichiers était important pour moi car le serveur web que je télécharge doit donner à chaque thread son propre ID de session, mais les autres personnes implémentant ce code n'ont pas besoin de l'utiliser. J'ai remarqué qu'il semble "oublier" les variables entre l'appel init et l'appel d'exécution, donc le cookiejar ne peut pas faire le saut.

+0

Encore un bogue dans mon code que je posterai comme question future, aucun de mes fils ne sortira correctement. La fonction atexit est là, mais elle ne se déclenche pas, sauf si je la change en décorateur. Mais, alors il perd toutes les variables de session que j'ai utilisées pour me connecter au site en premier lieu! Pour l'instant, il est permis de laisser les huit séances suspendues, mais je devrai revoir la procédure à l'avenir. –

4

La création d'une bande racleuse multitâche dans le bon sens est difficile. Je suis sûr que vous pourriez le gérer, mais pourquoi ne pas utiliser quelque chose qui a déjà été fait?

je suggère vraiment vraiment vous de vérifier Scrapy http://scrapy.org/

Il est un cadre de grattoir web open source très flexible qui va gérer la plupart des choses que vous auriez besoin ici. Avec Scrapy, l'exécution d'araignées simultanées est un problème de configuration et non un problème de programmation (http://doc.scrapy.org/topics/settings.html#concurrent-requests-per-spider). Vous obtiendrez également un support pour les cookies, les proxies, l'authentification HTTP et bien plus encore.

Pour moi, il a fallu environ 4 heures pour réécrire mon grattoir dans Scrapy. Alors, posez-vous la question: voulez-vous vraiment résoudre vous-même le problème du threading ou plutôt vous tourner vers les épaules des autres et vous concentrer sur les problèmes de raclage web, pas de threading?

PS. Utilisez-vous mécaniser maintenant? S'il vous plaît vérifier cet état de mécaniser FAQ http://wwwsearch.sourceforge.net/mechanize/faq.html:

« Est-il threadsafe

Non Pour autant que je sache, vous pouvez utiliser mécaniser dans le code threadé, mais il ne fournit pas la synchronisation: vous devez fournir vous-même. "

Si vous voulez vraiment continuer à utiliser mécanize, commencez à lire la documentation sur la façon de fournir la synchronisation. (Par exemple, http://effbot.org/zone/thread-synchronization.htm, http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm)

+1

De la question OP, il semble qu'il le fait pour l'éducation. Scrapy ne répondra donc pas à ses besoins. –

+0

Ah oui, je n'ai pas choisi cette chanson. Mais ouais je laisse toujours ma réponse telle quelle au cas où d'autres viendraient à cette réponse avec google. – jsalonen

+0

Scrapy ressemble à une excellente ressource, et je vais certainement vérifier que nos besoins augmentent. Cependant, mon code de grattage est déjà fonctionnel (et monothread), et n'a pas besoin d'un temps ou d'une logique qui me demanderait de commencer avec une autre solution. Les téléchargements, en revanche, sont beaucoup plus importants avec plus de 400 feuilles de calcul Excel qui doivent être téléchargées chaque semaine. –

0

Afin de permettre la session de cookie dans le premier exemple de code, ajoutez le code suivant à la fonction DownloadJob:

cj = mechanize.LWPCookieJar() 
opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cj)) 
mechanize.install_opener(opener) 

Et vous pouvez récupérer l'URL que vous:

mechanize.urlretrieve(mechanize.urljoin(SiteBase, link),filename=DataPath+'\\'+filename,data=data) 
Questions connexes