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

mli*_*ner 3 python oop design-patterns web-scraping

我正在编写一些 Python 代码来抓取网站,我最终会得到越来越多的自定义抓取工具,每个大约 50 行长,并且从特定网站定制提取特定信息。

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

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

scrape.py --site google
Run Code Online (Sandbox Code Playgroud)

我想要一个类似于以下内容的文件结构:

scrape.py
sites/
    google.py
    yahoo.py
    ...
    bing.py
Run Code Online (Sandbox Code Playgroud)

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

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

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

aar*_*ing 5

您可以将代码放在一个类中,其中包含一个__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)
Run Code Online (Sandbox Code Playgroud)

这个类可以存在于您的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
Run Code Online (Sandbox Code Playgroud)

然后您可以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)
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它

scraper = get_scraper('google')
scraper.run()
Run Code Online (Sandbox Code Playgroud)

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

我的感觉是您可能需要问几个问题才能解决所有细节,但这将是一次很好的学习体验。