2010-11-22 29 views
2

J'écris du code Python pour gratter des sites Web, et je vais finir par créer une collection croissante de grattoirs personnalisés, chacun d'environ 50 lignes longues et sur mesure extraire des informations spécifiques à partir d'un site Web spécifique. Ma première itération du programme est un fichier géant qui prend un site Web comme argument, et racle ce site Web s'il le reconnaît et a un code personnalisé (en utilisant une déclaration de cas géant pour vérifier s'il reconnaît le site Web) .Motif et conception pour des fonctions très différentes les unes des autres mais traitées de manière similaire

De toute évidence, ce n'est pas une conception géniale, ce que je voudrais faire est de tirer les fonctions de raclage personnalisées dans leurs propres fichiers/classes, et avoir un petit script que je peux utiliser pour les appeler par leur nom. Par exemple:

scrape.py --site google 

Je voudrais avoir une structure de fichier similaire à:

scrape.py 
sites/ 
    google.py 
    yahoo.py 
    ... 
    bing.py 

Je ne l'ai pas encore maîtrisé l'orientation objet, mais je reconnais que cela appelle pour elle, et que ce que je cherche est probablement un modèle OO commun.

Toute aide pour obtenir ce code refactorisé correctement? Post-scriptum - J'ai regardé Scrapy, et ce n'est pas vraiment ce dont j'ai besoin pour diverses raisons.
PPS - Je ne suis pas en train de gratter les sites de recherche, je suis en train de gratter les sites Web des tribunaux américains.

Répondre

4

Vous pouvez mettre le code dans une classe avec une méthode __init__ pour obtenir tout configuré, une méthode _download pour se connecter sur le site et le télécharger, une méthode _store pour enregistrer les résultats et une méthode run pour l'attacher tous ensemble, comme ceci:

class Scraper(object): 
    def __init__(self, parser, page_generator): 
     self._parser = parser 
     self._pages = pages 

    def _download(self, page): 
     # do whatever you're already doing to download it 
     return html 

    def _store(self, data): 
     # Do whatever you're already doing to store the data 

    def run(self): 
     for page in pages: 
      html = self._download(page) 
      data = self._parser.parse(html) 
      self._store(data) 

Cette classe peut vivre dans votre fichier parser.py.

Dans chacun de vos fichiers spécifiques au site, mettez deux choses.

class Parser(object): 
    def parse(html): 
     # All of your rules go here 

def pages(some, args, if_, you, need, them): # but they should be the same for all files 
    return a_list_of_pages_or_generator 

Ensuite, vous pouvez configurer votre fichier python.py avec la fonction suivante:

def get_scraper(name): 
    mod = __import__(name) 

    parser = mod.Parser() 
    pages = mod.pages() # Pass whatever args you need to figure out the urls 

    return Scraper(parser, pages) 

Vous pouvez alors l'utiliser comme

scraper = get_scraper('google') 
scraper.run() 

En procédant ainsi, a l'avantage de ne pas nécessiter vous pour apporter des modifications à la classe Scraper. Si vous devez faire des trucs différents pour que les serveurs communiquent avec votre grattoir, vous pouvez créer une classe Downloader dans chaque module et l'utiliser comme la classe Parser. Si vous avez deux analyseurs ou plus qui font la même chose, il suffit de les définir comme un analyseur générique dans un module séparé et de l'importer dans le module de chaque site qui le requiert. Ou sous-classe pour faire des réglages. Sans savoir comment vous téléchargez et analysez les sites, il est difficile d'être plus précis. Mon sentiment est que vous pourriez avoir à poser plusieurs questions pour obtenir tous les détails aplani mais ce sera une bonne expérience d'apprentissage.

+0

En fin de compte, j'ai utilisé les deux réponses ici pour construire la bibliothèque [Juriscraper] (https://bitbucket.org/mlissner/juriscraper/), mais j'ai emprunté plus fortement à celle-ci. Trucs vraiment utiles, merci! – mlissner

1

Votre technique pour le refactoring est comment je voudrais aller. Voici comment, je regarde l'application de ce problème.

Première

Je voudrais créer une fonction unique appelée ScrapeHandler dans tous les fichiers dans le répertoire du site - google.py, yahoo.py etc

def ScrapeHandler(...): 
    ... 

Deuxième

Je créerais un __init__.py dans le répertoire de sites avec le contenu suivant.

scrapers = ["google", "yahoo", ...] 

Troisième

Dans le fichier principal scrape.py, je charge le grattoir à l'exécution de choisir la logique racleuse appropriée.

from sites import scrapers 
all_scrapers = {} 
...... 
# Load all scrapers 
for scraper_name in scrapers: 
    all_scrapers[scraper_name] = __import__('%s.%s' % (sites.__name__, scraper_name), fromlist=[scraper_name], level=0) 
# get the input on what to scrape via command line etc 
scraper_name = .. 
assert scraper_name not in scrapers 
# call the function based on name 
scrapeHandler = all_scrapers.get(scraper_name, None) 
if scrapeHandler is not None: 
    scrapeHandler(....) 
+0

Pour le gestionnaire, j'ai fini par itérer sur la variable __all__ dans le module que j'ai créé, mais oui, de bonnes choses ici. Je vous remercie! – mlissner