

<!--
 * @Author: Boris
 * @Date: 2020-06-21 22:32:55
 * @Description:
-->

# 命令行工具

命令行工具为**feapder**内置支持的，可方便快速的创建项目、爬虫、item、以及调试请求等，使用方法如下：

## 1.查看支持的命令行

打开命令行窗口，输入feapder

    >feapder

    feapder 1.1.3

    Usage:
      feapder <command> [options] [args]

    Available commands:
      create        create project、feapder、item and so on
      shell         debug response
      zip           zip project

    Use "feapder <command> -h" to see more info about a command


可见feapder支持`create`、`shell`及`zip`三种命令

## 2. feapder create

使用feapder create 可快速创建项目、爬虫、item等，具体支持的命令可输入`feapder create -h` 查看使用帮助

    > feapder create -h
    usage: cmdline.py [-h] [-p] [-s] [-i] [-t] [-init] [-j] [-sj] [-c] [--params] [--setting] [--host] [--port] [--username] [--password] [--db]

    生成器
    
    optional arguments:
      -h, --help        show this help message and exit
      -p , --project    创建项目 如 feapder create -p <project_name>
      -s , --spider     创建爬虫 如 feapder create -s <spider_name>
      -i , --item       创建item 如 feapder create -i <table_name> 支持模糊匹配 如 feapder create -i %table_name%
      -t , --table      根据json创建表 如 feapder create -t <table_name>
      -init             创建__init__.py 如 feapder create -init
      -j, --json        创建json
      -sj, --sort_json  创建有序json
      -c, --cookies     创建cookie
      --params          解析地址中的参数
      --setting         创建全局配置文件feapder create --setting
      --host            mysql 连接地址
      --port            mysql 端口
      --username        mysql 用户名
      --password        mysql 密码
      --db              mysql 数据库名

具体使用方法如下：

### 1. 创建爬虫项目

命令

    feapder create -p <project_name>

示例：

    feapder create -p first-project

生成如下：

![-w354](http://markdown-media.oss-cn-beijing.aliyuncs.com/2021/02/08/16127822246620.jpg)

* items： 文件夹存放与数据库表映射的item
* spiders： 文件夹存放爬虫脚本
* main.py： 运行入口
* setting.py： 爬虫配置文件

若项目比较简单，不需要这个层次结构管理，也可不创建项目，直接创建爬虫

### 2. 创建爬虫

命令

    feapder create -s <spider_name>
    
示例：创建名为first_spider的爬虫

```shell
feapder create -s first_spider

请选择爬虫模板
> AirSpider
  Spider
  TaskSpider
  BatchSpider
``` 
    
输入命令后，可以按上下键选择爬虫模板，如选择 AirSpider爬虫模板，生成first_spider.py, 内容如下：

    import feapder


    class FirstSpider(feapder.AirSpider):
        def start_requests(self):
            yield feapder.Request("https://www.baidu.com")

        def parse(self, request, response):
            print(response)


    if __name__ == "__main__":
        FirstSpider().start()


若在项目下创建，建议先进入到spiders目录下，再创建爬虫

### 3. 创建 item

item为与数据库表的映射，与数据入库的逻辑相关。
在使用此命令前，需在数据库中创建好表，且setting.py中配置好数据库连接地址

命令

    feapder create -i <item_name>
    
输出：

```
请选择Item类型
> Item
  Item 支持字典赋值
  UpdateItem
  UpdateItem 支持字典赋值
```

示例

1. 建表

        CREATE TABLE `spider_data` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
          `title` varchar(255) DEFAULT NULL,
          PRIMARY KEY (`id`)
        ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;


2. 配置setting.py， 连接方式换成自己数据库的

    ![-w799](http://markdown-media.oss-cn-beijing.aliyuncs.com/2021/02/08/16127839359771.jpg)


3. 进入items目录，执行命令

        feapder create -i spider_data

生成如下:

```
from feapder import Item


class SpiderDataItem(Item):
    """
    This class was generated by feapder.
    command: feapder create -i spider_data.
    """

    def __init__(self, *args, **kwargs):
        # self.id = None
        self.title = None

```

若字段有默认值或者自增，则默认注释掉，可按需打开

若不配置setting.py, 可在命令行中指定数据库连接信息

     feapder create -i spider_data --host localhost --db feapder --username feapder --password feapder123

也可在环境变量中配置数据库连接信息, 以mac电脑为例

    > vim ~/.bash_profile

    export MYSQL_IP='xxx'
    export MYSQL_PORT='xxx'
    export MYSQL_DB='xxx'
    export MYSQL_USER_NAME='xxx'
    export MYSQL_USER_PASS='xxx'

    > source ~/.bash_profile

这样，以后所有的项目setting.py中均可不配置mysql连接信息

**若item字段过多，不想逐一赋值，可选择支持字典赋值的Item类型创建**

![](http://markdown-media.oss-cn-beijing.aliyuncs.com/2022/09/09/16626945562298.jpg)

生成：

```
from feapder import Item


class SpiderDataItem(Item):
    """
    This class was generated by feapder.
    command: feapder create -i spider_data 1.
    """

    def __init__(self, *args, **kwargs):
        # self.id = kwargs.get('id')
        self.title = kwargs.get('title')
```

这样当我们请求回来的json数据时，可直接赋值，如

```
response_data = {"title":" 测试"} # 模拟请求回来的数据
item = SpiderDataItem(**response_data)
```


### 4. 创建json或有序json

此命令和快速将 `xxx:xxx` 这种字符串格式转为json格式，常用于将网页或者抓包工具抓取出来的header、cookie转为json

用法示例：

1. 输入命令，回车

        > feapder create -j
        请输入需要转换的内容： （xxx:xxx格式，支持多行）


1. copy 请求头，粘贴到提示下方

    ![](http://markdown-media.oss-cn-beijing.aliyuncs.com/2021/02/08/16127849396722.jpg)

1. 输出如下：

    ![-w1394](http://markdown-media.oss-cn-beijing.aliyuncs.com/2021/02/08/16127850065269.jpg)

**sort_json** 与json命令类似，只不过该命令生成的json是按照key排序的有序字典， 命令为

    feapder create -sj

### 5. 根据json创建表

有时，我们需要解析的字段特别多，手动建表太麻烦，此命令可根据json内容的key创建表，根据value推断字段类型

运行前，请参考**创建item**过程，将数据库配置好

命令：

    feapder create -t <table_name>

用的示例：

    > feapder create -t test

    请输入表数据 json格式 如 {"name":"张三"}
    等待输入：

     {"name":"张三"}

    请设置注释 回车跳过
    name : varchar(255)  -> comment：

    是否添加batch_date 字段 （y/n）:n

    请设置唯一索引, 多个逗号间隔
    等待输入：
    id

                CREATE TABLE `feapder`.`test` (
                    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id 自动递增',
                    `name` varchar(255) COMMENT '',

                    `gtime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '抓取时间',
                    PRIMARY KEY (`id`),
                    UNIQUE `idx` USING BTREE (`id`) comment ''
                ) COMMENT='';


    test 创建成功

创建过程会提示添加字段注释、唯一索引等。batch_date用于记录采集批次的，不用可输入n。看到创建成功后，我们去数据库里就可以看到表了

### 6. 创建init

该命令用于创建\__init__.py的，并自动会引入当前目录下的所有py文件到\__all__中

示例如下：

    feapder create -init

观察生成的\__init__.py文件，已自动包含当前目录下的py文件
![-w880](http://markdown-media.oss-cn-beijing.aliyuncs.com/2021/02/08/16127859798201.jpg)


## 3. feapder shell

下载调试器，可方便测试抽取规则是否正确，输入`feapder shell -h`查看使用帮助

    > feapder shell -h

    下载调试器

    usage: feapder shell [options] [args]

    optional arguments:
      -u, --url     抓取指定url
      -c, --curl    抓取curl格式的请求

由提示可以看出，支持url及curl两种方式

1. 以url为例，请求百度：

    请求

        > feapder shell -u https://www.baidu.com
        MainThread|2020-06-21 23:21:37,208|request.py|get_response|line:283|DEBUG|
                        -------------- None.parser request for ----------------
                        url  = https://www.baidu.com
                        method = GET
                        body = {'proxies': None, 'timeout': 22, 'stream': True, 'verify': False, 'headers': {'User-Agent': 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1866.237 Safari/537.36'}}

        <Response [200]>
        Python 3.6.3 (v3.6.3:2c5fed86e0, Oct  3 2017, 00:32:08)
        Type 'copyright', 'credits' or 'license' for more information
        IPython 7.14.0 -- An enhanced Interactive Python. Type '?' for help.


        now you can use response

        In [1]:

    测试抽取title的xpath是否正确

        In [1]: response.xpath('//title/text()').extract_first()
        Out[1]: '百度一下，你就知道'

    可以看出，我们可以直接使用response， response支持xpath表达式

    若想查看都支持哪些函数，可输入`response.` 然后敲两次`tab键`，如下：
    ![-w1738](http://markdown-media.oss-cn-beijing.aliyuncs.com/2020/06/21/15927532396490.jpg)

1. 以curl为例，请求百度（通常用来测试post接口比较方便）

    1. 打开浏览器检查工具，复制需要测试的接口为curl格式
    ![-w569](http://markdown-media.oss-cn-beijing.aliyuncs.com/2020/06/21/15927533333272.jpg)

    1. 测试 输入`feapder shell --`, 粘贴刚刚复制的curl

            > feapder shell --curl 'https://www.baidu.com/' \
              -H 'Connection: keep-alive' \
              -H 'Pragma: no-cache' \
              -H 'Cache-Control: no-cache' \
              -H 'Upgrade-Insecure-Requests: 1' \
              -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36' \
              -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' \
              -H 'Sec-Fetch-Site: none' \
              -H 'Sec-Fetch-Mode: navigate' \
              -H 'Sec-Fetch-User: ?1' \
              -H 'Sec-Fetch-Dest: document' \
              -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8' \
              -H 'Cookie: PSTM=1589621705; BAIDUID=BC3B63E7833EB3D77970A2CF9AE7F4A2:FG=1; BIDUPSID=016405978E2364A4AFD0227A36DA60DE; BD_UPN=123253; BDUSS=nl2a3EzNGp-aGdham5xanhVYjZLbXNWTENBRWl0VTJuLTVEUGJOa2VodjhGTzVlRVFBQUFBJCQAAAAAAAAAAAEAAACCgXjpQm9yaXMwNjIxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPyHxl78h8ZeW; delPer=0; BD_CK_SAM=1; BD_HOME=1; BDRCVFR[feWj1Vr5u3D]=I67x6TjHwwYf0; PSINO=1; ZD_ENTRY=google; H_PS_PSSID=1442_31672_21102_32046_31321_30823_32107_26350; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm' \
              --compressed
            MainThread|2020-06-21 23:29:48,773|request.py|get_response|line:283|DEBUG|
                            -------------- None.parser request for ----------------
                            url  = https://www.baidu.com/
                            method = POST
                            body = {'data': {}, 'headers': {'Connection': 'keep-alive', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-User': '?1', 'Sec-Fetch-Dest': 'document', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Cookie': 'PSTM=1589621705; BAIDUID=BC3B63E7833EB3D77970A2CF9AE7F4A2:FG=1; BIDUPSID=016405978E2364A4AFD0227A36DA60DE; BD_UPN=123253; BDUSS=nl2a3EzNGp-aGdham5xanhVYjZLbXNWTENBRWl0VTJuLTVEUGJOa2VodjhGTzVlRVFBQUFBJCQAAAAAAAAAAAEAAACCgXjpQm9yaXMwNjIxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPyHxl78h8ZeW; delPer=0; BD_CK_SAM=1; BD_HOME=1; BDRCVFR[feWj1Vr5u3D]=I67x6TjHwwYf0; PSINO=1; ZD_ENTRY=google; H_PS_PSSID=1442_31672_21102_32046_31321_30823_32107_26350; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm'}, 'proxies': None, 'timeout': 22, 'stream': True, 'verify': False}

            <Response [200]>
            Python 3.6.3 (v3.6.3:2c5fed86e0, Oct  3 2017, 00:32:08)
            Type 'copyright', 'credits' or 'license' for more information
            IPython 7.14.0 -- An enhanced Interactive Python. Type '?' for help.
            now you can use response

            In [1]:

