Python 批量生成PDF文档, 办公效率火箭般提升

日常办公中,经常会使用PDF文档,难免需要对PDF文档进行编辑,有时候PDF文档中的大部分内容都是一样的,只是发送对象不同。

这种模板套用的场景下,使用Python进行自动化就尤为方便,用最短的时间办最高效的事。

今天就给大家讲讲如何用Python自动套用模板批量生产PDF文档。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

当然,我更推荐大家用VSCode编辑器,把本文代码Copy下来,在编辑器下方的终端运行命令安装依赖模块,多舒服的一件事啊:Python 编程的最好搭档—VSCode 详细指南。

在终端输入以下命令安装我们所需要的依赖模块:

pip install fpdf

看到 Successfully installed xxx 则说明安装成功。

完整源代码可在【Python实用宝典】公众号后台回复:批量生成pdf 下载

2.基本使用

PyPDF是从PHP移植到Python的第三方模块。其主要特点有:

1.非常容易使用和扩展
2.提供了许多简单示例
3.没有外部依赖项
4.不需要安装变异其他库(DLL)

它的基本功能有:

1.调整PDF精度、页面格式、边距
2.管理页眉、页脚
3.自动分页符、自动换行和文本对齐
4.支持图像、颜色、超链接文本
5.支持压缩

一个最简单的生成示例:

from fpdf import FPDF

pdf = FPDF()
# add_page() 增加一页
pdf.add_page()
# set_font 调整字体
pdf.set_font('Arial', 'B', 16)
# 输出Hello World,宽度40,高度10
pdf.cell(40, 10, 'Hello World!')

# 输出文件
pdf.output('tuto2.pdf', 'F')

生成的效果如下:

这只是一个压缩后的图片,真实PDF比这大得多。

把生成的文本变复杂一点:

from fpdf import FPDF

pdf = FPDF()
# add_page() 增加一页
pdf.add_page()
# set_font 调整字体
pdf.set_font('Arial', 'B', 16)
# 输出Hello World,宽度40,高度10
pdf.cell(40, 10, 'Hello World!')

# 居中输出Powered by FPDF.宽度60,高度10
# border=1 设置显示文本框的框,
# ln=1 代表在这个文本框后换行
# align='C' 则表明文本居中。
pdf.cell(60, 10, 'Powered by FPDF.', border=1, ln=1, align='C')

# 输出文件
pdf.output('tuto2.pdf', 'F')

注意cell的参数:
1. border 取值为0或1,代表是否显示边框。
2. ln 是指在生成该文本框之后焦点移动到的位置,0 代表到该文本框的右边,1 代表换行,2 代表文本框的正下方。
3. align 是指文本对齐格式,C为居中、L为左对齐、R为右对齐。

因此上述代码会生成类似这样的PDF文档:

cell还有2个参数:

1. fill 参数用于设置文本框的背景,TRUE时为白色,False为透明。
2. link 参数用于添加超链接

下面看一个比较复杂的例子,带图片LOGO作为页眉,带页数作为页脚的PDF文档:

看看要怎么实现这个PDF文档的生成。

首先需要继承FPDF类,以用于自定义页眉和页脚。

from fpdf import FPDF

class PDF(FPDF):
    """
    继承FPDF类,以自定义页眉也页脚
    """
    def header(self):
        """
        设置页眉
        """
        # 添加logo
        self.image('pythondict_logo.png', 10, 8, 33)
        # 设置字体
        self.set_font('Arial', 'B', 15)
        # 移动焦点至右边
        self.cell(80)
        # 添加标题
        self.cell(30, 10, 'Title', 1, 0, 'C')
        # 换行
        self.ln(20)

    def footer(self):
        """
        设置页脚
        """
        # 底部以上1.5厘米
        self.set_y(-15)
        # 设置字体
        self.set_font('Arial', 'I', 8)
        # 页码
        self.cell(0, 10, 'Page ' + str(self.page_no()) + '/{nb}', 0, 0, 'C')

这样设置完页眉也页脚后,只要你使用该类定义PDF文档,便会自动带上页眉页脚。

# 使用上述继承并进行了自定义的类
pdf = PDF()
pdf.alias_nb_pages()
pdf.add_page()
pdf.set_font('Times', '', 12)
# 填充内容
for i in range(1, 41):
    pdf.cell(0, 10, 'Printing line number ' + str(i), 0, 1)
pdf.output('tuto2.pdf', 'F')

真的是非常方便。

3.批量生成PDF

接下来在Python实用宝典网站(https://pythondict.com)上随机抽取了5位注册了账号的同学的ID批量制作祝福PDF文档。

这里可以复用咱刚自定义完成的页眉和页尾。不过,为了显示中文,需要在继承的类里面添加并设置字体为本地中文字体,如:

from fpdf import FPDF

class PDF(FPDF):
    """
    继承FPDF类,以自定义页眉也页脚
    """
    def header(self):
        """
        设置页眉
        """
        # 添加logo
        self.image('pythondict_logo.png', 10, 8, 33)
        # 设置字体
        self.add_font('yuanti','','yuanti.ttf', True)
        self.set_font('yuanti')
        # 移动焦点至右边
        self.cell(80)
        # 添加标题
        self.cell(30, 10, 'Python实用宝典祝福您', 0, 0, 'C')
        # 换行
        self.ln(20)

注意,add_font第一个参数是该字体注册进FPDF的名字,之后set_font的时候都使用该名字。

然后将用户名都放到一个数组里,遍历该数组,填充用户名,循环批量生成PDF.

for user in users:
    pdf = PDF()
    pdf.alias_nb_pages()
    pdf.add_page()
    pdf.set_font('yuanti', '', 12)
    pdf.cell(0, 10, 'Dear ' + str(user) + ': ', 0, 1)
    # 填充内容
    for i in range(len(texts)):
        pdf.cell(5)
        pdf.multi_cell(0, 10, texts[i], 0)
    pdf.output(f'output/{user}.pdf', 'F')

最终效果如下:

完整源代码可在【Python实用宝典】公众号后台回复:批量生成pdf 下载

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 一行语句算出每个省的面积—geopandas

教你如何用一行语句算出每个省的面积—Geopandas.

GeoPandas是一个基于pandas,针对地理数据做了特别支持的第三方模块。

它继承pandas.Series和pandas.Dataframe,实现了GeoSeries和GeoDataFrame类,使得其操纵和分析平面几何对象非常方便。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

当然,我更推荐大家用VSCode编辑器,把本文代码Copy下来,在编辑器下方的终端运行命令安装依赖模块,多舒服的一件事啊:Python 编程的最好搭档—VSCode 详细指南。

由于geopandas涉及到许多第三方依赖,pip安装起来非常麻烦。因此在本教程中,我只推荐使用conda安装geopandas:

conda install geopandas

一行语句即可完成安装。

2.基本使用

设定坐标绘制简单的图形:

>>> import geopandas
>>> from shapely.geometry import Polygon
>>> p1 = Polygon([(0, 0), (1, 0), (1, 1)])
>>> p2 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
>>> p3 = Polygon([(2, 0), (3, 0), (3, 1), (2, 1)])
>>> g = geopandas.GeoSeries([p1, p2, p3])
>>> g
0    POLYGON ((0 0, 1 0, 1 1, 0 0))
1    POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))
2    POLYGON ((2 0, 3 0, 3 1, 2 1, 2 0))
dtype: geometry

这里有一个强大的用法,通过area属性,geopandas能直接返回这些图形的面积:

>>> print(g.area)
0    0.5
1    1.0
2    1.0
dtype: float64

不仅如此,通过plot属性函数,你还可以直接生成matplotlib图。

>>> g.plot()

通过matplot的pyplot,可以将图片保存下来:

import matplotlib.pyplot as plt
g.plot()
plt.savefig("test.png")

学会上面的基本用法, 我们就可以进行简单的地图绘制及面积的计算了。

3.绘制并算出每个省的面积

此外,它最大的亮点是可以通过fiona读取比如ESRI shapefile(一种用于存储地理要素的几何位置和属性信息的非拓扑简单格式)。

import geopandas
import matplotlib.pyplot as plt
from shapely.geometry import Polygon
maps = geopandas.read_file('1.shx')
# 读取的数据格式类似于
#                                             geometry
# 0   POLYGON ((1329152.341 5619034.278, 1323327.591...
# 1   POLYGON ((-2189253.375 4611401.367, -2202922.3...
# 2   POLYGON ((761692.092 4443124.843, 760999.873 4...
# 3   POLYGON ((-34477.046 4516813.963, -41105.128 4...
# ... ...
maps.plot()
plt.savefig("test.png")

如代码所示,通过read_file你可以读取shx、gpkg、geojson等数据。读取出来的图形如下:

同样滴,我这个shapefile是省级行政区的,每一个省级行政区都被划分为一个区块,因此可以一行语句算出每个省级行政区所占面积:

print(maps.area)
# 0     4.156054e+11
# 1     1.528346e+12
# 2     1.487538e+11
# 3     4.781135e+10
# 4     1.189317e+12
# 5     1.468277e+11
# 6     1.597052e+11
# 7     9.770609e+10
# 8     1.385692e+11
# 9     1.846538e+11
# 10    1.015979e+11
# ... ...

怎么样,是不是很酷?它还有许多更库的特性,欢迎阅读官方文档:
https://geopandas.readthedocs.io/

本文用到的shx格式省级行政区数据,可以在【Python实用宝典】公众号后台回复 省级行政区下载。​

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

Pycharm+Django 安装及配置指南

虽然平时我极力推荐使用简单、轻便、灵活的VSCode编辑器进行开发。

但是对于大型Web项目(Django、Tornado等框架)开发,我还是推荐大家使用Pycharm.

Pycharm拥有强大的配置工具、Git版本管理工具、代码补全工具、Debug工具等等,这些都是进行大型项目开发的利器。

尤其是今天的主角Django,由于太过于重要了,Pycharm甚至专门给其提供了配置模板:

能直接在新建项目的时候选择Django并新建一个独立的虚拟环境:

从新建到编码测试,一套流程用起来都极其方便。

1.下载Pycharm

在jetbrains官网选择相应的系统下载Pycharm:
https://www.jetbrains.com/pycharm/download/

这里强烈推荐下载Professional版(专业版),激活方法可以自行在网上查询,推荐知了哥的文章(zhile.io)。

安装完成后,根据你是否需要新建Django项目分为两种配置方式。

1.1 新建Django项目

File-New Project 新建一个Django项目:

强烈推荐新建一个环境,默认新建环境的工具是Virtualenv, 我这里用的是conda,效果其实都差不多。区别在于,conda可以选择Python版本

输入好location(安装位置)后点击create,即可生成Django项目。

1.2 配置已开发的Django项目

Pycharm中适配已开发的Django项目也非常容易,因为它为这种情况专门提供了配置模板:

点击右上角的配置框选择 Edit Configurations

进来后先输入Name 项目名称,然后在Python Interpreter选择你的代码所属环境的编译器,最后选择Fix,弹出Django配置页。

在点击Fix后出现的配置页中,输入这三项:

第一个是项目根目录,第二个是settings.py文件的位置,第三个是manage.py的位置。三者缺一不可。搞定后点击OK,配置完成。

2.运行项目

Pycharm运行Django项目只需要点击右上角这两者之一即可:

第一个是普通的启动方式,第二个是Debug启动方式,推荐第二个,因为开发的时候如果需要跟踪代码流程,Debug模式非常方便。

点击后会自动生成启动Django的命令,你可以在console里查看该语句,出现以下的输出即启动完成:

访问http://127.0.0.1:8000/就是网站的首页了。

这里默认使用的端口号是8000,你可以在配置页修改默认的域名和端口,只不过其他域名你需要在hosts中将其定向到127.0.0.1, 比如:

修改hosts文件,增加:dev.goldenstone.com 127.0.0.1

保存后修改右上角的configurations配置:

在settings.py中,将dev.goldenstone.com这个域名加入到 ALLOWED_HOSTS 中:

这样就可以通过 http://dev.goldenstone.com:5555/ 访问你的开发环境了:

3.工具

下面介绍一些Pycharm中独有的、特别的工具:

3.1 查看文件历史修改及提交记录非常方便:

点击每一个提交,都能看到每次提交的内容。

3.2 选择指定的commit行

VCS-Commit,它可以自由选择你需要提交的代码块:

3.3 自动格式化代码

3.4 全局搜索

尽管VSCode也有全局搜索,但相信我,他们两个不是一个概念:

windows下选择 Ctrl+shift+F 即可在Pycharm中全局搜索,或者在Edit-Find-Find in Path 找到该功能:

界面如下,它不仅仅是全局搜索,还能指定模块、目录进行搜索。

指定文件名搜索,高级过滤器中还能搜索指定除注释以外的符合关键词的句子等等,是一个非常强大的搜索工具。

这个搜索工具对于我而言,使用频率仅次于Debug工具。


Pycharm中非常有用的生产工具还有很多,简直是用都用不完,下次再给大家详细介绍一番!

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

编程利器!有道词典命令行快速翻译

在编程时经常会遇到需要将中文词汇翻译成英文的情况。

比如变量名的定义、取一个合适的函数名等情况。

遇到不会翻译的词汇时,往往都需要借助有道词典等翻译工具。

但无论是下载客户端、还是打开翻译网站页面,都感觉非常麻烦。

现在,有一个小伙伴帮我们制作了一个在命令行输入词汇,就能即时翻译的工具,实在非常方便!

比如翻译hello world, 你只需要在终端输入:

youdao hello world

项目地址是:WangXin93/youdao_dict

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

在终端输入以下命令安装我们所需要的依赖模块:

pip install youdao_dict

看到 Successfully installed xxx 则说明安装成功。

2.使用

当你遇到不会翻译的词汇,只需要在下方编辑器,轻轻敲击:

youdao 你不会翻译的词汇

比如说,“微醺”的英文:

当然,也可以英文翻译为中文,比如standalone:

3.源码分析

这个模块的原理非常简单,就是调用了有道翻译网页版的API,这个API拥有自动检测语言的功能:

然后再根据页面HTML结构及其分布,爬取不同释义:

Python就是一门能用如此简单的代码,给你带来巨大方便的语言,体会到了吗?

如果你有类似的需求,也可以学着做一个类似地、能够提高生活效率的第三方库,上传到GitHub,然后在【Python实用宝典】公众号后台留言,我会帮你宣传并分享给大家。

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 三行代码检测爬虫

是否担心别人将你的博客文章全部爬下来?

是否担心高频率爬虫导致网站瘫痪?

别担心,现在有一个Python写的神器——crawlerdetect,帮助你检测爬虫,保障网站的正常运转。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

当然,我更推荐大家用VSCode编辑器,把本文代码Copy下来,在编辑器下方的终端运行命令安装依赖模块,多舒服的一件事啊:Python 编程的最好搭档—VSCode 详细指南。

在终端输入以下命令安装我们所需要的依赖模块:

pip install crawlerdetect

看到 Successfully installed xxx 则说明安装成功。

2.使用方法

它可以通过user-agent、headers等请求头识别爬虫或机器人。

因此,你可以传递两种参数。第一种,使用user-agent检测机器人:

from crawlerdetect import CrawlerDetect
crawler_detect = CrawlerDetect(user_agent='Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit (KHTML, like Gecko) Mobile (compatible; Yahoo Ad monitoring; https://help.yahoo.com/kb/yahoo-ad-monitoring-SLN24857.html)')
crawler_detect.isCrawler()
# 如果是机器人,这条语句返回True

第二种识别方式会用上全部headers参数,这种方式比单纯用user-agent精准,因为它判断的依据更加全面。

from crawlerdetect import CrawlerDetect
crawler_detect = CrawlerDetect(headers={'DOCUMENT_ROOT': '/home/test/public_html', 'GATEWAY_INTERFACE': 'CGI/1.1', 'HTTP_ACCEPT': '*/*', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_CACHE_CONTROL': 'no-cache', 'HTTP_CONNECTION': 'Keep-Alive', 'HTTP_FROM': 'googlebot(at)googlebot.com', 'HTTP_HOST': 'www.test.com', 'HTTP_PRAGMA': 'no-cache', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36', 'PATH': '/bin:/usr/bin', 'QUERY_STRING': 'order=closingDate', 'REDIRECT_STATUS': '200', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '3360', 'REQUEST_METHOD': 'GET', 'REQUEST_URI': '/?test=testing', 'SCRIPT_FILENAME': '/home/test/public_html/index.php', 'SCRIPT_NAME': '/index.php', 'SERVER_ADDR': '127.0.0.1', 'SERVER_ADMIN': 'webmaster@test.com', 'SERVER_NAME': 'www.test.com', 'SERVER_PORT': '80', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SIGNATURE': '', 'SERVER_SOFTWARE': 'Apache', 'UNIQUE_ID': 'Vx6MENRxerBUSDEQgFLAAAAAS', 'PHP_SELF': '/index.php', 'REQUEST_TIME_FLOAT': 1461619728.0705, 'REQUEST_TIME': 1461619728})
crawler_detect.isCrawler()
# 如果是机器人,这条语句返回True

你还可以识别相应爬虫的名字(如果有的话),通过这种方式,你能给一些著名的爬虫(如baiduspider、googlebot)添加白名单,不进行拦截。

from crawlerdetect import CrawlerDetect
crawler_detect = CrawlerDetect()
crawler_detect.isCrawler('Mozilla/5.0 (compatible; Sosospider/2.0; +http://help.soso.com/webspider.htm)')
# 如果是机器人,这条语句返回True
crawler_detect.getMatches()
# Sosospider

有了这个工具,我们就可以实现实时的爬虫封禁:

首先,你要实时监控网站上的http请求,检测其对应的headers.

其次,如果识别到该请求是机器人(爬虫)发出的,就可将其IP记录下来。

最后,将IP加入到Nginx或Apache的动态黑名单中,实现实时的爬虫封禁。

这一套流程我还没有试验过,大家有兴趣可以试试,理论上可行。

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python celery异步快速下载股票数据

上一篇股票文章中,我们讲了如何通过tushare下载股票数据,并存入mongodb:

Python 获取股票数据并存入MongoDB实战教程

其中有非常大的优化空间,比如,单线程的下载速度太慢了,能不能用多线程的方式?有时候网络连接会失败,能不能增加重试机制?

这些问题,我们将在这篇文章里进行优化。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

当然,我更推荐大家用VSCode编辑器,把本文代码Copy下来,在编辑器下方的终端运行命令安装依赖模块,多舒服的一件事啊:Python 编程的最好搭档—VSCode 详细指南。

此外,本文章是 Python 获取股票数据并存入MongoDB实战教程 的优化版,请根据该文章提前装好tushare和mongodb.

除此之外,你还需要安装celery和eventlet:

pip install celery
pip install eventlet

看到 Successfully installed xxx 则说明安装成功。

2.使用Celery异步下载股票数据

Celery是一个强大的异步任务队列,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。

我们通常使用它来实现异步任务(async task)和定时任务(crontab)。

为了能异步下载股票,我们需要将上篇文章的内容拆分为两个部分:

1.Celery任务:获得某股票的日线数据
2.分发任务:分发获取指定股票的日线数据

这样,在我们的场景中,使用Celery实现异步任务主要包含三个步骤:

1.创建Celery任务实例(获得某股票的日线数据)
2.启动Celery Worker,用于运行任务
3.应用程序分发任务(分发获取指定股票的日线数据)

思路已经理清楚了,下面动手实践一下:

2.1 创建Celery任务实例:

此处大部分代码和上篇文章相似,因此下面仅显示和celery相关的核心代码:

# Python实用宝典
# https://pythondict.com
from celery import Celery

# 设置BROKER
BROKER_URL = 'mongodb://127.0.0.1:27017/celery'
# 新建celery任务
app = Celery('my_task', broker=BROKER_URL)

@app.task
def get_stock_daily(start_date, end_date, code):
    """
    Celery任务:获得某股票的日数据

    Args:
        start_date (str): 起始日
        end_date (str): 结束日
        code (str): 指定股票
    """


    # 请求tushare数据,并转化为json格式
    df = pro.daily(ts_code=code, start_date=start_date, end_date=end_date)
    data = json.loads(df.T.to_json()).values()

    # 这里为了保证数据的绝对稳定性,选择一条条创建
    for row in data:
        daily.update({"_id": f"{row['ts_code']}-{row['trade_date']}"}, row, upsert=True)

    print(f"{code}: 插入\更新 完毕 - {start_date} to {end_date}")

2.2 启动worker

在cmd执行以下命令启动celery worker:

python -m celery worker -A tasks --loglevel=info --pool=eventlet

注意,这里使用了–pool=eventlet,是为了让windows机器具有并行运行的能力。

2.3 分发获取指定股票的日数据

遍历一遍股票列表,通过delay调用Celery任务实例,将任务分发给worker:

# Python实用宝典
# https://pythondict.com
from tasks import get_stock_daily

def delay_stock_data(start_date, end_date):
    """
    获得A股所有股票日数据

    Args:
        start_date (str): 起始日
        end_date (str): 结束日
    """

    codes = open('./codes.csv', 'r', encoding='utf-8').readlines()

    # 遍历所有股票ID
    for code in codes:
        get_stock_daily.delay(start_date, end_date, code)

delay_stock_data("20180101", "20200725")

这样,worker就会在后台异步执行这些任务,切换到worker的命令行中,你会看到输出如丝般润滑:

好景不长,不久后你肯定会受到tushare发送回来的CPS限制错误:

Exception: 抱歉,您每分钟最多访问该接口800次,权限的具体详情访问:https://tushare.pro/document/1?doc_id=108。

3.限制访问次数与重试机制

为了解决这个CPS问题,我确实花了不少时间,尝试控制worker频率,无果,最终选择了一个不是办法的办法:

在Tushare报错的时候,捕捉起来,控制其60秒后重试

    # 请求tushare数据,并转化为json格式
    try:
        df = pro.daily(ts_code=code, start_date=start_date, end_date=end_date)
    except Exception as e:
        # 触发普通用户CPS限制,60秒后重试
        print(e)
        get_stock_daily.retry(countdown=60)

简单,但有效。

此外,为了防止网络请求出现问题,导致某个任务丢失,我们还可以在下发任务的时候使用apply_async配置失败重试。默认重试三次:

def delay_stock_data(start_date, end_date):
    """
    获得A股所有股票日数据

    Args:
        start_date (str): 起始日
        end_date (str): 结束日
    """

    codes = open('./codes.csv', 'r', encoding='utf-8').readlines()

    # 遍历所有股票ID
    for code in codes:
        get_stock_daily.apply_async(
            (start_date, end_date, code), retry=True
        )

这样,一个相对健壮的股票数据异步下载器就完成了。

用该方法把A股所有股票下载一遍,应该不会超过5分钟。

如果你给tushare氪了金,没有cps限制,下载时间不会超过1分钟。

目前github上好像缺少这样的项目,因此我将该项目开源到了GitHub上:

https://github.com/Ckend/stock_download_celery

目前仅支持日线数据,欢迎补充。

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

最全总结!聊聊 Python 发送邮件的几种方式

1. 前言

邮件,作为最正式规范的沟通方式,在日常办公过程中经常被用到

我们都知道 Python内置了对 SMTP 的支持,可以发送纯文本、富文本、HTML 等格式的邮件

本文将聊聊利用 Python 发送邮件的 3 种方式

2. 准备

以 126 邮箱为例,在编码之前,我们需要开启 SMTP 服务

image

然后,手动新增一个授权码

其中,账号、授权码和服务器地址用于连接登录邮箱服务器

3. 方式一:smtplib

smtplib是 Python 自带的依赖库,可以直接导入使用首先,通过邮箱账号、授权码、邮箱服务器地址初始化一个 SMTP 实例,然后进行连接

def __init__(self):
    # 初始化
    self.smtp = smtplib.SMTP()

    # 连接邮箱服务器地址
    self.smtp.connect('smtp.126.com')

    # 加入主题和附件,邮件体
    self.email_body = MIMEMultipart('mixed')

    # 发件人地址及授权码
    self.email_from_username = '**@126.com'
    self.email_from_password = '授权码'

# 登录
self.smtp.login(self.email_from_username, self.email_from_password)

然后,将收件人列表、邮件标题、邮件正文内容、附件路径及附件文件名加入到上面定义的邮件体中

def generate_email_body(self, email_to_list, email_title, email_content, attchment_path, files):
    """
    组成邮件体
    :param email_to_list:收件人列表
    :param email_title:邮件标题
    :param email_content:邮件正文内容
    :param attchment_path:附件的路径
    :param files:附件文件名列表
    :return:
    """
    self.email_body['Subject'] = email_title
    self.email_body['From'] = self.email_from_username
    self.email_body['To'] = ",".join(email_to_list)

    for file in files:
        file_path = attchment_path + '/' + file
        if os.path.isfile(file_path):
            # 构建一个附件对象
            att = MIMEText(open(file_path, 'rb').read(), 'base64', 'utf-8')
            att["Content-Type"] = 'application/octet-stream'
            att.add_header("Content-Disposition", "attachment", filename=("gbk", "", file))
            self.email_body.attach(att)

    text_plain = MIMEText(email_content, 'plain', 'utf-8')
    self.email_body.attach(text_plain)

接着,就可以使用 SMTP 实例对象,将邮件发送出去

  # 收件人列表
email_to_list = ['收件人1地址','收件人2地址']

# 发送邮件
# 注意:此处必须同时指定发件人与收件人,否则会当作垃圾邮件处理掉
self.smtp.sendmail(self.email_from_username, email_to_list, self.email_body.as_string())

邮件发送完毕之后,退出服务即可

def exit(self):
    """
    退出服务
    :return:
    """
    self.smtp.quit()

4. 方式二:zmail

Zmail 项目创建目的是,使邮件处理变得更简单使用 Zmail 发送接收邮件方便快捷,不需要手动添加服务器地址、端口以及适合的协议,可以轻松创建 MIME 对象和头文件注意:Zmail 仅支持 Python3,不支持 Python2首先,安装依赖库

# 安装依赖库
pip3 install zmail

然后,使用邮箱账号、授权码创建一个邮箱服务对象

class ZMailObject(object):
​
    def __init__(self):
        # 邮箱账号
        self.username = '**@126.com'

        # 邮箱授权码
        self.authorization_code = '授权码'

        # 构建一个邮箱服务对象
        self.server = zmail.server(self.username, self.authorization_code)

接着,将邮件主题、邮件内容、包含的附件路径加入到一个字典中,组成邮件主体

# 邮件主体
mail_body = {
        'subject': '测试报告',
        'content_text': '这是一个测试报告',  # 纯文本或者HTML内容
        'attachments': ['./attachments/report.png'],
}

最后,将调用send_mail()函数,将邮件发送出去即可

# 收件人
# 可以指定一个人,字符串;也可以是多个人,列表
mail_to = "收件人1"

# 发送邮件
self.server.send_mail(mail_to, mail_body)

5. 方式三:yagmail

yagmail 只需要几行代码,就能实现发送邮件的功能

相比 zmail,yagmail 实现发送邮件的方式更加简洁优雅

首先,安装依赖库

# 安装依赖库
pip3 install yagmail

然后,通过账号、授权码和服务器地址连接邮箱服务器,返回一个服务对象

​import yagmail

# 连接服务器
# 用户名、授权码、服务器地址
yag_server = yagmail.SMTP(user='**@126.com', password='授权码', host='smtp.126.com')

接着,通过 send() 函数,将邮件发送出去

​# 发送对象列表
email_to = ['**@qq.com', ]
email_title = '测试报告'
email_content = "这是测试报告的具体内容"
# 附件列表
email_attachments = ['./attachments/report.png', ]

# 发送邮件
yag_server.send(email_to, email_title, email_content, email_attachments)

邮件发送完毕之后,关闭连接即可

# 关闭连接
yag_server.close()

6. 最后

上面总结了 Python 发送邮件的 3 种方式,实际项目中,更推荐使用后两种方式

我已经将文中全部源码上传到公众号「 AirPython 」后台,关注公众号后回复「 email 」即可获得全部源码

如果你觉得文章还不错,请大家 点赞、分享、留言下,因为这将是我持续输出更多优质文章的最强动力!

推荐阅读
这款黑科技,不会代码也能玩自动化,高效摸鱼

自动化篇 | PC 端这款黑科技录制脚本,完爆按 X 精灵!

如何结合爬虫与自动化,帮助小姐姐刷抖音完全解放掉双手

Python 一个漂亮的音乐节奏可视化方案

相信很多人都有这样的疑问:如何用Python音乐的节奏可视化出来?我曾有过一篇文章:Python 提取音乐频谱并可视化,也不过是浅尝辄止,没有完成精美的可视化,只是将频谱用折线图进行了可视化

国外有个网友(u/avirzayev)分享了他的可视化方案。上方的视频就是用他的方案可视化Tattoo.mp3得到的结果,大家可以欣赏一下。

这份代码确实有效地跟上了音乐的节奏,如果能加强可视化效果,丰富颜色,是一份非常好的可视化代码。

下面给大家介绍一下怎么使用这份代码:

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

当然,我更推荐大家用vscode编辑器,把本文代码Copy下来,在编辑器下方的终端运行命令安装依赖模块,多舒服的一件事啊:Python 编程的最好搭档—VSCode 详细指南。

在终端输入以下命令安装我们所需要的依赖模块:

pip install matplotlib
pip install librosa
pip install numpy
pip install pygame

看到 Successfully installed xxx 则说明安装成功。

当然,还有作者的源代码,请在 Python实用宝典 公众号后台回复:【音乐可视化】获取

2.代码调整

代码架构分为两个部分,一个是用于计算频谱的 AudioAnalyzer.py,一个用于渲染生成动态视频的 mAIn.py.

这个“动态视频”是基于pygame实现的,部分代码如下:

作者的代码风格随意,不过仔细阅读还是能读懂大概的。

首先,通过pygame加载(load)音乐文件并播放(play).

然后,通过while循环和ticks对画面中的图像进行实时渲染。

渲染的代码比较长,就是一些计算柱体长度的过程,这里就不赘述了,有兴趣的同学可以开源代码。

如果你想要将你的音乐用这份代码进行可视化,仅需要修改mAIn.py的第5行代码:

这个filename可以是当前运行Python的路径下的音乐文件名称,也可以是音乐文件的绝对路径。

如果你想优化生成的动态图像的颜色,可以修改rnd_color函数,该函数控制图形颜色的变化:

如果你想修改生成的动态图像的形状,比如说去掉中间那个圆,仅需要这么改:

pygame.draw.circle(screen, circle_color, (circleX, circleY), int(radius))

将radius直接设为0,或者直接将这行代码注释掉即可:

pygame.draw.circle(screen, circle_color, (circleX, circleY), 0)

效果:

如果你熟悉pygame的相关函数,可以对这份可视化的代码做更多的DIY,比如说将其展平到平面上,用一条条柱体来展示,像网易云音乐一样:

尽管不可能做得像真正的产品那样好看,不过大家可以先试试,这份代码我就留着下次再展示吧。

如果有遇到不懂的,请在公众号后台回复:加群,加入互助群。记得备注相应的验证信息我才会通过哦。

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 获取股票数据并存入MongoDB实战教程

量化投资系列文章:

Backtrader 教程 — Python 量化投资实战教程(1)

Python 量化投资实战教程(2) —MACD策略

Python 量化投资实战教程(3) —A股回测MACD策略

Python 量化投资实战教程(4) —KDJ 策略

Python 量化投资实战教程(5) — A股回测KDJ 策略

Python 量化投资实战教程(6) — 交易平均收益率

在前几篇量化投资文章中,使用的是保存为csv的本地股票数据。

但考虑到长远的未来,如果我们想要加快回测运行速度,就要分布式地在不同的机器上进行策略回测。

那么将股票数据保存到数据库上则是必然的选择,因为这样每台服务器都能够直接连接到同一个数据库拉到数据,管理起来方便许多。

今天我们就先讲讲如何使用MongoDB保存股票数据。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

当然,我更推荐大家用VSCode编辑器,把本文代码Copy下来,在编辑器下方的终端运行命令安装依赖模块,多舒服的一件事啊:Python 编程的最好搭档—VSCode 详细指南。

在终端输入以下命令安装我们所需要的依赖模块:

pip install tushare
pip install pymongo

看到 Successfully installed xxx 则说明安装成功。

安装MongoDB

MongoDB 请前往以下链接下载:

https://www.mongodb.com/try/download/community

第一步选择On-Premises, 第二部选择自己的操作系统,第三部点击Download下载安装包。

下载完成安装包后一路默认即可,不过推荐修改data_path, 毕竟数据库有可能变得比较大,装在C盘不合适。

安装完成后,打开CMD输入以下命令启动MongoDB服务:

C:\Program Files\MongoDB\Server\4.2\bin\mongod.exe --dbpath F:\mongodb\data

前者是你的mongod.exe所在文件夹,后者是你的数据库文件路径。请注意根据自己的安装配置更改。

Tushare 股票数据接口

为了下载股票数据,我用到了tushare,一个大佬做的相对稳定的股票数据服务。

tushare采用积分制,你的积分越高,能调的接口约高级。不过其实我们的策略所需要的接口用普通账号的积分就够了。

你需要到https://tushare.pro/上注册一个tushare账户,然后进入账号页面拿到token:

这是我们需要用到的接口:

2.编写代码

首先,学会连接MongoDB数据库并创建集合(表),也就4行代码的事情:

import pymongo

# 建立连接
client = pymongo.MongoClient(host='localhost', port=27017)

# 连接stock数据库,注意只有往数据库中插入了数据,数据库才会自动创建
stock_db = client.stock

# 创建一个daily集合,类似于MySQL中"表"的概念
daily = stock_db["daily"]

这样就能连接本地MongoDB的stock数据库,如果该库不存在,当你往集合中插入数据的时候,就会自动新建数据库。

有一点需要非常注意:MongoDB默认不设密码,因此你如果要上线MongoDB,请注意手动设置密码。

其次,拿到股票数据:

def get_stock_daily(start_date, end_date):
    """
    获得A股所有股票日数据

    Args:
        start_date (str): 起始日
        end_date (str): 结束日
    """

    pro = ts.pro_api(token="你的账号token")
    codes = open('./codes.csv', 'r', encoding='utf-8').readlines()

    # 遍历所有股票ID
    for code in codes:
        code = code.strip('\n')

        # 请求tushare数据,并转化为json格式
        df = pro.daily(ts_code=code, start_date=start_date, end_date=end_date)

因为并不一定需要用到所有股票数据,所以我维护了一个股票列表:codes.csv.

当然,这个股票列表最好也写到MongoDB里以方便维护(我这里省麻烦就没写入了)。

如果你需要用到所有股票,请调tushare的stock_basic接口:

data = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date')

这个接口能返回当前所有正常上市的股票列表。

最后,将这些股票数据写入到MongoDB中:

def get_stock_daily(start_date, end_date):
    """
    获得A股所有股票日数据

    Args:
        start_date (str): 起始日
        end_date (str): 结束日
    """

    pro = ts.pro_api(token="你的账号token")
    codes = open('./codes.csv', 'r', encoding='utf-8').readlines()

    # 遍历所有股票ID
    for code in codes:
        code = code.strip('\n')

        # 请求tushare数据,并转化为json格式
        df = pro.daily(ts_code=code, start_date=start_date, end_date=end_date)
        data = json.loads(df.T.to_json()).values()

        # 对股票每一天数据进行保存,注意唯一性
        # 这里也可以批量创建,速度更快,但批量创建容易丢失数据
        # 这里为了保证数据的绝对稳定性,选择一条条创建
        for row in data:
            daily.update({"_id": f"{row['ts_code']}-{row['trade_date']}"}, row, upsert=True)

        time.sleep(0.1)

获得请求数据后,将其转置一下,以变成【字段-值】的形式,并转成json.

转成json后就可以保存到数据库中,这里我们选择了一条条数据地插入,而不是批量插入。

因为批量插入在遇到重复值的时候,可能会导致那一批数据全部丢失,对于股票回测而言,丢失数据是致命的。

最后,为了防止请求接口频率过高,设置了一个time.sleep延时。

全部核心代码如上所示,可运行的完整代码可以在【Python实用宝典】后台回复:MongoDB 下载。

3.可视化查看数据

MongoDB可视化查看数据,我推荐NoSQLBooster. 一个免费的MongoDB查看器。

https://nosqlbooster.com/downloads

选择对应系统下载,一路默认安装就能开始使用。

基本上你需要的功能它都有了(这界面其实很像Mysql Workbench)

啊对了,最后别忘记加我们新的交流群,加我好友,备注上面红字语句答案即可进群(防范营销号,大家见谅):

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

用Python实战制作一张极具意义的人生进度图

时间如流水,一路向前,转眼间,2020年已过半。

这半年的你,过得还好吗?

无论过去的半年里,你是快乐还是难过,请不要沉溺于过去。

因为如果我们从宏观的角度上来看待人生,按周计算,你会发现过去的半年实际上也不过是微小的几个点而已。

对于我而言,它是这样的:

这些微小的点像钻石一般组成了你的人生。每一个点都很重要,但未来的点更关键。

看着这张图表,你有一个非常重要的问题需要回答:“我是否在充分利用自己的时间,我有没有适当地享受人生”

事实上,我认为这张图表上的每个点,即人生的每个星期,这样过是最合适的:

1.享受生活

2.为未来的每个点能够享受生活打下基础

3.创造一些能让自己或他人活得更有趣的东西

在理想状态下,上面的三点应该是互相平衡的。(当然,理想状态是不存在的)

如果你现在暂时无法平衡这三点,没关系,制作这张图,每个星期都来关注一下自己的人生进度。

神秘的原力会帮助你平衡这三点的(咋回事,我可是纯正的理工科生。

你可以把一些关键的人生时间节点标记在这张图表上,比如我认为创建Python实用宝典是一件很有趣的事情:

这么看,Python实用宝典创建了才不到半年的时间就赶上了疫情啊,这一点我倒是没注意到。

当然,你也可以丰富一下这张图表,把一些从小到大的重要节点写上,并记录今天所在的位置:

粗略地画了一张我的Life Graph,(当然没把细致的写出来哈,毕竟属于隐私),不过,我这个一会玩疯一会学疯也是挺有趣的。

接下来就教大家如何用Python最快、最方便地画出这张表,实际上我仅用了20行代码。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda

事实上,如果我们从零开始画这个图表,是会比较麻烦的。

幸好,已经有Python同好—K20shore 大神的 Life-Graph项目帮我们做了这件事了。

你可以直接在GitHub上下载他的源代码,也可以关注Python实用宝典公众号,回复 “人生” 下载。

这份源代码依赖于Latex,所以你还需要下载 MiKTeX,一路按默认安装即可:
https://miktex.org/download

安装的时候请记住路径,因为安装完成后还需要将latex.exe的路径放入环境变量中:

2.基本使用

K20shores大神的这个库基本包含了我们所有需要使用的元件。

比如前面提到的标记某一个时间段,可以用add_era:

# 增加事件名称及其时间段
g.add_era('COVID-19', date(2020, 1, 1), date(2020, 7, 1), color="#605041")

示例如下:

from lifegraph.lifegraph import Lifegraph, Papersize, Side
from datetime import date

# 以出生日期为起点
birthday = date(1997, 6, 28)
g = Lifegraph(birthday, dpi=300, size=Papersize.A4, max_age=100)

g.add_title("Life Graph")
g.show_max_age_label()

# 增加事件名称及其时间段
g.add_era('COVID-19', date(2020, 1, 1), date(2020, 7, 1), color="#605041")

g.save("grid.png")

部分截图:

如果你需要标记一个点,可以使用add_life_event函数:

# 标记一个生命事件,可配置文本颜色及显示位置
g.add_life_event('Pythondict\nCreated', date(2019, 7, 31), color="#dc143c", side=Side.LEFT)

参数color可以配置相关线条及文本颜色,side可以配置文本显示在左边还是右边。

示例如下:

from lifegraph.lifegraph import Lifegraph, Papersize, Side
from datetime import date

# 以出生日期为起点
birthday = date(1997, 6, 28)
g = Lifegraph(birthday, dpi=300, size=Papersize.A4, max_age=100)

g.add_title("Life Graph")
g.show_max_age_label()

# 增加事件名称及其时间段
g.add_era('COVID-19', date(2020, 1, 1), date(2020, 7, 1), color="#605041")
g.add_life_event('Pythondict\nCreated', date(2019, 7, 31), color="#dc143c", side=Side.LEFT)

g.save("grid.png")

部分截图:

事实上,只要你会了这两个操作,基本上就没问题了。

值得注意的是,如果你需要用中文,请搜索“Latex 使用中文”相关的配置教程。

3.高级使用

其实它还可以配置背景图,这是我没想到的:

from lifegraph.lifegraph import Lifegraph, Papersize, Side
from datetime import date

# 以出生日期为起点
birthday = date(1997, 6, 28)
g = Lifegraph(birthday, dpi=300, size=Papersize.A4, max_age=100)

g.add_title("Life Graph")
g.show_max_age_label()

# 增加事件名称及其时间段
g.add_era('COVID-19', date(2020, 1, 1), date(2020, 7, 1), color="#605041")
g.add_life_event('Pythondict\nCreated', date(2019, 7, 31), color="#dc143c", side=Side.LEFT)

# 设置背景图
g.add_image("unnamed.jpg", alpha=0.5)

g.save("grid.png")

效果如下:

此外,连接多个点也是可以的:

from lifegraph.lifegraph import Lifegraph, Papersize, Side
from datetime import date

# 以出生日期为起点
birthday = date(1997, 6, 28)
g = Lifegraph(birthday, dpi=300, size=Papersize.A4, max_age=100)

g.add_title("Life Graph")
g.show_max_age_label()

# 增加事件名称及其时间段
g.add_era('COVID-19', date(2020, 1, 1), date(2020, 7, 1), color="#605041")

# 连接 A B 两个日期
g.add_era_span(
    'From A to B',
    date(2016, 12, 31), date(2020, 7, 13),
    color="#4423fe",
    side=Side.LEFT
)

g.save("grid.png")

基本上全部的使用方法就这些了。

希望大家能用这个工具,时刻提醒自己:

快乐地度过每一个星期,做一些有意义的事情,不负韵华。

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

有趣好用的Python教程

退出移动版
微信支付
请使用 微信 扫码支付