如何解决如何解耦python应用程序的两个组件
我正在尝试学习python开发,并且我一直在阅读有关体系结构模式和代码设计的主题,因为我想停止黑客攻击并真正进行开发。我正在实现一个网络爬虫,并且您知道它的结构有问题,但是我不知道如何解决它。
搜寻器将返回一个操作列表,以在mongoDB实例中输入数据。
这是我的应用程序的一般结构:
Spiders
crawlers.py
connections.py
utils.py
__init__.py
crawlers.py 实现类型为Crawler
的类,每个特定的搜寻器都会继承它。每个抓取工具都有一个属性 table_name 和一个方法:crawl
。
在 connections.py 中,我实现了一个pymongo
驱动程序以连接到数据库。它期望将搜寻器作为其write
方法的参数。现在这是技巧部分了……rawler2取决于rawler1的结果,所以我最终得到这样的东西:
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
然后,在我的连接中,我创建了一个需要搜寻器的类。
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)]
,因此,这是(最大的)问题(因为我认为还有很多其他问题,其中一些我几乎无法理解,而另一些我还是完全看不到):实现连接需要上下文爬虫!例如:
mongo_driver = MongoDriver()
crawler1 = Crawler1()
crawler2 = Crawler2()
mongo_driver.write(crawler1)
mongo_driver.write(crawler2,list_of_codes=mongo_driver.get_list_of_codes())
如何解决呢?在这个结构中还有什么特别令人担忧的呢?感谢您的反馈!
解决方法
问题1 :MongoDriver
对您的搜寻器了解太多。您应该将驱动程序与crawler1
和crawler2
分开。我不确定您的crawl
函数返回什么,但是我认为它是A
类型的对象的列表。
您可以使用诸如CrawlerService
之类的对象来管理MongoDriver
和Crawler
之间的依赖关系。这会将驱动程序的写责任与爬虫的爬虫责任分开。该服务还将管理操作顺序,在某些情况下,这些顺序可能被认为足够好。
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
问题2 :Crawler1
和Crawler2
几乎相同;它们仅在我们调用crawl
函数时有所不同。考虑到DRY原理,您可以将搜寻算法分离为诸如策略之类的对象,并在其上具有一个Crawler依赖项(具有组合)。
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
的结构(例如表名等)仅存在于一个位置,以后可以对其进行扩展。
问题3 :从这里开始,您可以采用多种方法来改善总体设计。您可以通过创建依赖于数据库连接的新策略来删除CrawlService
。为了表示一种策略依赖于另一种策略(例如crawler1
产生crawler2
的结果),您可以将两种策略组合在一起,例如:
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:您还可以尝试使用除策略模式之外的其他替代方法,这可能取决于您使用的抓取工具数量及其总体结构,您必须使用装饰器模式。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。