如何獲取指定模塊下全部的類

前言

在使用 scrapy 時,運行爬蟲僅須要經過 scrapy crawl 爬蟲名 就可啓動咱們寫好的爬蟲,那麼 scrapy 是如何經過名稱找到爬蟲類的呢?經過分析源碼可窺見一二,同時也可從中找出獲取指定模塊下的全部類的方法。python

scrapy 源碼分析

scrapy.spiderloader.SpiderLoader 中,能夠發現一個名爲 _load_all_spiders 的方法,經過名稱不難看出,該方法用於讀取全部的爬蟲。再看源碼(爲便於理解,省掉其中部份內容):app

def _load_all_spiders(self):
        for name in self.spider_modules:
            try:
                for module in walk_modules(name):
                    self._load_spiders(module)
            except ImportError:
                ...
        self._check_name_duplicates()

這裏,self.spider_modules 即爲 scrapy 項目中配置文件中的 SPIDER_MODULES 配置項。默認狀況下該配置項指向項目爬蟲中的 spiders 文件夾。scrapy

這裏經過對配置項的遍歷,找到每個爬蟲模塊,再調用 walk_modules 函數獲取模塊下的全部子模塊(包含其自己),而後找到每個模塊中的 spider 類(也就是 self._load_spiders 方法作的事情)。接下來追溯到 walk_modules 查看其代碼:ide

def walk_modules(path):
    """讀取模塊及其下面的全部子模塊並返回
    For example: walk_modules('scrapy.utils')
    """

    mods = []
    mod = import_module(path)
    mods.append(mod)
    if hasattr(mod, '__path__'):
        for _, subpath, ispkg in iter_modules(mod.__path__):
            fullpath = path + '.' + subpath
            if ispkg:
                mods += walk_modules(fullpath)
            else:
                submod = import_module(fullpath)
                mods.append(submod)
    return mods

這裏對於註釋作了簡單的翻譯並省掉一部分可有可無的內容。能夠看到該方法調用了 iter_modules 找出模塊的全部子模塊(若是有),iter_modules 屬於內置模塊 pkgutil 中的方法,該方法返回指定模塊路徑下的全部子模塊信息(不包含其自己)。經過獲取的子模塊信息進行完整的模塊路徑拼接,若是子模塊爲包的話則依次遞歸調用,不然導入該模塊並放入結果中等待返回。函數

最後,再來看看 self._load_spiders 方法具體作了哪些事情:源碼分析

def _load_spiders(self, module):
        for spcls in iter_spider_classes(module):
            self._found[spcls.name].append((module.__name__, spcls.__name__))
            self._spiders[spcls.name] = spcls

這裏主要邏輯被封裝在 iter_spider_classes 函數中,追溯能夠看到其源碼:翻譯

def iter_spider_classes(module):
    """返回一個迭代器,包含指定模塊下全部定義的爬蟲類
    """
    from scrapy.spiders import Spider

    for obj in vars(module).values():
        if inspect.isclass(obj) and \
           issubclass(obj, Spider) and \
           obj.__module__ == module.__name__ and \
           getattr(obj, 'name', None):
            yield obj

一樣,對註釋作了簡單的處理。這裏調用了 inspect.isclass 函數用來判斷對象是否爲類。而後再判斷是否爲 scrapy.Spider 的子類。由此咱們就知道了如何去獲取指定模塊下(包含其子模塊)的全部定義的類了。code

簡單實現

經過對 scrapy 源碼的分析,咱們能夠定義一個方法用來返回指定模塊下的全部指定類:對象

from inspect import isclass

from scrapy.utils.misc import walk_modules


def iter_cls_from_module(path, base_cls=None):
    """迭代返回指定模塊下的全部定義的類,若是指定 base_cls,則僅返回其子類"""
    for mod in walk_modules(path):
        for obj in vars(mod).values():
            if isclass(obj):
                if base_cls is not None:
                    if issubclass(obj, base_cls):
                        yield obj
                else:
                    yield obj

這裏爲了方便就再也不重複造輪子了,使用 scrapy 提供的 walk_modules 方法便可。遞歸

相關文章
相關標籤/搜索