成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Scrapy-實用的命令行工具實現(xiàn)方法

silenceboy / 1181人閱讀

摘要:學(xué)習(xí)點的命令行工具實現(xiàn)了低耦合,需要刪減增加哪個命令行只需要在模塊中修改增刪就可以實現(xiàn)。下一篇將會記錄根據(jù)借鑒命令行工具實現(xiàn)方法來實現(xiàn)自己的命令行

其實這篇文章是scrapy源碼學(xué)習(xí)的(一),加載器那篇才是(二)
scrapy的命令行工具

本文環(huán)境:

wind7 64bits

python 3.7

scrapy 1.5.1

scrapy擁有非常靈活的低耦合的命令行工具,如果自己想要重新實現(xiàn)覆蓋掉scrapy自帶的命令也是可以的。
使用它的命令行工具可以大致分為兩種情況:

在創(chuàng)建的project路徑下

不在project路徑下

先看下不在scrapy項目路徑下的命令行有哪些:

Scrapy 1.5.1 - no active project

Usage:
  scrapy  [options] [args]

Available commands:
  bench         Run quick benchmark test
  fetch         Fetch a URL using the Scrapy downloader
  genspider     Generate new spider using pre-defined templates
  runspider     Run a self-contained spider (without creating a project)
  settings      Get settings values
  shell         Interactive scraping console
  startproject  Create new project
  version       Print Scrapy version
  view          Open URL in browser, as seen by Scrapy

  [ more ]      More commands available when run from project directory

Use "scrapy  -h" to see more info about a command

在項目路徑下的命令行新增了check、crawl、edit、list、parse這些命令,具體:

Scrapy 1.5.1 - project: myspider01

Usage:
  scrapy  [options] [args]

Available commands:
  bench         Run quick benchmark test
  check         Check spider contracts
  crawl         Run a spider
  edit          Edit spider
  fetch         Fetch a URL using the Scrapy downloader
  genspider     Generate new spider using pre-defined templates
  list          List available spiders
  parse         Parse URL (using its spider) and print the results
  runspider     Run a self-contained spider (without creating a project)
  settings      Get settings values
  shell         Interactive scraping console
  startproject  Create new project
  version       Print Scrapy version
  view          Open URL in browser, as seen by Scrapy

Use "scrapy  -h" to see more info about a command

也即是說scrapy可以根據(jù)當(dāng)前路徑是否是scrapy項目路徑來判斷提供可用的命令給用戶。

創(chuàng)建一個scrapy項目

在當(dāng)前路徑下創(chuàng)建一個scrapy項目,DOS下輸入:

scrapy startproject myproject

可以查看剛剛創(chuàng)建的項目myproject的目錄結(jié)構(gòu):

├── scrapy.cfg                         //scrapy項目配置文件
├── myproject
    ├── spiders                     // 爬蟲腳本目錄
        ├── __init__.py   
    ├── __init__.py 
    ├── items.py 
    ├── middlewares.py 
    ├── pipelines.py 
    ├── settings.py                  // 項目設(shè)置
               

可以斷定,在我們使用"startproject"這個scrapy命令時,scrapy會把一些項目默認(rèn)模板拷貝到我們創(chuàng)建項目的路徑下,從而生成我們看到的類似上面的目錄結(jié)構(gòu)。我們可以打開scrapy的包,看看這些模板在哪個地方。切換至scrapy的安裝路徑(比如:..Python37Libsite-packagesscrapy),可以看到路徑下有templates文件夾,而此文件夾下的project文件夾便是創(chuàng)建項目時拷貝的默認(rèn)模板存放目錄。
那么scrapy是怎么實現(xiàn)類似“startproject”這樣的命令的呢?

打開scrapy源碼 找到入口

scrapy是使用命令行來啟動腳本的(當(dāng)然也可以調(diào)用入口函數(shù)來啟動),查看其命令行實現(xiàn)流程必須先找到命令行實行的入口點,這個從其安裝文件setup.py中找到。
打開setup.py 找到entry_points:

...
  entry_points={
        "console_scripts": ["scrapy = scrapy.cmdline:execute"]
    },
...

可以看到scrapy開頭的命令皆由模塊scrapy.cmdline的execute函數(shù)作為入口函數(shù)。

分析入口函數(shù)

先瀏覽一下execute函數(shù)源碼,這里只貼主要部分:

def execute(argv=None, settings=None):
    if argv is None:
        argv = sys.argv

    ...

    #主要部分:獲取當(dāng)前項目的設(shè)置
    if settings is None:
        settings = get_project_settings()
        # set EDITOR from environment if available
        try:
            editor = os.environ["EDITOR"]
        except KeyError: pass
        else:
            settings["EDITOR"] = editor

    #檢查提醒已不被支持的設(shè)置項目
    check_deprecated_settings(settings)

    ...

    #主要部分:判斷是否在項目路徑下,加載可見命令,解析命令參數(shù)
    inproject = inside_project()
    cmds = _get_commands_dict(settings, inproject)
    cmdname = _pop_command_name(argv)
    parser = optparse.OptionParser(formatter=optparse.TitledHelpFormatter(), 
        conflict_handler="resolve")
    if not cmdname:
        _print_commands(settings, inproject)
        sys.exit(0)
    elif cmdname not in cmds:
        _print_unknown_command(settings, cmdname, inproject)
        sys.exit(2)

    cmd = cmds[cmdname]
    parser.usage = "scrapy %s %s" % (cmdname, cmd.syntax())
    parser.description = cmd.long_desc()
    settings.setdict(cmd.default_settings, priority="command")
    cmd.settings = settings
    cmd.add_options(parser)
    opts, args = parser.parse_args(args=argv[1:])
    _run_print_help(parser, cmd.process_options, args, opts)
    cmd.crawler_process = CrawlerProcess(settings)
    _run_print_help(parser, _run_command, cmd, args, opts)
    sys.exit(cmd.exitcode)

閱讀cmdline.py的execute函數(shù),大概了解了命令行實現(xiàn)的基本流程:

1.獲取命令參數(shù)

命令參數(shù)的獲取可以通過兩種方式傳遞:
第一種是調(diào)用execute,比如:

from scrapy.cmdline import execute
execute(argv=["scrapy","startproject","myproject","-a","xxxx"])

這樣就相當(dāng)于第二種方式:命令控制臺執(zhí)行

scrapy startproject myproject -a xxxx

傳遞的參數(shù)都是

["scrapy","startproject","myproject","-a","xxxx"]
2.獲取scrapy項目配置

如果當(dāng)前不是調(diào)用的方式傳遞settings給execute入口,而是一般的命令控制臺啟動scrapy,那么scrapy會在當(dāng)前路徑下搜索加載可能存在的項目配置文件。主要是通過函數(shù)get_project_settings執(zhí)行。

ENVVAR = "SCRAPY_SETTINGS_MODULE"

def get_project_settings():
    #獲取配置
    if ENVVAR not in os.environ:
        #初始化獲取項目的default級配置,即是scrapy生成的默認(rèn)配置
        project = os.environ.get("SCRAPY_PROJECT", "default")
        #初始化項目環(huán)境,設(shè)置系統(tǒng)環(huán)境變量SCRAPY_SETTINGS_MODULE的值為配置模塊路徑
        init_env(project)

    settings = Settings()
    settings_module_path = os.environ.get(ENVVAR)
    if settings_module_path:
        settings.setmodule(settings_module_path, priority="project")

    ...

    return settings

獲取的配置文件主要是scrapy.cfg,我們可以看下他的內(nèi)容:

[settings]
default = myproject.settings
[deploy]
#url = http://localhost:6800/
project = myproject

在生成項目myproject的時候,這個配置文件就已經(jīng)指定了項目設(shè)置模塊的路徑"myproject.settings",所以上面的get_project_settings函數(shù)獲取便是配置文件settings字段中的default鍵值,然后導(dǎo)入該設(shè)置模塊來生成配置。具體實現(xiàn)在init_env函數(shù)中。

def init_env(project="default", set_syspath=True):
    """在當(dāng)前項目路徑下初始化項目環(huán)境. 并且通過配置系統(tǒng)環(huán)境來讓python能夠定位配置模塊
    """
    #在項目路徑下進入命令行,才能準(zhǔn)確獲取配置
    #獲取可能存在scrapy.cfg配置文件的模塊路徑
    cfg = get_config()
    #獲取到配置文件后設(shè)置系統(tǒng)環(huán)境變量SCRAPY_SETTINGS_MODULE為配置模塊路徑,
    #如: myproject.settings,默認(rèn)項目級別均為default,即是配置文件字段settings中的鍵
    if cfg.has_option("settings", project):
        os.environ["SCRAPY_SETTINGS_MODULE"] = cfg.get("settings", project)
    #將最近的scrapy.cfg模塊路徑放入系統(tǒng)路徑使Python能夠找到該模塊導(dǎo)入
    closest = closest_scrapy_cfg()
    if closest:
        projdir = os.path.dirname(closest)
        if set_syspath and projdir not in sys.path:
        #加入項目設(shè)置模塊路徑到系統(tǒng)路徑讓Python能夠定位到
            sys.path.append(projdir)

def get_config(use_closest=True):
    """
    SafeConfigParser.read(filenames)
    嘗試解析文件列表,如果解析成功返回文件列表。如果filenames是string或Unicode string,
    將會按單個文件來解析。如果在filenames中的文件不能打開,該文件將被忽略。這樣設(shè)計的目的是,
    讓你能指定本地有可能是配置文件的列表(例如,當(dāng)前文件夾,用戶的根目錄,及一些全系統(tǒng)目錄),
    所以在列表中存在的配置文件都會被讀取。"""
    sources = get_sources(use_closest)
    cfg = SafeConfigParser()
    cfg.read(sources)
    return cfg

def get_sources(use_closest=True):
    """先獲取用戶的根目錄,及一些全系統(tǒng)目錄下的有scrapy.cfg的路徑加入sources
    最后如果使用最靠近當(dāng)前路徑的scrapy.cfg的標(biāo)志use_closest為True時加入該scrapy.cfg路徑"""
    xdg_config_home = os.environ.get("XDG_CONFIG_HOME") or 
        os.path.expanduser("~/.config")
    sources = ["/etc/scrapy.cfg", r"c:scrapyscrapy.cfg",
               xdg_config_home + "/scrapy.cfg",
               os.path.expanduser("~/.scrapy.cfg")]
    if use_closest:
        sources.append(closest_scrapy_cfg())
    return sources

def closest_scrapy_cfg(path=".", prevpath=None):
    """
    搜索最靠近當(dāng)前當(dāng)前路徑的scrapy.cfg配置文件并返回其路徑。
    搜索會按照當(dāng)前路徑-->父路徑的遞歸方式進行,到達頂層沒有結(jié)果則返回‘’
    """
    if path == prevpath:
        return ""
    path = os.path.abspath(path)
    cfgfile = os.path.join(path, "scrapy.cfg")
    if os.path.exists(cfgfile):
        return cfgfile
    return closest_scrapy_cfg(os.path.dirname(path), path)

通過init_env來設(shè)置os.environ["SCRAPY_SETTINGS_MODULE"]的值,這樣的話

#將項目配置模塊路徑設(shè)置進系統(tǒng)環(huán)境變量
os.environ["SCRAPY_SETTINGS_MODULE"] = "myproject.settings"

初始化后返回到原先的get_project_settings,生成一個設(shè)置類Settings實例,然后再將設(shè)置模塊加載進實例中完成項目配置的獲取這一動作。

3.判斷是否在scrapy項目路徑下

判斷當(dāng)前路徑是否是scrapy項目路徑,其實很簡單,因為前面已經(jīng)初始化過settings,如果在項目路徑下,那么
os.environ["SCRAPY_SETTINGS_MODULE"]的值就已經(jīng)被設(shè)置了,現(xiàn)在只需要判斷這個值是否存在便可以判斷是否在項目路徑下。具體實現(xiàn)在inside_project函數(shù)中實現(xiàn):

def inside_project():
    scrapy_module = os.environ.get("SCRAPY_SETTINGS_MODULE")
    if scrapy_module is not None:
        try:
            import_module(scrapy_module)
        except ImportError as exc:
            warnings.warn("Cannot import scrapy settings module %s: %s" % (scrapy_module, exc))
        else:
            return True
    return bool(closest_scrapy_cfg())
4.獲取命令集合,命令解析

知道了當(dāng)前是否在項目路徑下,還有初始化了項目配置,這個時候就可以獲取到在當(dāng)前路徑下能夠使用的命令行有哪些了。
獲取當(dāng)前可用命令集合比較簡單,直接加載模塊scrapy.commands下的所有命令行類,判斷是否需要在項目路徑下才能使用該命令,是的話直接實例化加入一個字典(格式:<命令名稱>:<命令實例>)返回,具體實現(xiàn)通過_get_commands_dict:

def _get_commands_dict(settings, inproject):
    cmds = _get_commands_from_module("scrapy.commands", inproject)
    cmds.update(_get_commands_from_entry_points(inproject))
    #如果有新的命令行模塊在配置中設(shè)置,會自動載入
    cmds_module = settings["COMMANDS_MODULE"]
    if cmds_module:
        cmds.update(_get_commands_from_module(cmds_module, inproject))
    return cmds

def _get_commands_from_module(module, inproject):
    d = {}
    for cmd in _iter_command_classes(module):
        #判斷是否需要先創(chuàng)建一個項目才能使用該命令,
        #即目前是否位于項目路徑下(inproject)的可用命令有哪些,不是的有哪些
        if inproject or not cmd.requires_project:
            cmdname = cmd.__module__.split(".")[-1]
            #獲取該命令名稱并實例化 加入返回字典
            #返回{<命令名稱>:<命令實例>}
            d[cmdname] = cmd()
    return d 

def _iter_command_classes(module_name):
    #獲取scrapy.commands下所有模塊文件中屬于ScrapyCommand子類的命令行類
    for module in walk_modules(module_name):
        for obj in vars(module).values():
            if inspect.isclass(obj) and 
                    issubclass(obj, ScrapyCommand) and 
                    obj.__module__ == module.__name__ and 
                    not obj == ScrapyCommand:
                yield obj

其中判斷是否是命令類的關(guān)鍵在于該命令模塊中的命令類是否繼承了命令基類ScrapyCommand,只要繼承了該基類就可以被檢測到。這有點類似接口的作用,ScrapyCommand基類其實就是一個標(biāo)識類(該類比較簡單,可以查看基類代碼)。而該基類中有一個requires_project標(biāo)識,標(biāo)識是否需要在scrapy項目路徑下才能使用該命令,判斷該值就可以獲得當(dāng)前可用命令。
獲取到了可用命令集合,接下來會加載Python自帶的命令行解析模塊optparser.OptionParser的命令行參數(shù)解析器,通過實例化獲取該parser,傳入當(dāng)前命令實例的add_options屬性方法中來加載當(dāng)前命令實例附加的解析命令,如:-a xxx, -p xxx, --dir xxx 之類的類似Unix命令行的命令。這些都是通過parser來實現(xiàn)解析。

5.判斷當(dāng)前命令是否可用

其實在加載解析器之前,會去判斷當(dāng)前的用戶輸入命令是否是合法的,是不是可用的,如果可用會接下去解析執(zhí)行該命令,不可用便打印出相關(guān)的幫助提示。比如:

Usage
=====
  scrapy startproject  [project_dir]

Create new project

Options
=======
--help, -h              show this help message and exit

Global Options
--------------
--logfile=FILE          log file. if omitted stderr will be used
--loglevel=LEVEL, -L LEVEL
                        log level (default: DEBUG)
--nolog                 disable logging completely
--profile=FILE          write python cProfile stats to FILE
--pidfile=FILE          write process ID to FILE
--set=NAME=VALUE, -s NAME=VALUE
                        set/override setting (may be repeated)
--pdb                   enable pdb on failure

至此,scrapy命令行工具的實現(xiàn)流程基本結(jié)束。

學(xué)習(xí)點

scrapy的命令行工具實現(xiàn)了低耦合,需要刪減增加哪個命令行只需要在scrapy.commands模塊中修改增刪就可以實現(xiàn)。但是實現(xiàn)的關(guān)鍵在于該模塊下的每一個命令行類都得繼承ScrapyCommand這個基類,這樣在導(dǎo)入的時候才能有所判斷,所以我說ScrapyCommand是個標(biāo)識類。基于標(biāo)識類來實現(xiàn)模塊的低耦合。

下一篇將會記錄根據(jù)借鑒scrapy命令行工具實現(xiàn)方法來實現(xiàn)自己的命令行

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/42650.html

相關(guān)文章

  • 從零開始寫爬蟲

    摘要:幾個朋友對爬蟲很感興趣,他們也都是開發(fā)人員,一個開發(fā)兩個開發(fā),都沒有過項目開發(fā)經(jīng)驗,正好其中一個最近要爬一個網(wǎng)店的產(chǎn)品信息,所以希望我能拿這網(wǎng)站當(dāng)寫一個爬蟲來給他們參考學(xué)習(xí)。我們就在這個文件里開發(fā)爬蟲的相關(guān)邏輯。 幾個朋友對爬蟲很感興趣,他們也都是開發(fā)人員,一個PHP開發(fā)兩個JAVA開發(fā),都沒有過python項目開發(fā)經(jīng)驗,正好其中一個最近要爬一個網(wǎng)店的產(chǎn)品信息,所以希望我能拿這網(wǎng)站當(dāng)d...

    wwq0327 評論0 收藏0
  • 基于 Python Scrapy 爬蟲入門:環(huán)境搭建

    摘要:一基礎(chǔ)環(huán)境由于不是職業(yè)的開發(fā)者,因此環(huán)境是基于的。二安裝打開命令行工具創(chuàng)建虛擬環(huán)境,默認(rèn)情況下會創(chuàng)建目錄,所有的虛擬環(huán)境都會產(chǎn)生一個子目錄保存在此,里面包含基本程序文件以及庫文件。 目錄 基于 Python 的 Scrapy 爬蟲入門:環(huán)境搭建 基于 Python 的 Scrapy 爬蟲入門:頁面提取 基于 Python 的 Scrapy 爬蟲入門:圖片處理 作為一個全棧工程師(...

    Gu_Yan 評論0 收藏0
  • scrapy提升篇之配置

    摘要:提升篇之配置增加并發(fā)并發(fā)是指同時處理的的數(shù)量。其有全局限制和局部每個網(wǎng)站的限制。使用級別來報告這些信息。在進行通用爬取時并不需要,搜索引擎則忽略。禁止能減少使用率及爬蟲在內(nèi)存中記錄的蹤跡,提高性能。 scrapy提升篇之配置 增加并發(fā) 并發(fā)是指同時處理的request的數(shù)量。其有全局限制和局部(每個網(wǎng)站)的限制。Scrapy默認(rèn)的全局并發(fā)限制對同時爬取大量網(wǎng)站的情況并不適用,因此您需要...

    劉永祥 評論0 收藏0
  • 網(wǎng)絡(luò)爬蟲介紹

    摘要:什么是爬蟲網(wǎng)絡(luò)爬蟲也叫網(wǎng)絡(luò)蜘蛛,是一種自動化瀏覽網(wǎng)絡(luò)的程序,或者說是一種網(wǎng)絡(luò)機器人。 什么是爬蟲 網(wǎng)絡(luò)爬蟲也叫網(wǎng)絡(luò)蜘蛛,是一種自動化瀏覽網(wǎng)絡(luò)的程序,或者說是一種網(wǎng)絡(luò)機器人。它們被廣泛用于互聯(lián)網(wǎng)搜索引擎或其他類似網(wǎng)站,以獲取或更新這些網(wǎng)站的內(nèi)容和檢索方式。它們可以自動采集所有其能夠訪問到的頁面內(nèi)容,以供搜索引擎做進一步處理(分檢整理下載的頁面),而使得用戶能更快的檢索到他們需要的信息。簡...

    sf190404 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<