Jak mogę przeanalizować plik YAML w Pythonie

611

Jak parsować plik YAML w Pythonie?

Szymon Lipiński
źródło

Odpowiedzi:

805

Najłatwiejszą i najczystszą metodą bez polegania na nagłówkach C jest PyYaml ( dokumentacja ), którą można zainstalować za pomocą pip install pyyaml:

#!/usr/bin/env python

import yaml

with open("example.yaml", 'r') as stream:
    try:
        print(yaml.safe_load(stream))
    except yaml.YAMLError as exc:
        print(exc)

I to wszystko. Istnieje yaml.load()również prosta funkcja, ale yaml.safe_load()zawsze powinna być preferowana, chyba że jawnie potrzebujesz dostarczonej serializacji / deserializacji dowolnego obiektu, aby uniknąć wprowadzenia możliwości wykonania dowolnego kodu.

Uwaga: projekt PyYaml obsługuje wersje do specyfikacji YAML 1.1 . Jeśli wymagana jest obsługa specyfikacji YAML 1.2 , zobacz ruamel.yaml, jak wspomniano w tej odpowiedzi .

Jon
źródło
96
Dodam, że jeśli nie chcesz serializować / deserializować dowolnych obiektów, lepiej jest użyć, yaml.safe_loadponieważ nie może wykonać dowolnego kodu z pliku YAML.
ternaryOperator
4
Yaml yaml = new Yaml (); Obiekt obj = yaml.load ("a: 1 \ nb: 2 \ nc: \ n - aaa \ n - bbb");
MayTheSchwartzBeWithYou
2
Podoba mi się artykuł autorstwa łosia: martin-thoma.com/configuration-files-in-python
SaurabhM
4
Być może trzeba najpierw zainstalować pakiet PyYAML pip install pyyaml, zobacz ten post, aby uzyskać więcej opcji stackoverflow.com/questions/14261614/…
Romain
7
Jaki jest sens uchwycenia wyjątku w tym przykładzie? W każdym razie wydrukuje się, a to tylko bardziej skomplikuje przykład.
naught101
116

Odczytywanie i zapisywanie plików YAML za pomocą Python 2 + 3 (i Unicode)

# -*- coding: utf-8 -*-
import yaml
import io

# Define data
data = {
    'a list': [
        1, 
        42, 
        3.141, 
        1337, 
        'help', 
        u'€'
    ],
    'a string': 'bla',
    'another dict': {
        'foo': 'bar',
        'key': 'value',
        'the answer': 42
    }
}

# Write YAML file
with io.open('data.yaml', 'w', encoding='utf8') as outfile:
    yaml.dump(data, outfile, default_flow_style=False, allow_unicode=True)

# Read YAML file
with open("data.yaml", 'r') as stream:
    data_loaded = yaml.safe_load(stream)

print(data == data_loaded)

Utworzono plik YAML

a list:
- 1
- 42
- 3.141
- 1337
- help
- 
a string: bla
another dict:
  foo: bar
  key: value
  the answer: 42

Typowe zakończenia plików

.yml i .yaml

Alternatywy

W przypadku aplikacji ważne mogą być:

  • Wsparcie z innych języków programowania
  • Wydajność czytania / pisania
  • Kompaktowość (rozmiar pliku)

Zobacz także: Porównanie formatów serializacji danych

Jeśli szukasz sposobu na utworzenie plików konfiguracyjnych, możesz przeczytać mój krótki artykuł Pliki konfiguracyjne w Pythonie

Martin Thoma
źródło
Moje wyniki w systemie Windows to €. Czy ktoś zna przyczynę?
Cloud Cho
Jakie kodowanie ma plik? Jesteś pewien, że jest zakodowany w UTF-8?
Martin Thoma,
1
Dziękuję za sugestie. Mój plik ma kodowanie utf-8. Musiałem zmienić linię kodu, io.open(doc_name, 'r', encoding='utf8')aby odczytać znak specjalny. Wersja YAML 0.1.7
Cloud Cho
Ciekawe. Spróbuję to odtworzyć jutro i jeśli to możliwe, dostosuję pytanie. Dziękuję Ci!
Martin Thoma,
1
Możesz użyć wbudowanego open(doc_name, ..., encodung='utf8')do odczytu i zapisu, bez importowania io.
dexteritas
61

Jeśli masz YAML, który jest zgodny ze specyfikacją YAML 1.2 (wydaną w 2009 r.), Powinieneś użyć ruamel.yaml (zrzeczenie się: Jestem autorem tego pakietu). Jest to w zasadzie nadzbiór PyYAML, który obsługuje większość YAML 1.1 (od 2005).

Jeśli chcesz zachować swoje komentarze podczas podróży w obie strony, z pewnością powinieneś użyć ruamel.yaml.

Aktualizacja przykładu @ Jona jest łatwa:

import ruamel.yaml as yaml

with open("example.yaml") as stream:
    try:
        print(yaml.safe_load(stream))
    except yaml.YAMLError as exc:
        print(exc)

Używaj, safe_load()chyba że naprawdę masz pełną kontrolę nad danymi wejściowymi, potrzebujesz ich (rzadko w przypadku) i wiesz, co robisz.

Jeśli używasz pathlib Pathdo manipulowania plikami, lepiej jest użyć nowego interfejsu API ruamel.yaml zapewnia:

from ruamel.yaml import YAML
from pathlib import Path

path = Path('example.yaml')
yaml = YAML(typ='safe')
data = yaml.load(path)
Anthon
źródło
Cześć @Anthon. Korzystałem z Ruamela, ale wystąpił problem z dokumentami, które nie są zgodne z ASCII ( UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 926: ordinal not in range(128)). Próbowałem ustawić yaml.encoding na utf-8, ale nie działałem, ponieważ metoda ładowania w YAML nadal używa ascii_decode. Czy to błąd?
SnwBr
27

Najpierw zainstaluj pyyaml ​​za pomocą pip3.

Następnie zaimportuj moduł yaml i załaduj plik do słownika o nazwie „my_dict”:

import yaml
with open('filename.yaml') as f:
    my_dict = yaml.safe_load(f)

To wszystko czego potrzebujesz. Teraz cały plik yaml znajduje się w słowniku „my_dict”.

Kumpel
źródło
6
Czy to zamyka uchwyt pliku?
yangmillstheory
2
Jeśli plik zawiera wiersz „- hello world”, niewłaściwe jest wywoływanie zmiennej my_dict, ponieważ będzie ona zawierać listę. Jeśli plik ten zawiera określone tagi (zaczynając od !!python), może być również niebezpieczny (jak w przypadku całkowitego wyczyszczenia dysku twardego) yaml.load(). Ponieważ jest to wyraźnie udokumentowane, powinieneś powtórzyć to ostrzeżenie tutaj (w prawie wszystkich przypadkach yaml.safe_load()można użyć).
Anthon
4
Używasz import yaml, ale to nie jest wbudowany moduł i nie określasz, który to pakiet. Uruchomienie import yamlnowej instalacji Python3 powodujeModuleNotFoundError: No module named 'yaml'
cowlinator
11

Przykład:


defaults.yaml

url: https://www.google.com

environment.py

from ruamel import yaml

data = yaml.safe_load(open('defaults.yaml'))
data['url']
Prashanth Sams
źródło
czy warto nie zamykać strumienia?
qrtLs
3

Używam ruamel.yaml . Szczegóły i debata tutaj .

from ruamel import yaml

with open(filename, 'r') as fp:
    read_data = yaml.load(fp)

Użycie ruamel.yaml jest kompatybilne (z pewnymi prostymi rozwiązalnymi problemami) ze starymi zastosowaniami PyYAML i jak podano w linku, który podałem , użyj

from ruamel import yaml

zamiast

import yaml

i naprawi większość twoich problemów.

EDYCJA : PyYAML nie jest martwy, jak się okazuje, po prostu jest trzymany w innym miejscu.

Oleksandr Zelentsov
źródło
@Oleksander: PyYaml zobowiązuje się w ciągu ostatnich 7 miesięcy, a ostatni zamknięty problem miał miejsce 12 dni temu. Czy potrafisz zdefiniować „dawno nie żyje”?
abalter
@abalter Przepraszam, wygląda na to, że otrzymałem informacje z ich oficjalnej strony lub posta tutaj stackoverflow.com/a/36760452/5510526
Oleksandr Zelentsov
@OleksandrZelentsov Widzę zamieszanie. Był martwy, gdy był martwy. github.com/yaml/pyyaml/graphs/contributors . Jednak ich witryna jest wyświetlana i pokazuje komunikaty opublikowane PO poście SO odnoszącym się do śmierci PyYaml. Można więc powiedzieć, że w tym momencie wciąż żyje, chociaż jego kierunek w stosunku do ruamelu jest wyraźnie niepewny. Również odbyła się tutaj długa dyskusja z ostatnimi postami. Dodałem komentarz, a teraz mój jest jedyny. Chyba nie rozumiem, jak działają zamknięte problemy. github.com/yaml/pyyaml/issues/145
abalter
@abalter FWIW, kiedy ta odpowiedź została opublikowana, w przeszłości było w sumie 9 zatwierdzeń ... niecałe 7 lat. Jednym z nich była zautomatyzowana „poprawka” złej gramatyki. Dwie dotyczyły wydania ledwo zmienionej nowej wersji. Resztę stanowiły stosunkowo drobne poprawki, wykonane głównie pięć lat przed odpowiedzią. Wszystkie oprócz automatycznej poprawki zostały wykonane przez jedną osobę. Nie oceniłbym surowo tej odpowiedzi za to, że nazwałem PyYAML „dawno zmarłym”.
Pozew Fund Moniki w dniu
-1
#!/usr/bin/env python

import sys
import yaml

def main(argv):

    with open(argv[0]) as stream:
        try:
            #print(yaml.load(stream))
            return 0
        except yaml.YAMLError as exc:
            print(exc)
            return 1

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))
Wojciech Sciesiński
źródło
1
Ten kod nic nie robi. Czy chciałeś skomentować kod?
cowlinator