① 如何使用python解析超大XML文档
在工作时最有吸引力的地方在于可以尽量避免使用昔日的技术。主机、租用线路、COBOL语言......没有人应该要处理这些东西了,对不对?不幸的是,你最终会与现实发生冲突,即使是2014年,大家都知道JSON是最好的方式,你的票务供应商(你无法控制的)会告诉你,只有使用XML导出才能让大容量的数据输出他们的系统。
唉~~~~,好,很好,无所谓。这只是一次性的事情,我不需要照顾和养活这个XML,我只需要解析它并将数据保存到Postgres中,我们就可以利用它。不应该太困难,我需要写一点python脚本…
import xml.etree.cElementTree as ET
tree = ET.parse('huge.xml')
for ticket_node in tree.findall('ticket'):
#etc...
......这将工作的非常好,如果我们谈论的是一个几MB的XML文档,但是如果遇到的是huge.xml它是1.3GB的巨大文档,这种方法只会融化你的笔记本电脑(以16GB的MacBookPro,一旦python的过程花了超过约3GB的内存,系统变得几乎完全反应迟钝,并且它几乎还没有完成)。回到原点。
首先让我们快速浏览一下我们的数据。
<?xml version="1.0" encoding="UTF-8"?>
<tickets report_date="20140217">
<ticket>
<!-- various ticket fields, some of which I want -->
<comments type="array">
<comment>
<!-- various comment fields, some of which I want -->
</comment>
<!-- possibly more comment tags -->
</comments>
</ticket>
<!-- many, many ticket tags -->
</tickets>
不是很复杂,作为一个整体它不是一个真正的文件中,<ticket>节点只是一个列表,每一类又是一个小文件,我想挑出几部分出来。我不需要做针对树的任何复杂的遍历,只是希望从每个<ticket>节点获得一些数据然后把它扔掉再读下一个。原来ElementTree的对眼前这个场景提供了一个工具:iterparse()。让我们再试一次:
import xml.etree.cElementTree as ET
for event, element in ET.iterparse('huge.xml'):
if event == 'end' and element.tag == 'ticket':
#process ticket...
…什么? !我的笔记本电脑又融化了!跟parse-the-whole-file的方法一样使用了完全相同的内存(和系统响应能力)。到底发生了什么事?
好吧,稍微google了一下,google告诉我,当iterparse()读取元素时,它仍然是在内存中建立了一个完整的文档树,就像我一开始使用parse()方法一样。几个博客和stackoverflow的答案推荐添加element.clear()方法在循环结束时清理你不需要的对象,可以限制内存消耗。我拯救了你的麻烦:它不工作。其他博客,so的答案,甚至一个IBM白皮书表明需要在循环结束时进行更彻底的清扫工作结束:
import lxml.etree as ET #the IBM piece used lxml but I tried cElementTree also
for event, element in ET.iterparse('huge.xml'):
if event == 'end' and element.tag == 'ticket':
#process ticket...
element.clear()
while elem.getprevious() is not None:
del elem.getparent()[0]
......哎呀!我溶化了另一台笔记本电脑!
为什么不工作?坦率地说,我不知道。
我稍微离题一下来说说为什么我爱Python。作为一个DBA和系统工程师,我面对着大量的一次性编程挑战。移动这个从这里到那里、Munge数据、将数据从这里迁移到哪里。这种类型的挑战是非常适合于蛮力编程解决问题的这种方式。总之,有时是不值得在建立一个优雅的、易于维护的解决方案上花费任何时间。有时候,你只需要解决这个问题,然后忘掉它。 在处理这类问题上Python最棒的,简洁的语法、良好的设计理念、丰富的库都有助于这个工具,很容易快速解决您碰到的任何问题。即使速度比同等的Java解决方案的10倍还慢,如果需要5分钟的时间写而不是5小时,我更愿意使用python,因为人类工时比CPU工时更有价值。
所有这一切都证明下述方式解决了我的问题,而不会融化的笔记本电脑:
import xml.etree.cElementTree as ET
def process_buffer(buf):
tnode = ET.fromstring(buf)
#pull it apart and stick it in the database
inputbuffer = ''
with open('huge.xml','rb') as inputfile:
append = False
for line in inputfile:
if '<ticket>' in line:
inputbuffer = line
append = True
elif '</ticket>' in line:
inputbuffer += line
append = False
process_buffer(inputbuffer)
inputbuffer = None
del inputbuffer #probably rendant...
elif append:
inputbuffer += line
不是最优雅,或有效率,或者通用的解决方案,但它可以工作。刚刚看了手边的手册,利用其结构的简单性,在解析之前根据xml文件的内容将它切成可管理的块,然后解析和处理每个块,终于可以确保不再需要更长的时间来把它全部处理完。
② python 读取CSV 文件
读取一个CSV 文件
最全的
一个简化版本
filepath_or_buffer : str,pathlib。str, pathlib.Path, py._path.local.LocalPath or any object with a read() method (such as a file handle or StringIO)
可以是URL,可用URL类型包括:http, ftp, s3和文件。对于多文件正在准备中
本地文件读取实例:://localhost/path/to/table.csv
**sep **: str, default ‘,’
指定分隔符。如果不指定参数,则会尝试使用逗号分隔。分隔符长于一个字符并且不是‘s+’,将使用python的语法分析器。并且忽略数据中的逗号。正则表达式例子:' '
**delimiter **: str, default None
定界符,备选分隔符(如果指定该参数,则sep参数失效)
delim_whitespace : boolean, default False.
指定空格(例如’ ‘或者’ ‘)是否作为分隔符使用,等效于设定sep='s+'。如果这个参数设定为Ture那么delimiter 参数失效。
在新版本0.18.1支持
header : int or list of ints, default ‘infer’
指定行数用来作为列名,数据开始行数。如果文件中没有列名,则默认为0,否则设置为None。如果明确设定header=0 就会替换掉原来存在列名。header参数可以是一个list例如:[0,1,3],这个list表示将文件中的这些行作为列标题(意味着每一列有多个标题),介于中间的行将被忽略掉。
注意:如果skip_blank_lines=True 那么header参数忽略注释行和空行,所以header=0表示第一行数据而不是文件的第一行。
**names **: array-like, default None
用于结果的列名列表,如果数据文件中没有列标题行,就需要执行header=None。默认列表中不能出现重复,除非设定参数mangle_pe_cols=True。
index_col : int or sequence or False, default None
用作行索引的列编号或者列名,如果给定一个序列则有多个行索引。
如果文件不规则,行尾有分隔符,则可以设定index_col=False 来是的pandas不适用第一列作为行索引。
usecols : array-like, default None
返回一个数据子集,该列表中的值必须可以对应到文件中的位置(数字可以对应到指定的列)或者是字符传为文件中的列名。例如:usecols有效参数可能是 [0,1,2]或者是 [‘foo’, ‘bar’, ‘baz’]。使用这个参数可以加快加载速度并降低内存消耗。
as_recarray : boolean, default False
不赞成使用:该参数会在未来版本移除。请使用pd.read_csv(...).to_records()替代。
返回一个Numpy的recarray来替代DataFrame。如果该参数设定为True。将会优先squeeze参数使用。并且行索引将不再可用,索引列也将被忽略。
**squeeze **: boolean, default False
如果文件值包含一列,则返回一个Series
**prefix **: str, default None
在没有列标题时,给列添加前缀。例如:添加‘X’ 成为 X0, X1, ...
**mangle_pe_cols **: boolean, default True
重复的列,将‘X’...’X’表示为‘X.0’...’X.N’。如果设定为false则会将所有重名列覆盖。
dtype : Type name or dict of column -> type, default None
每列数据的数据类型。例如 {‘a’: np.float64, ‘b’: np.int32}
**engine **: {‘c’, ‘python’}, optional
Parser engine to use. The C engine is faster while the python engine is currently more feature-complete.
使用的分析引擎。可以选择C或者是python。C引擎快但是Python引擎功能更加完备。
converters : dict, default None
列转换函数的字典。key可以是列名或者列的序号。
true_values : list, default None
Values to consider as True
false_values : list, default None
Values to consider as False
**skipinitialspace **: boolean, default False
忽略分隔符后的空白(默认为False,即不忽略).
skiprows : list-like or integer, default None
需要忽略的行数(从文件开始处算起),或需要跳过的行号列表(从0开始)。
skipfooter : int, default 0
从文件尾部开始忽略。 (c引擎不支持)
skip_footer : int, default 0
不推荐使用:建议使用skipfooter ,功能一样。
nrows : int, default None
需要读取的行数(从文件头开始算起)。
na_values : scalar, str, list-like, or dict, default None
一组用于替换NA/NaN的值。如果传参,需要制定特定列的空值。默认为‘1.#IND’, ‘1.#QNAN’, ‘N/A’, ‘NA’, ‘NULL’, ‘NaN’, ‘nan’`.
**keep_default_na **: bool, default True
如果指定na_values参数,并且keep_default_na=False,那么默认的NaN将被覆盖,否则添加。
**na_filter **: boolean, default True
是否检查丢失值(空字符串或者是空值)。对于大文件来说数据集中没有空值,设定na_filter=False可以提升读取速度。
verbose : boolean, default False
是否打印各种解析器的输出信息,例如:“非数值列中缺失值的数量”等。
skip_blank_lines : boolean, default True
如果为True,则跳过空行;否则记为NaN。
**parse_dates **: boolean or list of ints or names or list of lists or dict, default False
infer_datetime_format : boolean, default False
如果设定为True并且parse_dates 可用,那么pandas将尝试转换为日期类型,如果可以转换,转换方法并解析。在某些情况下会快5~10倍。
**keep_date_col **: boolean, default False
如果连接多列解析日期,则保持参与连接的列。默认为False。
date_parser : function, default None
用于解析日期的函数,默认使用dateutil.parser.parser来做转换。Pandas尝试使用三种不同的方式解析,如果遇到问题则使用下一种方式。
1.使用一个或者多个arrays(由parse_dates指定)作为参数;
2.连接指定多列字符串作为一个列作为参数;
3.每行调用一次date_parser函数来解析一个或者多个字符串(由parse_dates指定)作为参数。
**dayfirst **: boolean, default False
DD/MM格式的日期类型
**iterator **: boolean, default False
返回一个TextFileReader 对象,以便逐块处理文件。
chunksize : int, default None
文件块的大小, See IO Tools docs for more information on iterator and chunksize.
compression : {‘infer’, ‘gzip’, ‘bz2’, ‘zip’, ‘xz’, None}, default ‘infer’
直接使用磁盘上的压缩文件。如果使用infer参数,则使用 gzip, bz2, zip或者解压文件名中以‘.gz’, ‘.bz2’, ‘.zip’, or ‘xz’这些为后缀的文件,否则不解压。如果使用zip,那么ZIP包中国必须只包含一个文件。设置为None则不解压。
新版本0.18.1版本支持zip和xz解压
thousands : str, default None
千分位分割符,如“,”或者“."
decimal : str, default ‘.’
字符中的小数点 (例如:欧洲数据使用’,‘).
float_precision : string, default None
Specifies which converter the C engine should use for floating-point values. The options are None for the ordinary converter, high for the high-precision converter, and round_trip for the round-trip converter.
指定
**lineterminator **: str (length 1), default None
行分割符,只在C解析器下使用。
**quotechar **: str (length 1), optional
引号,用作标识开始和解释的字符,引号内的分割符将被忽略。
quoting : int or csv.QUOTE_* instance, default 0
控制csv中的引号常量。可选 QUOTE_MINIMAL (0), QUOTE_ALL (1), QUOTE_NONNUMERIC (2) or QUOTE_NONE (3)
doublequote : boolean, default True
双引号,当单引号已经被定义,并且quoting 参数不是QUOTE_NONE的时候,使用双引号表示引号内的元素作为一个元素使用。
escapechar : str (length 1), default None
当quoting 为QUOTE_NONE时,指定一个字符使的不受分隔符限值。
comment : str, default None
标识着多余的行不被解析。如果该字符出现在行首,这一行将被全部忽略。这个参数只能是一个字符,空行(就像skip_blank_lines=True)注释行被header和skiprows忽略一样。例如如果指定comment='#' 解析‘#empty a,b,c 1,2,3’ 以header=0 那么返回结果将是以’a,b,c'作为header。
encoding : str, default None
指定字符集类型,通常指定为'utf-8'. List of Python standard encodings
dialect : str or csv.Dialect instance, default None
如果没有指定特定的语言,如果sep大于一个字符则忽略。具体查看csv.Dialect 文档
tupleize_cols : boolean, default False
Leave a list of tuples on columns as is (default is to convert to a Multi Index on the columns)
error_bad_lines : boolean, default True
如果一行包含太多的列,那么默认不会返回DataFrame ,如果设置成false,那么会将改行剔除(只能在C解析器下使用)。
warn_bad_lines : boolean, default True
如果error_bad_lines =False,并且warn_bad_lines =True 那么所有的“bad lines”将会被输出(只能在C解析器下使用)。
**low_memory **: boolean, default True
分块加载到内存,再低内存消耗中解析。但是可能出现类型混淆。确保类型不被混淆需要设置为False。或者使用dtype 参数指定类型。注意使用chunksize 或者iterator 参数分块读入会将整个文件读入到一个Dataframe,而忽略类型(只能在C解析器中有效)
**buffer_lines **: int, default None
不推荐使用,这个参数将会在未来版本移除,因为他的值在解析器中不推荐使用
compact_ints : boolean, default False
不推荐使用,这个参数将会在未来版本移除
如果设置compact_ints=True ,那么任何有整数类型构成的列将被按照最小的整数类型存储,是否有符号将取决于use_unsigned 参数
use_unsigned : boolean, default False
不推荐使用:这个参数将会在未来版本移除
如果整数列被压缩(i.e. compact_ints=True),指定被压缩的列是有符号还是无符号的。
memory_map : boolean, default False
如果使用的文件在内存内,那么直接map文件使用。使用这种方式可以避免文件再次进行IO操作。
ref:
http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html
③ 如何在csv文件中用Python语言求没科的评论就是分
要使用 Python 计算 CSV 文件中每个科目的平均分数,您可以使用以下代码:
此代码使用该模块从 CSV 文件中读取数据并将其解析为字典。然后使用字典来跟踪每个科目的总分和分数数。从文件中读取所有数据后,代码将计算每个主题的平均分数并打印结果。csv
您可以通过更改 CSV 文件的名称以及包含主题和分数信息的列的名称来自定义此代码。如果需要,您还可以修改代码以将结果存储在不同的数据结构中,例如列表或 Pandas 数据帧。
回答不易望请采纳
④ 说说如何利用 Python 处理 CSV 文件
CSV 表示 “Comma-Separated Values (逗号分隔的值) ” , CSV 文件是简化的电子表格,实际为纯文本文件。
一个 CSV 文件,格式是这样的:
因为 CSV 文件中的每个单元格都是以逗号分割,所以也许有人会对每行文本调用 split() 方法,来解析 CSV 文件。但 CSV 文件也有自己的转义字符,通过转义字符,允许逗号和其他字符作为值的一部分,但单纯使用 split() 方法不能处理这些转义字符。因为这些潜在的缺陷,所以建议总是使用 csv 模块来读写 CSV 文件。
csv 模块是 Python 自带的,所以可以直接导入。
要使用 csv 模块从 CSV 文件中读取数据,我们需要创建一个 Reader 对象。 通过 Reader 对象,我们可以迭代遍历 CSV 文件中的每一行内容。
运行结果:
要用 csv 模块读取 CSV 文件,首先先使用 open() 函数打开它 ,就像打开任何其他文本文件一样。然后将它传递给 csv .reader() 函数。 这个函数将返回一个 Reader 对象。注意,csv .reader() 函数不接受文件名作为入参。
要访问 Reader 对象中的值,最直接的方法,就是利用 list() 将它转换成一个普通 Python 列表。它实际为一个包含列表的列表,用于表示二维数据。
我们还可以使用表达式 data [ row ][ col ] 来访问 CSV 中特定行和列的值。其中, row 是 data 中一个列表的下标, col 是该列表中,我们想访问的项的下标:
运行结果:
运行结果:
Writer 对象可以让我们把数据写入 CSV 文件。
运行结果:
在 Windows 上,需要为 open() 函数的 newline 关键字参数传入一个空字符串。如果没有设置 newline 参数, output.csv 中的行距将变为两倍,如下图所示。
如果写入的内容包含逗号,那么 csv 模块会自动加上双引号,对其进行转义,如下例所示。
运行结果:
我们也可以利用 delimiter ,来制作 TSV 文件,TSV 是Tab-separated values的缩写,即以制表符作为分隔符的文件;利用 lineterminator 参数来设定行距。
运行结果:
这里利用 lineterminator='\n\n\n' 将行与行之间的字符变为三个换行符,效果就是实现了 3 倍行距。
⑤ 【MMD】用python解析VMD格式读取
MikuMikuDance(简称MMD)是一款动画软件,早期视为Vocaload角色制作动画的软件,现在还经常能在B站等视频网站,或一些动画网站(某I站)看到MMD作品。
我在高中也简单学过操作这款软件以及PE、水杉等软件,学会了简单k帧、套动作、调渲染、加后期、压缩等技术,这与我学习计算机专业有很大的关系(虽然学校学的和这个八竿子打不着,或许我应该学美术去),现在已经分不清很多东西了,封面静画就是杂七杂八过气MME一锅扔的成果,得益于G渲的强大,还能看出一点效果。
现在我想学一些3D的开发,包括用程序读取模型、动作等,很快我就想到之前用过的MMD。
一些3D姿势估计(3D pose estimate)或许能得到骨骼位置以及PAF(骨骼间关系),但我需要知道3D动画是如何储存动作数据的,才能想到怎样将姿势估计得到的数据转化为动作数据。
因此我找了一些资料解析MMD的动作数据VMD(Vocaload Mation Data)文件,并写下这篇记录。
本文会用python解析vmd文件,并纠正上述文章的一点错误。
根据MMD的规矩,上借物表:
封面静画:
首先,vmd文件本身是一个二进制文件,里面装着类型不同的数据:uint8、uint32_t、float,甚至还有不同编码的字符串,因此我们需要二进制流读入这个文件。
vmd格式很像计算机网络的协议格式,某某位是什么含义,区别是,vmd文件的长度 理论 上是无限的,让我们来看看。
vmd的大致格式如下:
最开始的就是 头部(header) ,看到这就有十分强烈的既视感:
其中, 版本信息(VersionInformation) 长度为30,是ascii编码的字符串,翻译过来有两种,一为“Vocaloid Motion Data file”,二为“Vocaloid Motion Data 0002”,长度不足30后用 (或者说b'x00')填充。这是由于vmd版本有两种,大概是为了解决模型名称长度不足,因此后续只影响模型名称的占用长度。
模型名称(ModelName) ,是动作数据保存时用的模型的模型名,通过这个我们可以获取到那个名称,我们知道,一个动作数据想要运作起来,只要套用模型的骨骼名称是标准的模板就可以,因此我想象不出这个名称有何用处,或许某些模型带有特殊骨骼,例如翅膀之类的,这样能方便回溯?模型名称的长度根据版本而决定,version1为10,version长度为20。编码原文写的是shift-JIS,是日语编码,这样想没错,然而我试验后发现并非如此,例如经常改模型的大神 神帝宇 的模型,他的模型名称用shift-JIS为乱码,用gb2312竟然能正常读出来;还有 机动牛肉 大神的模型,他的模型名称用gb2312无法解码,用shift-JIS解码竟然是正常的简体中文???怎么做到的?
骨骼关键帧,分为两部分:骨骼关键帧数、骨骼关键帧记录:
我们可以查一下,每个骨骼关键帧的数量为111字节。
一开始还没发现,旋转坐标竟然有四个,分别为x, y, z, w,急的我去MMD里查看一下,发现和我印象中没有什么差别
为何补间曲线的类型不确定呢?上面csdn博客的教程说 “uint8_t那里有冗余,每四个只读第一个就行” 。说的没有问题,首先我们要清楚这个补间曲线坐标的含义。
我们打开MMD,读入模型,随意改变一个骨骼点,记录帧,就会发现左下角会出现补间曲线。
后面的格式与这个格式大同小异。
表情关键帧分为:表情关键帧数、表情关键帧记录:
镜头关键帧分为:镜头关键帧数、镜头关键帧记录:
距离是我们镜头与中心红点的距离,在MMD中,我们可以通过滑轮改变
Orthographic似乎是一种特殊的相机,没有近大远小的透视关系(不确定),不过在我的实验中,它一直取值为0。和上面的已透视没有关系,当取消已透视时,透视值会强制为1。
下面的骨骼追踪似乎没有记录,可能是强制转换成骨骼所在的坐标了。
后面的格式与这个格式大同小异。
表情关键帧分为:光线关键帧数、光线关键帧记录:
rgb颜色空间之[0, 1]之间的数,类似html的RGB(50%, 20%, 30%)这种表示方法,转换方式就是把RGB值分别除以256。
光线投射方向是[-1, 1]之间的小数。正所对的投射方向是坐标轴的负方向,例如将Y拉到1, 光线会从上向下投影。
我依旧会使用面向对象的方式构建VMD类,不过构造方法无力,属性太多,我选择用静态方法添加属性的方式构建对象
随意掰弯一些关节并注册、使用:
output:
因为前面提到的编码模式,我选择用gb2312解码,在很多(也许是大部分)动作数据都会报错,可以去掉编码方式:
我们没有移动方块骨骼,因此位置信息都是0。
不喜欢看欧拉角的话,可以写一个转换方法:
这样只要调用:
即可得到转换成欧拉角的结果,同样的方式还可以编写转换RGB、弧度、角度等
python内置的json包可以很方便得将字典转换成json格式文档储存。
我们也可以试着写一些将VMD转换成vmd文件的方法。
通过学习VMD的文件结构,大致了解了储存动作数据的格式和一些方法,或许可以类比到一些主流的商业3D软件上。
读取程序并不难,我写程序的很多时间都是查二进制操作消耗的,通过这个程序,还巩固了二进制操作的知识。
我在google上找到了一个包 saba ,专门用于操控MMD的文件,包括模型、动作数据等
现在学一下图形学,等学有所得再做出更多东西。
⑥ python解析xml,包含中文,gb2312编码。修改xml后重新写入xml有些内容怎么变了 我的步骤如下:
可能是python内置模块的输出是那样的,不需要用ue转,我觉得直接当文本读入内容MSG,然后解码再编码msg.decode("gb2312").encode("utf-8"),然后用正则表达式<xx>替换内容</xx>来替换内容。。。个人觉得省事些,当然要看你具体情况
⑦ 利用python实现数据分析
链接:
炼数成金:Python数据分析。Python是一种面向对象、直译式计算机程序设计语言。也是一种功能强大而完善的通用型语言,已经具有十多年的发展历史,成熟且稳定。Python 具有脚本语言中最丰富和强大的类库,足以支持绝大多数日常应用。 Python语法简捷而清晰,具有丰富和强大的类库。它常被昵称为胶水语言,它能够很轻松的把用其他语言制作的各种模块(尤其是C/C++)轻松地联结在一起。
课程将从Python的基本使用方法开始,一步步讲解,从ETL到各种数据分析方法的使用,并结合实例,让学员能从中借鉴学习。
课程目录:
Python基础
Python的概览——Python的基本介绍、安装与基本语法、变量类型与运算符
了解Python流程控制——条件、循环语句与其他语句
常用函数——函数的定义与使用方法、主要内置函数的介绍
.....
⑧ Python 自动化处理 Yaml 文件-
Yaml文件内容—示例1:
Yaml文件内容—示例2:
从上述示例文件内容可以看到 Yaml 数据结构:
1). 对象:键值对的集合(简称 "映射或字典")
例如:family 和 address 这两个对象后面分别有对应的键值对集合。
2). 键值对用冒号 “:” 结构表示,冒号与值之间需用空格分隔
例如:
family 对象中的 key 为 name 与其对应的 value 值 Smile_Family 之间是使用空格分隔的。
address 对象中的 key 为 province 与其对应的 value 值 BeiJing 之间是使用空格分隔的。
3). 数组:一组按序排列的值(简称 "序列或列表"),数组前加有 “-” 符号,符号与值之间需用空格分隔
例如:
parents 中的 John 和 Jane
children 中的 Lily 和 Frank
4). 纯量(scalars):单个的、不可再分的值。例如:字符串、bool值、整数、浮点数、时间、日期、null等
None值可用null也可用 ~ 表示;
yaml文件内容如下:
Python解析输出为:
这个例子输出一个字典,其中value包括所有基本类型
Yaml文件内容如下:
Python解析输出为:
Yaml文件内容如下:
Python输出为:
如果字符串没有空格或特殊字符,不需要加引号,但如果其中有空格或特殊字符,则需要加引号。
这里要注意单引号和双引号的区别:
单引号中的特殊字符转到 Python 会被转义,也就是到最后是原样输出;
双引号不会被 Python 转义,到最后是输出了特殊字符;
Yaml文件内容如下:
Python输出: