首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何将python应用程序的两个组件解耦?

如何将python应用程序的两个组件解耦?
EN

Stack Overflow用户
提问于 2020-08-18 13:38:16
回答 1查看 52关注 0票数 0

我正在努力学习python开发,我一直在阅读关于体系结构模式和代码设计的主题,因为我想停止黑客攻击并真正开发。我正在实现一个see爬虫,我知道它有一个有问题的结构,正如您将要看到的那样,但是我不知道如何修复它。

爬虫将返回用于在mongoDB实例中输入数据的操作列表。

这是我的应用程序的总体结构:

Spiders

crawlers.py

connections.py

utils.py

__init__.py

crawlers.py实现了一个类型为Crawler的类,每个特定的爬虫都继承它。每个Crawler都有一个属性table_name和一个方法:crawl。在connections.py中,我实现了一个pymongo驱动程序来连接到DB。它期望一个爬虫作为它的write方法的参数。现在来了魔术部分..。crawler2取决于crawler1的结果,因此我最后得到的结果如下:

代码语言:javascript
复制
from pymongo import InsertOne

class crawler1(Crawler):
    def __init__(self):
        super().__init__('Crawler 1', 'table_A')

    def crawl(self):
        return list of InsertOne

class crawler2(Crawler):
    def __init__(self):
        super().__init__('Crawler 2', 'table_B')

    def crawl(self, list_of_codes):
        return list of InsertOne # After crawling the list of codes/links

然后,在我的连接中,我创建了一个类,它需要一个爬虫。

代码语言:javascript
复制
class MongoDriver:
    def __init__.py
        self.db = MongoClient(...)

    def write(crawler, **kwargs):
        self.db[crawler.table_name].bulk_write(crawler.crawl(**kwargs))

    def get_list_of_codes():
        query = {}
        return [x['field'] for x in self.db.find(query)]

因此,这里出现了(最大的)问题(因为我认为还有许多其他的问题,其中一些我几乎无法理解,还有一些我仍然完全不了解):实现我的连接需要爬虫的上下文!!例如:

代码语言:javascript
复制
mongo_driver = MongoDriver()
crawler1 = Crawler1()
crawler2 = Crawler2()
mongo_driver.write(crawler1)
mongo_driver.write(crawler2, list_of_codes=mongo_driver.get_list_of_codes())

如何解决这个问题呢?在这个结构中还有什么特别令人担忧的呢?谢谢你的反馈!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-08-18 20:04:00

问题1MongoDriver对爬虫了解太多了。您应该将驱动程序从crawler1crawler2中分离出来。我不确定您的crawl函数返回什么,但我假设它是一个A类型对象的列表。

您可以使用像CrawlerService这样的对象来管理MongoDriverCrawler之间的依赖关系。这将将司机的书写责任与爬行者的爬行责任分开。该服务还将管理业务秩序,在某些情况下,这可能被认为是足够好的。

代码语言:javascript
复制
class Repository:

    def write(for_table: str, objects: 'List[A]'):
        self.db[for_table].bulk_write(objects)

class CrawlerService:

    def __init__(self, repository: Repository, crawlers: List[Crawler]):
        ...
   
    def crawl(self):
        crawler1, crawler2 = crawlers
        result = [repository.write(x) for x in crawler1.crawl()]
        ... # work with crawler2 and result 

问题2Crawler1Crawler2几乎是一样的;它们只有在调用crawl函数时才会有所不同。考虑到and的原理,您可以将爬行算法分离到诸如策略之类的对象中,并让一个Crawler依赖于它(包含组合)。

代码语言:javascript
复制
class CrawlStrategy(ABC):
    
    @abstractmethod
    def crawl(self) -> List[A]:
        pass
    
class CrawlStrategyA(CrawlStrategy):
    
    def crawl(self) -> List[A]:
        ...

class CrawlStrategyB(CrawlStrategy):
    
    def __init__(self, codes: List[int]):
        self.__codes = codes
    
    def crawl(self) -> List[A]:
        ...

    
class Crawler(ABC):
    
    def __init__(self, name: str, strategy: 'CrawlStrategy'):
        self.__name = name
        self.__strategy = strategy
       
    def crawl(self) -> List[int]:
        return self.__strategy.crawl()

通过这样做,Crawler的结构(例如表名等)只存在于一个地方,您可以在以后对其进行扩展。

Problem 3:从这里开始,您有多种改进总体设计的方法。您可以通过创建依赖于数据库连接的新策略来删除CrawlService。要表示一种策略依赖于另一种策略(例如,crawler1crawler2生成结果),您可以将两种策略组合在一起,例如:

代码语言:javascript
复制
class StrategyA(Strategy):
   
     def __init__(self, other: Strategy, database: DB):
          self.__other = other
          self.__db = database
    
     def crawl(self) -> 'List[A]':
          result = self.__other.crawl()
          self.__db.write(result)
          xs = self.__db.find(...)
          # do something with xs
          ...

当然,这是一个简化的示例,但将消除在数据库连接和爬行器之间使用单一中介的需要,并将提供更多的灵活性。此外,总体设计更容易测试,因为您所要做的就是对策略对象进行单元测试(您可以轻松地模拟数据库连接以执行DI)。

从这一点出发,接下来改进总体设计的步骤在很大程度上取决于实现的复杂性和一般需要多少灵活性。

PS:除了策略模式之外,你还可以尝试其他选择,可能取决于你有多少爬虫器,以及它们的一般结构,你需要使用装饰模式。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63469869

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档