是否可以使用PyYAML读取包含“YAML前言”块的文本文件?

4 投票
2 回答
5263 浏览
提问于 2025-04-21 08:56

抱歉,我对YAML和PyYAML了解得不多,但我很喜欢用“Jekyll”风格的配置文件的想法。Jekyll有一些叫做“YAML Front Matter”的块,看起来非常酷炫。
所以我在我的电脑上安装了PyYAML,并写了一个小文件,里面有这样的文本块:

---
First Name: John
Second Name: Doe
Born: Yes
---

Lorem ipsum dolor sit amet, consectetur adipiscing elit,  
sed do eiusmod tempor incididunt ut labore et dolore magna  
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco 
laboris nisi ut aliquip ex ea commodo consequat.

然后我尝试用Python 3.4和PyYAML来读取这个文本文件,使用了以下代码:

import yaml

stream = open("test.yaml")
a = stream.read()
b = yaml.load(a)

但显然这并没有成功,Python显示了这个错误信息:

Traceback (most recent call last):
  File "<pyshell#62>", line 1, in <module>
    b = yaml.load(a)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/yaml/__init__.py", line 72, in load
    return loader.get_single_data()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/yaml/constructor.py", line 35, in get_single_data
    node = self.get_single_node()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/yaml/composer.py", line 43, in get_single_node
    event.start_mark)
yaml.composer.ComposerError: expected a single document in the stream
  in "<unicode string>", line 2, column 1:
    First Name: John
    ^
but found another document
  in "<unicode string>", line 5, column 1:
    ---
    ^

你能帮我一下吗?
我写的代码是不是有问题,还是说PyYAML根本无法处理YAML前置块?
还有其他方法可以用PyYAML实现,还是我得自己用正则表达式写一个解析器?

非常感谢你的时间!

2 个回答

6

你可以通过调用 yaml.load_all() 来实现这个功能,而不需要任何自定义解析。这会返回一个生成器,生成器的第一个项目是你想要的前置信息(front matter),以字典的形式呈现,第二个项目则是文档的其余部分,以字符串形式呈现:

import yaml

with open('some-file-with-front-matter.md') as f:
    front_matter, content = list(yaml.load_all(f, Loader=yaml.FullLoader))[:2]

如果你只想要前置信息,那就更简单了:

import yaml

with open('some-file-with-front-matter.md') as f:
    front_matter = next(yaml.load_all(f, Loader=yaml.FullLoader))

之所以这样有效,是因为 yaml.load_all() 是用来加载同一个文档中的多个 YAML 文档,这些文档之间用 --- 来分隔。此外,确保在从不明来源加载 YAML 时采取常规的安全措施。

编辑: 更新了代码,加入了一个 Loader 参数 这是现在必须的,并更新了文档链接。同时也确认了代码在内容中包含 --- 时仍然有效,正如下面的评论所提到的。

10

Python的yaml库不支持直接读取嵌入在文档中的yaml内容。这里有一个工具函数,可以提取出yaml文本,这样你就可以在读取文件的其他部分之前先解析它:

#!/usr/bin/python2.7

import yaml
import sys

def get_yaml(f):
  pointer = f.tell()
  if f.readline() != '---\n':
    f.seek(pointer)
    return ''
  readline = iter(f.readline, '')
  readline = iter(readline.next, '---\n')
  return ''.join(readline)


for filename in sys.argv[1:]:
  with open(filename) as f:
    config = yaml.load(get_yaml(f))
    text = f.read()
    print "TEXT from", filename
    print text
    print "CONFIG from", filename
    print config

撰写回答