Python 简单后台项目的脚手架

说明

近期写了一个简单的项目,在后台运行获取网上的期货数据并保存到相应的数据库里。由于之前工作很多这种简单的类似调用接口或攫取数据的项目都是用 Python 来写,因此这次也继续用 Python 写。但是这次更换了几个包,此份文档简单来说明一下。

依赖的包

  • toml: 用户解析配置文件,配置文件用的是 toml 格式。
  • arrow: 用于处理日期相关。
  • loguru: 用于日志处理。
  • requests: 用于 http 请求响应。
  • pandas: 用于数据处理。
  • numpy: 用于数据计算。
  • mysql-connector-python: 用于操作 MySQL 数据库。
  • SQLAlchemy: 用于操作数据库。
  • click: 用于命令行参数解析。

项目结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ tree -L 2
.
├── README.md
├── config
│   └── config.toml.                            # 配置文件
├── log                                         # 日志目录
├── app_demo                                    # 程序目录
│   ├── __init__.py
│   ├── config.py                               # 加载配置信息代码
│   ├── container.py.                           # 容器类
│   ├── exceptions.py                           # 异常类
│   ├── log.py                                  # 日志程序
│   └── main.py                                 # 主程序
├── setup.py                                    # 安装程序
└── venv                                        # 虚拟环境
    ├── bin
    ├── lib
    └── pyvenv.cfg

部分代码示例

1、main.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import time  
import arrow  
import numpy as np  
import click  
import sqlalchemy  
from sqlalchemy import text  
from app_demp.container import Container  
  
config = None  
log = None  
db = None  
  
@click.command()  
@click.option('--config_file', default='./config.toml', help='Specify config file')  
@click.option('--start_date', default=None, help='Specify start date')  
@click.option('--end_date', default=None, help='Specify end date')  
def main(config_file, start_date, end_date):  
    global config, log, db
    print("Load config file: {}".format(config_file))  
    container = Container(config_file)  
    config = container.get_config()  
    log = container.get_logger()  
    log.info("Load config file: {}".format(config_file))  
    db = container.get_db()  
  
    if start_date is None or end_date is None:  
        start_date = arrow.now().format('YYYYMMDD')  
        end_date = start_date  
  
    log.info("Date Info, start date: {}, end date: {}".format(start_date, end_date))  
  
    run(start_date, end_date)  


if __name__ == '__main__':  
    main()

2、container.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  
from sqlalchemy import create_engine  
from sqlalchemy.orm import sessionmaker  
  
  
class Container(object):  
    """  
    容器类  
    """  
    def __init__(self, config_file):  
        """  
  
        """        self.config_file = config_file  
        self.config = None  
        self.logger = None  
        self.db = None  
  
    def get_config(self):  
        """  
        获取指定配置信息  
        :return:  
        """        if self.config is None:  
            from app_demo.config import get_config  
            self.config = get_config(self.config_file)  
        return self.config  
  
    def get_logger(self):  
        """  
        获取日志对象  
        :return:  
        """  
        if self.config is None:  
            self.get_config()  
        if self.logger is None:  
            from app_demo.log import get_logger  
            self.logger = get_logger(self)  
        return self.logger  
  
    def get_db(self):  
        if self.config is None:  
            self.get_config()  
  
        if self.db is None:  
            dsn = "mysql+mysqlconnector://{user}:{password}@{host}:{port}/{database}?auth_plugin=mysql_native_password".format(  
                    user=self.get_config().get('db').get('user'),  
                    password=self.get_config().get('db').get('password'),  
                    host=self.get_config().get('db').get('host'),  
                    port=self.get_config().get('db').get('port'),  
                    database=self.get_config().get('db').get('database'),  
                )  
  
            engine = create_engine(dsn, echo=False)  
            db_session = sessionmaker(bind=engine, autoflush=True, expire_on_commit=True)  
            self.db = db_session()  
  
        return self.db

3、config.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import toml  
  
from app_demo.exceptions import *  
  
  
def get_config(config_file):  
    if config_file is not None:  
        with open(config_file) as config_file_handler:  
            config = toml.loads(config_file_handler.read())  
  
            if not all(key in config for key in ['log']):  
                raise ConfigException('Config error, config file: {file}'.format(file=config_file))  
  
            if not (isinstance(config.get('db'), dict) and all(key in config.get('db') for key in  
                                                               ['host', 'port', 'database', 'user', 'password'])):  
                raise ConfigException('Config error, config file: {file}'.format(file=config_file))  
        return config  
    else:  
        raise Exception('Can not retrieve config file')

4、log.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from loguru import logger  
  
  
def get_logger(container):  
    log_config = container.get_config().get('log')  
  
    logger.add(  
        log_config.get('filename'),  
        rotation=log_config.get('rotation_size'),  
        retention=log_config.get('retention'),  
        compression=log_config.get('compression'),  
    )  
  
    return logger

结论

软件工程方面,软件开发方法学的泰山北斗 Kent Beck 说过一句至理名言: “Make it Work, Make it Right, Make it Fast.",最重要是先 Make it Work。简单点,大家可以参考这个脚手架先把项目做起来,后面再慢慢完善。

0%