Python|fastapi输出定制(四)

随着fastapi学习的深入,渐渐的不满足返回json这种格式,想要url重定向怎么办?想输出纯文本怎么办?想直接返回一个文件怎么办?这节来回答这些问题
直接查看fastapi.responses的源码,我们看到以下定义好的返回类型

from starlette.responses import FileResponse  # noqa
from starlette.responses import HTMLResponse  # noqa
from starlette.responses import JSONResponse  # noqa
from starlette.responses import PlainTextResponse  # noqa
from starlette.responses import RedirectResponse  # noqa
from starlette.responses import Response  # noqa
from starlette.responses import StreamingResponse  # noqa
from starlette.responses import UJSONResponse  # noqa


下面,开始我们的表演

URL重定向

用nginx来搞的话,一般配置301或者302,不过fastapi返回HTTP重定向,默认状态码为307(临时重定向)。

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()

@app.get("/blog")
go_to_blog():
    return RedirectResponse("https://blog.cnrainbird.com")

curl查看

curl -I -XGET http://0.0.0.0:8001/blog
HTTP/1.1 307 Temporary Redirect
date: Sat, 17 Oct 2020 07:52:01 GMT
server: uvicorn
location: https://blog.cnrainbird.com
transfer-encoding: chunked

输出纯文本

这次用到的是PlainTextResponse,直接打印个Hello world吧

from fastapi import FastAPI
from fastapi.responses import PlainTextResponse

app = FastAPI()

@app.get("/", response_class=PlainTextResponse)
def main():
    return "Hello World"

输出文件

这次选手叫:FileResponse异步流式输出一个文件。
不同于其他返回,FileResponse可以传入更多参数:

path       - 文件路径
headers    - 定制头信息,字典格式
media_type - media type,如果没有设置则会根据文件名或文件路径来推断media type
filename   - 文件名。如果设置,会被包含到response的Content-Disposition中

文件response会包含合适的Content-Length, Last-Modified 以及 ETag 头信息内容。

示例

from fastapi import FastAPI
from fastapi.responses import FileResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()

@app.get("/")
def main():
    return FileResponse(some_file_path)

定制默认类

比如,我们不想每次return时都再写类型,想直接全局定义返回文本怎么呢?

from fastapi import FastAPI
from fastapi.responses import PlainTextResponse

app = FastAPI(default_response_class=PlainTextResponse)

@app.get("/")
def main():
    return "Hello World"

结语

还有一些其它的类型,大同小异,不一一演示了.弄了一个微信群欢迎正常学习fastapi的朋友扫码加入.
fastapi

转载请注明: 转自Rainbird的个人博客
   本文链接: Python|fastapi输出定制(四)

Posted in Python | Tagged , , , , , , , , | Leave a comment

修身|2020年十一辟谷(断食)记录

这应该是2020年最后的疯狂了.
前面两篇辟谷总结也说.这个过程最重要的是保暖,五一开始是个合适的时间,十一转冷应该是最后上车的机会
先上图再说收获

时间确切的说是1号到5号中午,体重从开始的141.6降到了134.0,降幅7.6斤.表格是1号做好的,计划八天假期拿出七天来进行这个事儿,但是到5号的时候发现身体素质的得分已经到了95分.假期过半,目标达成,于是想着就此打住,多拿出一些时间来陪家人.
对于这次体重的下调还是很满意的.要知道67公斤,上一次这个体重的时候还是2008的青龙湖龙舟赛的时候,之所以印象深刻是因为那会儿小身板太轻,划龙舟的时候都不太容易找到另一边体重相当的小伙伴.
关于得分.这一项在小米运动中叫身体指数 得分.它是结合身高,体脂,水分,体内肌肉等多维度的数据,进行综合分析后,得出的一个参考性分数.可以帮助了解自身健康状况.
之前称体重的时候也一直有看到这个数据,只是没太当一回事儿,只是有一次得到过90分,然后就再也没有过了.这次顺道把这个统计上了.可以看到,分数一直在朝好的方向发展.
没有对比就没有伤害,这样,拿出五一时的数据,看一下

  • 从体重上看
    五一花了五天时间,减掉7.1斤.十一花了四天半,减掉7.6斤,基本上说可以是完胜.
    五一的结束点是145.1,十一的起点是141.6.半年的时间体重下降3.5斤.说明了一个很关键的问题:半年没有反弹.感谢这半年努力的自己!!!

  • 身体指数
    五一的指数从60到74,十一的指数从78到95.看起来五一减的还是有些匆忙,可以说是时间短任务重.因为时间的关系见好就收了,而十一的情况是时间还好,目标已然达成.

  • 个人感受
    努力,只为遇见最好的自己!
    4号,5号都开始6点自然醒了.除了假期已经休息了几天没有压力外,感觉身体肌能达到了健康状态,尤其6号去爬了趟山,感觉轻轻松松,毫无压力.用我媳妇儿的话:就这架式,看不出来是五天没吃饭的人.

北漂15年,一直低头向前冲,向前冲;想飞得高,想飞得更高;想跑的快,想跑的更快;终于冷不丁的体重恢复到了12年前,不由得让我思考:打拼可以,保重身体.如今有了这个好的契机,接下来会在标准的体重下,重新起飞

立个flag: 2021年1月8号,元旦时,再回顾一次体重.

Posted in 原创 | Tagged , , , , , , , | Leave a comment

Python|fastapi之隐藏docs(安全初探)(三)

fastapi开发api确实fast.东西开发完了,前后端调试也方便,要上线了,怎么保证接口文档不被非法访问呢?简单想了几个场景

内网部署

将swagger的入口监听在内网,域名访问的话,也直接解析成内网,外网不能访问也是个不错的选择.

直接关闭

这招算是釜底抽薪了,直接关门,咱都不访问.算是最简单有效的办法.
代码

app = FastAPI(
    docs_url=None,
    redoc_url=None
)

动态开关

默认关闭,确实有需要查看,临时打开,用完关闭,也是个不错的选择.
原理就是:访问/docs时,默认没有,通过另一个接口临时调整一个参数,这时才允许访问.
主要代码

app = FastAPI(
    docs_url=None,
    redoc_url=None
)
@app.get('/add_docs')
def show_docs_by_manager():
    logger.info(app.docs_url)
    msg = 'ok'
    if app.docs_url is None:
        app.docs_url = '/docs'
        msg = 'docs is open !'
    else:
        app.docs_url = None
        msg = 'docs is disabled !'
    logger.info(app.docs_url)
    return response_struct(msg=msg)
@app.get("/docs")
async def get_documentation():
    if app.docs_url is None:
        return response_struct()
    else:
        return get_swagger_ui_html(openapi_url="/openapi.json", title="docs")

如上,需要访问时,直接/add_docs接口,也不添加参数了,访问一次打开,再访问一次关闭,这样就实现了动态开关的目的.

开启basic auth

出发点就是访问/docs时进行一次用户名和密码认证,用nginx的话很容易实现
示例代码

echo "username:$(openssl passwd -crypt 'password')" >> /home/.htpasswd

location /docs {
    auth_basic  "请输入密码";
    auth_basic_user_file /etc/apache2/.htpasswd; 
}

既然nginx能实现,我牛掰fastapi自然也可以.
代码

from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from starlette.status import HTTP_401_UNAUTHORIZED
from fastapi.openapi.docs import get_swagger_ui_html
app = FastAPI(
    docs_url=None
)

security = HTTPBasic()

@app.get("/docs",include_in_schema=False)
async def get_documentation(credentials: HTTPBasicCredentials = Depends(security)):
    if credentials.username != "rainbird" or credentials.password != "pwd":
        raise HTTPException(
            status_code=HTTP_401_UNAUTHORIZED,
            detail="Incorrect email or password",
            headers={"WWW-Authenticate": "Basic"},
        )
    else:
        return get_swagger_ui_html(openapi_url="/openapi.json", title="docs")


如上,访问/docs需要验证,用户名是rainbird,密码是pwd

结尾

本文只是简单的示范了swagger的页面安全问题.对于后端接口,内部访问的话最好就是直接放内网,也不需要加密和认证,性能会好一些.如果接口一定要放公网的话除了考虑好swagger的安全,还要处理好接口的访问权限,切莫要门户大开,锁都不加.

fastapi

Posted in Nginx, Python | Tagged , , , , , , , | Leave a comment

Python|发送电子邮件(含附件)

电子邮件自诞生到现在,依旧是重要的通讯工具.在日常工作大量的告警,自动化报表依旧是通过邮件来完成.以前一直是只发送html正文,前两天遇到了发附件的情况,顺道解决了邮件名乱码的问题,记录一下

正常发送邮件

电子邮件到今天这个时间点,处理垃圾邮件的管控,很多云服务商和电子邮件服务商已经不再支持smtp通过25端口来发送,而要使用ssl加密的465端口
本文演示基本腾讯企业邮箱,估计QQ个人邮箱也一样.
Python:3.8.2

#!/usr/local/bin/python3.8.2
# -*- coding: utf-8 -*-
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

mail_host = 'smtp.exmail.qq.com'
mail_user = 'xxx@xxx.xx'
mail_pass = 'xxxxxx'
mail_from = 'rainbird'
mail_to   = 'xxx@xxx.xx'
mail_title= "rainbird's mac book"
me = mail_from +"<"+mail_user+">"

mail_body = '/result_report.html'
msg = MIMEText(mail_body,_subtype='html',_charset='utf8')
msg['Subject']  = mail_title
msg['From']     = me
msg['To']       = mail_to
try:
    s = smtplib.SMTP_SSL(host=mail_host)
    s.connect(mail_host)
    s.login(mail_user,mail_pass)
    s.sendmail(me, mail_to, msg.as_string())
    s.close()
    print('send mail success!')
except Exception as e:
    print(e)

发送附件

这部分比较困难的部分就是邮件名乱码.经过尝试指定邮件名UTF8编码,就可以了.

#!/usr/local/bin/python3.8.2
# -*- coding: utf-8 -*-
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

mail_host = 'smtp.exmail.qq.com'
mail_user = 'xxx@xxx.xx'
mail_pass = 'xxxxxx'
mail_from = 'rainbird'
mail_to   = 'xxx@xxx.xx'
mail_title= "rainbird's mac book"
me = mail_from +"<"+mail_user+">"

def file_get_content(file_name):
    with open (file_name,'r') as f:
        return f.read()

mail_body = '/result_report.html'
mail_att  = '/result.html'
msg = MIMEMultipart()
msg['Subject']  = mail_title
msg['From']     = me
msg['To']       = mail_to
msg.attach(MIMEText(file_get_content(mail_body),_subtype='html',_charset='utf8'))
# 邮件附件
att = MIMEText(file_get_content(mail_att), 'base64', 'utf-8')
att.add_header('Content-Disposition', 'attachment', filename=('utf-8', '', 'report.html'))
msg.attach(att)
try:
    s = smtplib.SMTP_SSL(host=mail_host,port=465)
    s.connect(mail_host)
    s.login(mail_user,mail_pass)
    s.sendmail(me, mail_to, msg.as_string())
    s.close()
    print('send mail success!')
except Exception as e:
    print(e)

结语

发邮件并不是什么困难的事儿,只是邮件涉及一堆参数,主机地址,用户名,密码啥的,把这些东西放在配置项里是好习惯.

Posted in Python | Tagged , , , , , | Leave a comment

Python|fastapi从入门到不放弃(二)

之前写了篇fastapi的简介[Python|介绍一下我的新伙伴fastapi(一)],今天直接进入干货阶段,将最近一段时间对fastapi的探索整理下来,也算是总结回顾.

基础配置

修改标题,描述,版本号
代码

app = FastAPI(
    ttitle="雨鸟飞行fastapi示例",
    description="演示fastapi常用的一些配置",
    version="0.1.0",
)

效果

修改swagger路径

默认/dcos访问swagger可以修改别的,比如/documents
代码

app = FastAPI(docs_url="/documents")

效果
访问/docs返回not found

{
detail: "Not Found"
}

隐藏redoc

fastapi默认自带两种接口显示方式:swagger+redoc,对于我来说swagger已经足够用了,没必要显示redoc直接干掉,也能避免一些安全隐患
代码

app = FastAPI(redoc_url=None)

效果
访问/redoc返回not found

{
detail: "Not Found"
}

### 接口分组
默认所有的接口都是放在default下显示,我们可以通过增加tag的方式,将不同类型的接口分组管理,核心在接口后添加tags=['分组名']
默认效果
代码

@app.get("/items/{item_id}",tags=["items"])
async def read_item(item_id: int, q: Optional[str] = None):
    return {"item_id": item_id, "q": q}
@app.put("/items/{item_id}",tags=["items"])
def update_item(item_id: int, item: Item):
    return {"item_name": item.name, "item_id": item_id}

效果

增加自定义提示和链接

其实就是自定义一些tag,如果没有接口关联,就直接显示在那里了
代码

tags_metadata = [
    {
        "name": "介绍",
        "description": "雨鸟飞行的博客,显示fastapi的基本使用",
        "externalDocs": {
            "description": "博客地址",
            "url": "https://blog.cnrainbird.com",
        },
    }
]

app = FastAPI(openapi_tags=tags_metadata)

效果

中间件获取执行时间

log打印每个请求花费的时间,方便排查问题
代码

@app.middleware("http")
async def log_middle(request: Request, call_next):
    start_time = time.time()
    logger.debug(f"{request.method} {request.url}")
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    logger.info(f"{request.method} {request.url} {process_time}")
    return response

效果

swagger隐藏关键接口

有些敏感接口涉及加密信息等的,不在swagger显示,此处以无用的默认/及favicon.ico为例,关键字是include_in_schema=False
代码

@app.get('/favicon.ico',include_in_schema=False)
@app.get('/',include_in_schema=False)

效果
不知道咋截图,就是在swagger看不到这两个了

暴露健康检查接口

每个应用都应该有个接口,直接查询本服务运行状态,依赖的三方是否正常运行
代码

@app.get('/healthz',tags=["health"])
def healthz_check(request:Request):
    res_dict = {}
    res_dict['headers'] = request.headers
    config = []
    cfg_path = os.path.dirname(os.path.abspath(__file__)) + '/config.json'
    if os.path.isfile(cfg_path):
        config.append(f'''ok {cfg_path} exists''')
    else:
        config.append(f'''failed {cfg_path} exists''')
    res_dict['config'] = config
    res_dict['run']    = {}
    res_dict['system'] = {'hostname':'','ip':''}
    res_dict['env'] = os.environ
    return response_struct(data=res_dict)

效果

接口用途和描述

swagger已经很方便展示接口的入参及类型了,如果再加上一些描述岂不更香!
代码

@app.get('/healthz',tags=["health"])
def healthz_check(request:Request):
    '''
    服务健康检查

    留给运维检查服务状态,此处建议代码检查服务正常运行依赖项
    包括:
    - **headers** 请求信息
    - **config** 配置信息
    - **run** 运行依赖配置
    - **redis**  redis是否正常连接
    - **mysql** mysql连接状态
    - **api** 三方api调用是否正常
    '''

效果

后台执行

对于比较耗时的操作,或者前端只是触发一下并不需要立即返回结果,或者是定时任务只需要触发一下后台慢慢执行等
代码

from fastapi import BackgroundTasks
def do_send_mail(mail_to,mail_body):
    logger.info(mail_to,mail_body)
    time.sleep(3)
    logger.info(mail_to,mail_body)
@app.get('/send_mail',tags=['backgound'])
def send_mail_to_someone(mail_to:str,mail_body:str,background_tasks: BackgroundTasks):
    '''
    异步发送邮件

    比较耗时,后台rv执行
    '''
    background_tasks.add_task(do_send_mail,mail_to,mail_body)
    return response_struct()

效果
可以看到两个mail_to的打印时间中间差了3秒,但前端立即返回了

结尾

在fastapi的使用上,我还是小学生,也会一直继续使用下去,有新的心得会继续发出来共享

fastapi

Posted in Python | Tagged , , , , , , , , , , , , | Leave a comment

Python|介绍一下我的新伙伴fastapi(一)

在日常运维工作中,经常性的会创造出一些快速脚本,用来提高工作效率.一直在想有没有可能写一个Web工具,把这些常用的东西收集起来,用的时候直接点击拿到结果.这样即方便又能避免输错还不用登陆服务器显得更有效率.
直到我遇到了fastapi突然眼前一亮,不就是这货吗?让我等的好苦!

fastapi是什么

FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。

关键特性:

  • 快速:可与 NodeJS 和 Go 比肩的极高性能(归功于 Starlette 和 Pydantic)。最快的 Python web 框架之一。

  • 高效编码:提高功能开发速度约 200% 至 300%。*

  • 更少 bug:减少约 40% 的人为(开发者)导致错误。*

  • 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。

  • 简单:设计的易于使用和学习,阅读文档的时间更短。

  • 简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。

  • 健壮:生产可用级别的代码。还有自动生成的交互式文档。

  • 标准化:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。

以上摘自官方文档
https://fastapi.tiangolo.com/zh/

我自己的一些感受

天然集成swagger,有了这个靠山,自然有了以下几大能力

  • 界面有了
    命令行很酷,键指如飞也很让人羡慕,但也仅而已了,真正牛掰的能力是复制,一旦你自身拥有了一项能力,你的团队同时俱备的这项能力,而且同你个人操作没有差别.这应该就是Web一直长盛不衰的本质所在.

  • 入参出参一目了然
    什么类型,是否必须,很直观的显示出来.以前做项目,写完实现,写注释,还要写接口文档给调用方,实在是费时费力,时间久了自己也不记得了.现在好了文档自动生成不说,忘记了直接打开看

  • 界面调试
    你很容易在浏览器里,直接调试你正在编写的接口.以前不觉得的什么,GET接口直接浏览器打开就好了,POST接口自己用curl或者postman造一下就行.现在好了直接内置这些技能,还给你自带一个curl的命令,方便你在命令行测试.当然输出也一目了然.

  • 开发效率
    这个要从几方面来说

    • 自动重启
      这个勉强算是吧,确切的说是uvicorn的能力,加了--reload参数,监控文件变化,自动重启
    • 参数校验
      明确定义参数和类型,少参数和类型不对的情况,fastapi帮你处理,这无形中是巨大的时间成本.
    • 返回结果
      默认json格式返回,以前处理完的结果,别管是数组还是字典都要json.dump一下.现在直接return好了.
  • 兼容flask
    确切的说不能算兼容,只能说语法类型,超级容易转换,只是获取参数的形式变一变,校验参数的部分拿掉,中间逻辑处理不变,返回结果时拿掉json转换
    举例
    转换前

    转换后

其实早就知道swagger这一神奇存在,甚至在我用上了fastapi还专门搜索了一下swagger在nodejs,golang,java等的集成方式,发现都是以插件的形式存在,需要不少的定制才能work,当然flask也可以集成swagger,只是有天然集成的存在实在找不出继续使用flask的理由
一口气,泛泛的纸上谈兵说了这么多,后面还会有一些相应的主题跟进,比如写一个比较全的例子,入门更容易一些,还有一些相对高级的诸如安全相关的东西.

fastapi

Posted in Python | Tagged , , , , , , | Leave a comment

2020年十一体重复盘

十一了,有些时间停下来思考,复盘一下体重情况.但是有段时间没写文章了,复盘前先复盘下这事儿.
发现上一篇公众号发的文章还是07.15.从03.16开始第一篇,写了四个月整.共计82篇.为什么没再坚持每天一篇呢?
首先,每天一篇本身就挑战很大,至少要花费两三个小时的时间,整理,发布.随着积累的文章源源不断的吐出,竟然有了江郎才尽的感觉,又不想胡乱发一些东西凑数,索性不如不写.
其次,时间变得有限了.早先突然想再折腾博客是因为疫情影响,半工状态,闲着也是闲着,不如把这些年积累的想法,有用的东西整理出来.随着疫情的恢复,进入正常工作状态.发现每天的业余时间很有限.工作时间不想开小差,晚上下班回家又仅有一个小时左右的时间,要么陪家人,要么两三天赶工写一篇文章.明显选择了前者.
进入正题,体重复盘
2020年疫情影响,不能正常工作,也减少外出锻炼,但是伙食并没有做相应的调整,于是一路飙升.有数据为证

以上数据来自小米智能秤.发现最早一条记录是03.13于是从15号开始隔10天一次整理出来.(智能手机是个好东西,跟智能秤一连接,每天自动收集,当然还有一点,要每天固定时间点秤一次(早,晚两次)).小米秤还会给出来一个得分.顺道也记录下来了.
半年时间里,体重最高166.5,最低138.7乖乖27.8斤的浮动.真有点折腾我这小身板了.再有就是发现这小这个得分竟然真能得90分.我一直以为这东西不准,只能六七十分呢.

一些直观的感受
确实人到中年啥啥都不容易,长胖最容易.之前体重一直稳定到143左右,忽悠一下就长到了167.真是不可思议.翻了翻之前发的朋友圈:

所以今年最重刷新了记录,最轻也刷新了记录,只不过又回来了一些,看来还有不少的下降空间.另外从整年的角度来看,短时间的起伏其实是可以接受的,不要固定下来就好.
接下来复盘三把斧,一一亮出来
1.做的好的地方

  • 每天秤体重
    数据采集很重要,无论数据好坏,收集起来,有了数据才能分析趋势,才能更好的指导方向.
  • 坚持每天快走1小时
    基本做到了每天,除了开始一段时间周末,懒不想出门,后面基本每天手环计步都在一万步.后来为啥周末也出门了呢,因为发现周末不但运动量少,在家吃的也多,两向一中和造成了很长一段时间,周末体重最高,然后周一到周五体重一直降,周五最低.周六又飙升.
  • 晚餐不吃主食(米,面)
    夏天晚上一般是吃西瓜为主,有时也吃其它水果.您可能问了,真能坚持晚上一直不吃主食?答:不能.吃了怎么办呢?还怎么办,接下来几天加大运动量,把吃的减下来呗.据说贾玲和瞿颖两个好朋友身材的区别就是:同样都撸了串,一个攒下来了,一个锻炼下去了.
  • 保持一个好的作息习惯
    早睡且固定时间入睡是一种能力.前面半年孩子一直在家,最晚能拖到11点入睡,孩子上学后更是养成了9点半洗漱,10点睡觉的好习惯.先不管能不能早起,起码睡眠是充足了.

2.做的不好的地方
吃还是控制不好.遇到好吃的,多吃一些,肚子难受,身体也难受.吃七分饱是个很重要的能力.未来还是要重点的练习.
3.以后怎么干
控制体重是个长期的事业,真正的逆水行舟不进则退.接下来天气凉了,肯定又想多吃点儿热乎热乎,又因为冷了,又不想动.怎么办,继续跟自己较劲呗.

转载请注明: 转自Rainbird的个人博客
   本文链接: 2020年十一体重复盘

Posted in 原创 | Tagged , , , | Leave a comment

Python|setuptools错误一例


随着python2.7的即将退出历史舞台,在ubuntu18.04上pip install pyenv竟然提示 Package 'setuptools' requires a different Python: 2.7.12 not in '>=3.5'

原因就是setuptools>=45的版本都不再支持python2.7
所以解决办法也很简单,降级setuptools

pip install setuptools==44.0.0
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting setuptools==44.0.0
  Downloading setuptools-44.0.0-py2.py3-none-any.whl (583 kB)
     |████████████████████████████████| 583 kB 7.1 kB/s 
Installing collected packages: setuptools
  Attempting uninstall: setuptools
    Found existing installation: setuptools 45.0.0
    Uninstalling setuptools-45.0.0:
      Successfully uninstalled setuptools-45.0.0
Successfully installed setuptools-44.0.0

然后就顺利了

转载请注明: 转自Rainbird的个人博客
   本文链接: Python|setuptools错误一例

Posted in Python | Tagged , , , , , | Leave a comment

Python|openpyxl公式的写入及读取

最近excel研究的比较多,先是字体大小,都是单元格宽度,接着链接的保存及读取,这不又要操作公式了,一并记录下来.

写入

写入比较简单,以SUM这个公式举例了

#/usr/bin/python
#-*- coding: utf-8 -*-
import openpyxl
import os
#将Excel文件放在python同级目录
dir_path    = os.path.dirname(os.path.realpath(__file__))
test_xlsx_w = os.path.join(dir_path,f'''test_write.xlsx''')

wb = openpyxl.Workbook()
sheet = wb.active
sheet.cell(1, 1).value = 1
sheet.cell(2, 1).value = 2
sheet.cell(3, 1).value = '=sum(A1:A2)'

#保存文件
wb.save(test_xlsx_w)

读取

读取要的文件名,是上面写入的那个文件,开工

#/usr/bin/python
#-*- coding: utf-8 -*-
import openpyxl
import os
#将Excel文件放在python同级目录
dir_path  = os.path.dirname(os.path.realpath(__file__))
test_xlsx = os.path.join(dir_path,f'''test_write.xlsx''')
wb = openpyxl.load_workbook(test_xlsx)
sheet = wb.active
print(sheet.cell(3, 1).value)

运行

what?
为啥把公式给我输出了,我需要计算后结果啊?
要实现自动公式计算,需要要读取时添加data_only=True参数,以便只读取数据.

wb = openpyxl.load_workbook(test_xlsx,data_only=True)

不过即使添加了这个参数,读取时得到的结果却为None.

这种情况,将之前的Excel打开,再保存一下就可以了.不知道是不是因为openpyxl生成的xlsx的格式有问题,还是什么原因

总结

开头也说了,写过好几篇操作Excel的文章了,罗列一下:
[Python|openpyxl设置单元格样式]()
[Python|openpyxl单元格设置超链接及读取]()
其实直接点击专辑也可以

Posted in Python | Tagged , , , , , , , , | Leave a comment

Python|openpyxl单元格设置超链接及读取

背景

在Excel中,有时为了提高效率会放置一些外部的超链接,方便点击查看.近日遇到Python要读取和设置超链接的情况,记录一下

软件 版本
Python 3.8.2
Openpyxl 2.6.4

读取

通过Python读取超链接要相对简单一些,先准备一个Excel文件,A1里放置一个超链接,然后开工

录入

#/usr/bin/python
#-*- coding: utf-8 -*-
import openpyxl
import os
#将Excel文件放在python同级目录
dir_path  = os.path.dirname(os.path.realpath(__file__))
test_xlsx = os.path.join(dir_path,f'''test.xlsx''')
wb = openpyxl.load_workbook(test_xlsx)
sheet = wb.active
print(sheet.cell(1, 1).value)
print(sheet.cell(1, 1).hyperlink.target)

保存为pyxl_link_read.py执行后输出

rainbird's blog
https://blog.cnrainbird.com/


比较简单,接下来代码创建

写入

excel设置超链接也比较简单,顺手赠一个设置单元格宽度和字体

#/usr/bin/python
#-*- coding: utf-8 -*-
import openpyxl

#将Excel文件放在python同级目录
dir_path    = os.path.dirname(os.path.realpath(__file__))
test_xlsx_w = os.path.join(dir_path,f'''test_write.xlsx''')
wb = openpyxl.Workbook()
sheet = wb.active
sheet.cell(1, 1).value = '''=HYPERLINK("https://blog.cnrainbird.com","rainbird's blog")'''
sheet.cell(1, 1).font  = openpyxl.styles.Font('宋体',size = 16,bold=False,italic=False,strike=False,color='000000')
sheet.column_dimensions['A'].width = 25.0

后话

之前有写过Python|openpyxl设置单元格样式那篇主要设置单元格字体,宽度,颜色等.今天又进一步学习了超链接方法

Posted in Python | Tagged , , , , , , , , | Leave a comment