如何獲取指定模塊下所有的類

前言

在使用 scrapy 時,運(yùn)行爬蟲僅需要通過 scrapy crawl 爬蟲名 就可啟動我們寫好的爬蟲,那么 scrapy 是如何通過名稱找到爬蟲類的呢?通過分析源碼可窺見一二,同時也可從中找出獲取指定模塊下的所有類的方法。

scrapy 源碼分析

scrapy.spiderloader.SpiderLoader 中,可以發(fā)現(xiàn)一個名為 _load_all_spiders 的方法,通過名稱不難看出,該方法用于讀取所有的爬蟲。再看源碼(為便于理解,省掉其中部分內(nèi)容):

   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 項(xiàng)目中配置文件中的 SPIDER_MODULES 配置項(xiàng)。默認(rèn)情況下該配置項(xiàng)指向項(xiàng)目爬蟲中的 spiders 文件夾。

這里通過對配置項(xiàng)的遍歷,找到每一個爬蟲模塊,再調(diào)用 walk_modules 函數(shù)獲取模塊下的所有子模塊(包含其本身),然后找到每一個模塊中的 spider 類(也就是 self._load_spiders 方法做的事情)。接下來追溯到 walk_modules 查看其代碼:

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

這里對于注釋做了簡單的翻譯并省掉一部分無關(guān)緊要的內(nèi)容??梢钥吹皆摲椒ㄕ{(diào)用了 iter_modules 找出模塊的所有子模塊(如果有),iter_modules 屬于內(nèi)置模塊 pkgutil 中的方法,該方法返回指定模塊路徑下的所有子模塊信息(不包含其本身)。通過獲取的子模塊信息進(jìn)行完整的模塊路徑拼接,如果子模塊為包的話則依次遞歸調(diào)用,否則導(dǎo)入該模塊并放入結(jié)果中等待返回。

最后,再來看看 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 函數(shù)中,追溯可以看到其源碼:

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

同樣,對注釋做了簡單的處理。這里調(diào)用了 inspect.isclass 函數(shù)用來判斷對象是否為類。然后再判斷是否為 scrapy.Spider 的子類。由此我們就知道了如何去獲取指定模塊下(包含其子模塊)的所有定義的類了。

簡單實(shí)現(xiàn)

通過對 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

這里為了方便就不再重復(fù)造輪子了,使用 scrapy 提供的 walk_modules 方法即可。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容