功能的模式和设计彼此差异很大但是处理方式相似

时间:2010-11-22 08:11:13

标签: python oop design-patterns web-scraping

我正在编写一些Python代码来搜索网站,而我最终会得到的是越来越多的自定义搜索工具,每个搜索引擎大约有50行,并且可以从特定网站中提取特定信息。

我程序的第一次迭代是一个以网站为参数的巨型文件,如果它识别出来并且有自定义代码(使用巨大的case语句来检查它是否识别网站),则会抓取该网站。

显然,这不是一个很棒的设计,所以我想做的是将自定义scrape函数拉到自己的文件/类中,并有一个小脚本,我可以使用它来按名称调用它们。例如:

scrape.py --site google

我希望文件结构类似于:

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

我还没有掌握面向对象,但我认识到这是在呼唤它,而我正在寻找的可能是一种常见的OO模式。

任何有助于正确重构此代码的帮助?

PS - 我看过Scrapy,但由于种种原因,这并不是我所需要的 PPS - 我实际上并没有抓取搜索网站,我正在抓取美国法院网站。

2 个答案:

答案 0 :(得分:4)

您可以将代码放在一个使用__init__方法的类中,以便配置所有内容,使用_download方法连接到网站并下载,使用_store方法保存结果和run方法将它们组合在一起,如下所示:

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)

此课程可以存放在您的parser.py文件中。

在每个特定于站点的文件中,放两件事。

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

然后,您可以使用以下功能设置python.py文件:

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)

然后您可以像

一样使用它
scraper = get_scraper('google')
scraper.run()

这样做的好处是不要求您对Scraper类进行任何更改。如果您需要使用不同的技巧让服务器与您的scraper交谈,那么您可以在每个模块中创建一个Downloader类,并像Parser类一样使用它。如果您有两个或更多解析器执行相同的操作,只需将它们定义为单独模块中的通用解析器,然后将其导入到需要它的每个站点的模块中。或者将其子类化以进行调整。在不知道如何下载和解析网站的情况下,很难更具体。

我的感觉是你可能需要提出几个问题来解决所有细节问题,但这将是一次很好的学习经历。

答案 1 :(得分:1)

你的重构技术就是我的目标。这是如何,我看看实现这个问题。

<强>第一

我会在网站目录中的所有文件中创建一个名为ScrapeHandler的函数 - google.py,yahoo.py等

def ScrapeHandler(...):
    ...

<强>第二

我会在sites目录中创建一个带有以下内容的__i​​nit__.py。

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

<强>第三

在主文件scrape.py中,我会在运行时加载scraper来选择合适的抓取逻辑。

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(....)