2010-11-22 2 views
2

Ich schreibe etwas Python-Code, um Websites zu scrappen, und was ich am Ende bin, ist eine wachsende Sammlung von benutzerdefinierten Scraper, jeder etwa 50 Linien lange und maßgeschneiderte extrahieren spezifische Informationen von einer bestimmten Website.Muster und Design für Funktionen, die stark voneinander abweichen, aber ähnlich verarbeitet werden

Meine erste Iteration des Programms ist eine riesige Datei, die eine Website als Argument nimmt, und kratzt diese Website, wenn sie es erkennt und benutzerdefinierten Code dafür hat (mithilfe einer riesigen Fallanweisung, ob sie die Website erkennt) .

Offensichtlich ist dies kein großartiges Design, also möchte ich die benutzerdefinierten Scrape-Funktionen in ihre eigenen Dateien/Klassen ziehen und ein kleines Skript haben, mit dem ich sie namentlich nennen kann. Zum Beispiel:

scrape.py --site google 

Und ich möchte eine Dateistruktur haben, ähnlich wie:

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

Ich habe noch nicht gemeistert Objektorientierung, aber ich erkenne, dass dies für sie ruft aus, und das, wonach ich suche, ist wahrscheinlich ein gemeinsames OO-Muster.

Haben Sie Hilfe dabei, diesen Code korrekt zu refaktorieren?

PS - Ich habe mir Scrapy angeschaut und es ist nicht wirklich das, was ich aus verschiedenen Gründen brauche.
PPS - Ich bin nicht wirklich scraping Suche Websites, ich kratze US-Gericht Websites.

Antwort

4

Sie den Code mit einer __init__ Methode in einer Klasse setzen kann alles konfiguriert, ein _download Verfahren um eine Verbindung zu der Website und laden Sie es zu erhalten, eine _store Methode, die Ergebnisse und eine run Methode zu speichern, es zu binden alle zusammen, etwa so:

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) 

Diese Klasse kann in Ihrer parser.py Datei leben.

Geben Sie in jede Ihrer Site-spezifischen Dateien zwei Dinge ein.

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 

Dann können Sie Ihre python.py Datei mit der folgenden Funktion ein:

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) 

Sie dann verwenden können, wie

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

es auf diese Weise tun hat den Vorteil, nicht erforderlich Sie können Änderungen an der Klasse Scraper vornehmen. Wenn Sie verschiedene Tricks ausführen müssen, damit die Server mit Ihrem Scraper kommunizieren können, können Sie in jedem Modul eine Downloader Klasse erstellen und diese wie die Klasse Parser verwenden. Wenn Sie zwei oder mehr Parser haben, die dasselbe tun, definieren Sie sie einfach als generischen Parser in einem separaten Modul und importieren Sie das in das Modul jeder Site, die es benötigt. Oder unterlasse es, um Tweaks zu machen. Ohne zu wissen, wie Sie die Websites herunterladen und analysieren, ist es schwierig, genauer zu sein.

Mein Gefühl ist, dass Sie möglicherweise mehrere Fragen stellen müssen, um alle Details ausgebügelt zu bekommen, aber es wird eine gute Lernerfahrung sein.

+0

Letztendlich verwendete ich beide Antworten hier, um die [Juriscraper] (https://bitbucket.org/mlissner/juriscraper/) -Bibliothek aufzubauen, aber ich lieh mir etwas mehr aus dieser heraus. Wirklich hilfreiches Zeug, danke! – mlissner

1

Ihre Technik für Refactoring ist, wie ich gehen würde. Hier ist, wie, schaue ich auf dieses Problem zu implementieren.

Erste

Ich würde eine einzige Funktion ScrapeHandler in allen Dateien in der Website-Verzeichnis namens erstellen - google.py, yahoo.py etc

def ScrapeHandler(...): 
    ... 

Zweite

Ich würde ein __init__.py im Website-Verzeichnis mit dem folgenden Inhalt erstellen.

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

Dritte

In der Hauptdatei scrape.py, würde ich den Schaber zur Laufzeit laden die entsprechende Schaben Logik zu wählen.

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

Für den Handler, endete ich beim Iterieren über die Variable __all__ in dem Modul, das ich gemacht habe, aber ja, gute Sachen hier. Vielen Dank! – mlissner