国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Scrapy-實用的命令行工具實現方法

silenceboy / 1169人閱讀

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

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

本文環境:

wind7 64bits

python 3.7

scrapy 1.5.1

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

在創建的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可以根據當前路徑是否是scrapy項目路徑來判斷提供可用的命令給用戶。

創建一個scrapy項目

在當前路徑下創建一個scrapy項目,DOS下輸入:

scrapy startproject myproject

可以查看剛剛創建的項目myproject的目錄結構:

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

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

打開scrapy源碼 找到入口

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

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

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

分析入口函數

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

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

    ...

    #主要部分:獲取當前項目的設置
    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

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

    ...

    #主要部分:判斷是否在項目路徑下,加載可見命令,解析命令參數
    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函數,大概了解了命令行實現的基本流程:

1.獲取命令參數

命令參數的獲取可以通過兩種方式傳遞:
第一種是調用execute,比如:

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

這樣就相當于第二種方式:命令控制臺執行

scrapy startproject myproject -a xxxx

傳遞的參數都是

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

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

ENVVAR = "SCRAPY_SETTINGS_MODULE"

def get_project_settings():
    #獲取配置
    if ENVVAR not in os.environ:
        #初始化獲取項目的default級配置,即是scrapy生成的默認配置
        project = os.environ.get("SCRAPY_PROJECT", "default")
        #初始化項目環境,設置系統環境變量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,我們可以看下他的內容:

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

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

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

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

def get_sources(use_closest=True):
    """先獲取用戶的根目錄,及一些全系統目錄下的有scrapy.cfg的路徑加入sources
    最后如果使用最靠近當前路徑的scrapy.cfg的標志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):
    """
    搜索最靠近當前當前路徑的scrapy.cfg配置文件并返回其路徑。
    搜索會按照當前路徑-->父路徑的遞歸方式進行,到達頂層沒有結果則返回‘’
    """
    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來設置os.environ["SCRAPY_SETTINGS_MODULE"]的值,這樣的話

#將項目配置模塊路徑設置進系統環境變量
os.environ["SCRAPY_SETTINGS_MODULE"] = "myproject.settings"

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

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

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

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.獲取命令集合,命令解析

知道了當前是否在項目路徑下,還有初始化了項目配置,這個時候就可以獲取到在當前路徑下能夠使用的命令行有哪些了。
獲取當前可用命令集合比較簡單,直接加載模塊scrapy.commands下的所有命令行類,判斷是否需要在項目路徑下才能使用該命令,是的話直接實例化加入一個字典(格式:<命令名稱>:<命令實例>)返回,具體實現通過_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))
    #如果有新的命令行模塊在配置中設置,會自動載入
    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):
        #判斷是否需要先創建一個項目才能使用該命令,
        #即目前是否位于項目路徑下(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

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

5.判斷當前命令是否可用

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

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命令行工具的實現流程基本結束。

學習點

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

下一篇將會記錄根據借鑒scrapy命令行工具實現方法來實現自己的命令行

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/42650.html

相關文章

  • 從零開始寫爬蟲

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

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

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

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

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

    劉永祥 評論0 收藏0
  • 網絡爬蟲介紹

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

    sf190404 評論0 收藏0

發表評論

0條評論

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