我正在努力学习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的结果,因此我最后得到的结果如下:
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())如何解决这个问题呢?在这个结构中还有什么特别令人担忧的呢?谢谢你的反馈!
发布于 2020-08-18 20:04:00
问题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函数时才会有所不同。考虑到and的原理,您可以将爬行算法分离到诸如策略之类的对象中,并让一个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的结构(例如表名等)只存在于一个地方,您可以在以后对其进行扩展。
Problem 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:除了策略模式之外,你还可以尝试其他选择,可能取决于你有多少爬虫器,以及它们的一般结构,你需要使用装饰模式。
https://stackoverflow.com/questions/63469869
复制相似问题