V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
soraping
V2EX  ›  Python

使用 Python 开发了一个能在命令行中查看基金涨幅的摸鱼工具

  •  
  •   soraping · 270 天前 · 1403 次点击
    这是一个创建于 270 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一朋友在上班期间看手机基金信息,但上班期间频繁看手机着实不太好,于是就想到写一个能在命令行查看 fund 估值的命令行工具,通过输入 fund 编号和名称,就能查询到当前时间的估值。 网络上有很多这样的摸鱼插件,甚至在 vscode 里的插件比比皆是。 本着练手的态度,自己也写了一个简单的工具,就当做学习新的库了。

    使用预览

    安装工具:

    pip install fundgz
    

    使用:

    > fundgz
    
    Usage: fundgz [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --help  Show this message and exit.
    
    Commands:
      add
      delete
      run
    

    命令行输入 fundgz 命令,可以查看这个命令的简介,可以看到提供了三个子命令:

    > fundgz add
    
    正在添加 fund 文档,ctrl+d 退出
    请输入 fund 编号: 078943
    请输入 fund 名称: test
    

    输入 fundgz add 命令,终端会提示输入 fund 编号和名称,输入完成后,可以按 ctrl+d 退出。

    fundgz delete 命令删除录入文档的编号

    fundgz run 命令就是根据之前存入的文档执行查询了:

    在终端会展示结果表格

    fund.png

    开发思路

    • 在终端录入 fund code 并保存至临时文件中
    • 查询临时文件,提取其中的 fund code 列表
    • 通过 code 列表,批量查询 fund 当日的估值信息
    • 在终端列表展示

    技术栈

    python3.7 版本

    数据抓取 - asyncio + aiohttp

    命令行工具 - click + inquirer

    终端展示 - rich

    打包发布 - setuptools

    click + inquirer 终端工具

    使用 click.group() 创建命令组,实现 add, deleterun 命令

    @click.group()
    def fc():
        pass
    
    @fc.command()
    def run():
        pass
    
    @fc.command()
    def add():
        pass
        
    @fc.command()
    def delete():
        pass
        
    fc()
    

    @fc.command() 是为了替代 fc.add_command(run) ,这样写更加优雅

    @click.command()
    def run():
        pass
    
    fc.add_command(run)
    

    inquirer 库是交互式工具,提供了很多便利的操作,这次使用的是 list 选择。

    执行 fundgz delete 选择要删除的 code

    fund-del.png

    del_list = [
                    inquirer.List('del',
                                  message="请选择要删除的 fund:",
                                  choices=codes)
                ]
                del_answers = inquirer.prompt(del_list)
    

    asyncio + aiohttp 批量数据查询

    读取临时文件后,提取出正在需要的 code 列表,通过这个列表批量查询数据

    async with aiohttp.ClientSession() as session:
        tasks = [asyncio.create_task(query_data(session, code)) for code in codes]
        data = await asyncio.gather(*tasks)
        print(data)
    

    如果没有查询到数据,则需要做好异常处理,则请求非 200 的则直接返回 None:

    async def query_data(session, fund_code):
        """
        :param fund_code:
        :return:
        """
        url = BASE_URL.format(fund_code)
        async with session.get(url=url) as response:
            if response.status == 200:
                bytes_data = await response.read()
                # 查看编码类型
                ret = chardet.detect(bytes_data)
                # bytes 转换成 str
                jsonp_data = str(bytes_data, ret['encoding'])
                # 判断是否有数据
                data = re.match('.*?({.*}).*', jsonp_data, re.S).group(1)
                return json.loads(data)
            else:
                return None
    

    如此,批量返回的数据中就会有 None,所以要 filter

    data = filter(lambda fund: fund, data)
    

    setuptools 打包

    命令行工具已经可以了,使用

    python fundgz/main.py run
    

    但是期望的是

    fundgz run
    

    所以,就要将这个工具发布到服务上。

    首先在 https://pypi.org/ 上注册一个账号,认证邮箱等

    其次,安装 setuptools,更新至最新版:

    python -m pip install --upgrade pip setuptools wheel
    

    具体可以查看官网 https://pypi.org/project/setuptools/

    接下来就是打包的事儿了。

    在项目根目录下新建两个文件 setup.pysetup.cfg

    此时项目的结构是

    ├── README.md
    ├── fund-code.txt
    ├── fundgz
    │   ├── __init__.py
    │   └── main.py
    ├── setup.cfg
    └── setup.py
    

    setup.py 文件内,填写打包的必要信息:

    #!usr/bin/env python
    # -*- coding:utf-8 _*-
    
    from setuptools import setup, find_packages
    
    VERSION = '0.1.7'
    
    setup(
        name='fundgz',
        version=VERSION,
        description='fund search',
        long_description='fund search',
        keywords='fund asynico click rich inquirer',
        author='caoshiping',
        author_email='[email protected]',
        url='https://github.com/soraping/fc',
        license='MIT',
        packages=find_packages(exclude=["*.txt"]),
        include_package_data=True,
        zip_safe=True,
        install_requires=[
            'aiohttp',
            'inquirer',
            'click',
            'rich'
        ],
        python_requires='>=3.7',
        entry_points={
            'console_scripts': [
                'fundgz = fundgz.main:main'
            ]
        }
    )
    

    关键字段详解:

    • name

    上传包的名称

    • version

    开发库版本

    • packages

    打包的内容目录,使用 find_packages 方法,使用参数 exclude 屏蔽一些文件

    • install_requires

    所依赖的库列表

    • entry_points

    自定义命令,不用 python xxxx.py 这样执行的,就要配置这个字段。这个字段不仅仅这个功能,当前包通过 setuptools 注册的一个外部可以直接调用的接口。

        entry_points={
            'console_scripts': [
                'fundgz = fundgz.main:main'
            ]
        }
    

    在终端输入:

    $ fundgz --help
    usage: fundgz...
    

    console_scripts 是自定义命令行列表

    fundgz = fundgz.main:mainfundgz 是命令名称,等号右边的是主方法路径,其中 fundgz.main 包根目录下执行文件的路径,最后是执行文件的方法名

    setup.py 文件配置完成后就可以执行打包操作了

    1. 在终端执行命令:
    # 使用 sdist 构建源码分发包
    python setup.py sdist bdist_wheel
    

    这里是固定的命令,且在 setup.py 文件相同的目录下执行,当这个命令运行结束后,确保在生成的 dist/ 文件夹下存在相应的 .whl 文件和 .tar.gz 文件。其中 .tar.gz 文件是我们 的 python package 的源文件文档。

    .whl 是一个软件分发包 (build distribution) 。新版本的 pip 将会首先尝试安装软件分发包,但在失败情况下会接着尝试采用源文件包安装。

    执行打包后,打开 dist 文件夹,可以发现有 setup.py 配置的 nameversion 拼接的包文件,这些就是待上传的包

    1. 上传包

    需要安装上传工具 twine

    pip install --user --upgrade twine
    
    # 上传
    twine upload dist/*
    

    上传的时候,会提示输入 pypi 账号密码。

    也可以指定版本号上传

    twine upload dist/fundgz-0.1.7*
    

    到此,一个包就算上传成功了,可以下载下来试下

    > pip install fundgz --upgrade
    
    > fundgz --help
    

    项目地址

    https://github.com/soraping/fc

    参考文章

    • click

    https://blog.csdn.net/weixin_38278993/article/details/100052961

    https://www.cnblogs.com/alexkn/p/6980400.html

    • setuptools

    https://www.cnblogs.com/Zzbj/p/11535625.html

    https://blog.csdn.net/u011519550/article/details/105253075/

    https://blog.csdn.net/yinshuilan/article/details/93469900

    https://www.jb51.net/article/178728.htm

    https://www.jianshu.com/p/eb27d5cb5e1d

    9 条回复    2021-03-05 13:17:24 +08:00
    LeeReamond
        1
    LeeReamond   270 天前 via Android
    查看基金涨幅 ×
    查看基金跌幅 ✓
    soraping
        2
    soraping   270 天前
    @LeeReamond
    低情商:基金又跌了
    高情商:基金在技术性调整
    jmyz0455
        3
    jmyz0455   270 天前
    有点长,回家再细看。
    请问基金接口从哪抓的,安全吗?
    soraping
        4
    soraping   270 天前
    @jmyz0455
    天天基金,里面有一个 jsonp 的接口,直接用
    shuianfendi6
        5
    shuianfendi6   270 天前   ❤️ 1
    这个包使用成本太高,看一次钱就少一点
    so1n
        6
    so1n   270 天前
    查看的手续费太高了 T_T
    carity
        7
    carity   270 天前
    要看他干嘛,钱越看越少 T_T

    我这也有个看股票行情的
    https://stock.zyqcn.cn/StockInfo/sh600519
    fhsan
        8
    fhsan   270 天前
    低情商:怎么又跌了,天天跌
    高情商:查询一次扣一次手续费
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2708 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 39ms · UTC 12:49 · PVG 20:49 · LAX 04:49 · JFK 07:49
    ♥ Do have faith in what you're doing.