美文网首页
如何获取指定模块下所有的类

如何获取指定模块下所有的类

作者: kingron | 来源:发表于2021-01-29 16:47 被阅读0次

前言

在使用 scrapy 时,运行爬虫仅需要通过 scrapy crawl 爬虫名 就可启动我们写好的爬虫,那么 scrapy 是如何通过名称找到爬虫类的呢?通过分析源码可窥见一二,同时也可从中找出获取指定模块下的所有类的方法。

scrapy 源码分析

scrapy.spiderloader.SpiderLoader 中,可以发现一个名为 _load_all_spiders 的方法,通过名称不难看出,该方法用于读取所有的爬虫。再看源码(为便于理解,省掉其中部分内容):

   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 文件夹。

这里通过对配置项的遍历,找到每一个爬虫模块,再调用 walk_modules 函数获取模块下的所有子模块(包含其本身),然后找到每一个模块中的 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

这里对于注释做了简单的翻译并省掉一部分无关紧要的内容。可以看到该方法调用了 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 的子类。由此我们就知道了如何去获取指定模块下(包含其子模块)的所有定义的类了。

简单实现

通过对 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 方法即可。

相关文章

  • 如何获取指定模块下所有的类

    前言 在使用 scrapy 时,运行爬虫仅需要通过 scrapy crawl 爬虫名 就可启动我们写好的爬虫,那么...

  • 24个解决实际问题的ES6代码段

    1、如何隐藏所有指定元素? 2、如何确认元素是否具有指定的类? 3、如何切换元素的类? 4、如何获取当前页面的滚动...

  • 19-与时间相关的模块

    时间日期相关的模块 日历模块 calendar() 获取指定年份的日历字符串 month() 获取指定年月的日历字...

  • 48.pip 查看已安装模块、卸载指定模块、安装指定版本模块

    场景 今天演示一下如何利用 pip来 查看已安装模块、卸载指定模块、安装指定版本模块 演示 1.查看已安装模块 p...

  • 记好这 24 个 ES6 方法,用来解决实际开发的 JS 问题

    1.如何隐藏所有指定的元素 2.如何检查元素是否具有指定的类? 3.如何切换一个元素的类? 4.如何获取当前页面的...

  • ClassUtil

    功能列表 获取指定 classloader 中已加载的类; 获取指定 classloader 中已加载且带有指定注...

  • 模块依赖分析

    1. 递归打印某个模块的所有类文件 2. 获取某个模块的所有类的全路径名 测试类: 3. 模块依赖分析 (1)获取...

  • 一些好用的代码

    隐藏所有指定的元素 检查元素是否具有指定的类 如何切换一个元素的类? 获取当前页面的滚动位置 平滑滚动到页面顶部 ...

  • 6.5导出:provide

    默认情况下,模块定义都是私有的。provide指定的定义通过require导入可用。 provide只能出现在模块...

  • Javascript字符串详解Miaov

    获取类 •charAt——指定字符 •charCodeAt——指定字符的编码 •fromCharCode——编码转...

网友评论

      本文标题:如何获取指定模块下所有的类

      本文链接:https://www.haomeiwen.com/subject/avzqtltx.html