XML
구분된 파일은 행(줄)과 열(줄 안의 필드)의 두 차원만 전달합니다. 프로그램 간에 데이터 구조를 교환하려면 계층, 시퀀스, 세트 및 기타 구조를 텍스트로 인코딩하는 방법이 필요합니다.
XML은 법안에 가장 적합한 마크업 형식이다. 이 샘플 menu.xml 파일에서와 같이 태그를 사용하여 데이터를 구분합니다.
- <?xml version="1.0"?>
- <menu>
<breakfast hours="7-11">- <item price="$6.00">breakfast burritos</item>
- <item price="$4.00">pancakes</item>
</breakfast>
<lunch hours="11-3">
<item price="$5.00">hamburger</item>
</lunch>
<dinner hours="3-10">
<item price="8.00">spaghetti</item>
</dinner>
</menu>
다음은 XML의 몇 가지 중요한 특성입니다.
• 태그는 < 문자로 시작합니다. 이 샘플의 태그는 메뉴, 아침, 점심, 그리고
디너와 아이템.
• 공백은 무시됩니다.
• 일반적으로 <메뉴>와 같은 시작 태그 다음에 다른 내용이 나온 다음 </menu>와 같은 최종 일치 끝 태그가 뒤따른다.
• 태그는 모든 수준으로 다른 태그에 중첩될 수 있습니다. 이 예에서 항목 태그는 아침, 점심 및 저녁 태그의 하위 항목이며, 차례로 메뉴의 하위 항목입니다.
• 시작 태그 내에서 선택적 속성이 발생할 수 있습니다. 이 예에서 가격은 품목의 속성입니다.
• 태그에는 값이 포함될 수 있습니다. 이 예제에서는 두 번째 아침 식사용 팬케이크와 같은 값이 각 품목에 있습니다.
• 어떤 물건에 값이나 자식 태그가 없는 경우, 그것은 <thing>과 같이 시작 태그와 끝 태그가 아닌 <thing/>와 같은 닫힘 각도 괄호 바로 앞에 슬래시를 포함시켜 단일 태그로 표현할 수 있다.
• 데이터를 저장할 위치(속성, 가치, 하위 태그)를 선택하는 것은 다소 어렵다. 예를 들어, 마지막 항목 태그를 <품목 가격="$8.00" 식품="food"/">으로 작성할 수 있었다.
XML은 종종 데이터 피드 및 메시지에 사용되며 RSS 및 Atom과 같은 보조 형식을 가집니다. 일부 산업에는 재무 분야와 같은 많은 전문 XML 형식이 있습니다.
XML의 위버 유연성은 접근 방식과 기능이 다른 여러 파이썬 라이브러리에 영감을 주었습니다.
파이썬에서 XML을 구문 분석하는 가장 간단한 방법은 ElementTree를 사용하는 것입니다. 다음은 menu.xml 파일을 구문 분석하고 태그와 속성을 인쇄하는 데 사용할 수 있는 작은 프로그램입니다.
- >>> import xml.etree.ElementTree as et
>>> tree = et.ElementTree(file='menu.xml')
>>> root = tree.getroot()
>>> root.tag
'menu'
>>> for child in root:
... print('tag:', child.tag, 'attributes:', child.attrib)
... for grandchild in child:
... print('\ttag:', grandchild.tag, 'attributes:', grandchild.attrib) ...
tag: breakfast attributes: {'hours': '7-11'}
tag: item attributes: {'price': '$6.00'}
tag: item attributes: {'price': '$4.00'}
tag: lunch attributes: {'hours': '11-3'}
tag: item attributes: {'price': '$5.00'}
tag: dinner attributes: {'hours': '3-10'}
tag: item attributes: {'price': '8.00'} >>> len(root) # number of menu sections 3
>>> len(root[0]) # number of breakfast items 2
중첩 목록의 각 요소에 대해 태그는 태그 문자열이고 속성은 해당 특성의 사전입니다. 원소트리는 XML에서 파생된 데이터를 검색하고, 데이터를 mod if ifing하고, XML 파일을 쓰는 다른 많은 방법을 가지고 있습니다. 원소트리 설명서에 자세한 내용이 있습니다.
다른 표준 파이썬 XML 라이브러리는 다음을 포함한다:
xml.dom
JavaScript 개발자에게 익숙한 DOM(Document Object Model)은 웹 문서를 계층 구조로 나타냅니다. 이 모듈은 전체 XML 파일을 메모리에 로드하고 모든 항목에 동일하게 액세스할 수 있도록 합니다.
xml.sax
XML용 단순 API 또는 SAX는 XML을 즉시 구문 분석하므로 모든 것을 한 번에 메모리에 로드할 필요가 없습니다. 따라서 매우 큰 XML 스트림을 처리해야 하는 경우 이 방법을 선택하는 것이 좋습니다.
HTML
엄청난 양의 데이터가 웹의 기본 문서 형식인 HTML(Hypertext Markup Language)로 저장된다. 문제는 HTML 규칙을 따르지 않는 경우가 너무 많아서 구문 분석하기가 어려울 수 있다는 것입니다. 또한 HTML의 대부분은 데이터를 교환하기 보다는 출력을 포맷하기 위한 것이다. 이 장은 꽤 잘 정의된 데이터 형식을 설명하기 위한 것이기 때문에 HTML에 대한 논의를 다음포스팅에서 분리했습니다.
JSON
자바스크립트 객체 표기법(JSON)은 그것의 자바스크립트 기원을 넘어 매우 대중적인 데이터 교환 형식이 되었다. JSON 형식은 자바스크립트의 하위 집합이며 종종 합법적인 파이썬 구문이기도 하다. Python에 매우 적합하기 때문에 프로그램 간의 데이터 교환에 적합합니다. 웹 개발을 위한 JSON의 예는 다음포스팅에서 많이 볼 수 있습니다.
XML 모듈의 다양성과 달리 JSON 모듈은 단 하나이며, gettable 이름은 json입니다. 이 프로그램은 데이터를 JSON 문자열로 인코딩(덤프)하고 JSON 문자열을 데이터로 다시 디코딩(로드)합니다. 다음 예에서는 이전 XML 예제의 데이터를 포함하는 파이썬 데이터 스트럭 튜어를 구축해 보겠습니다.
>>>menu=\
... {
... "breakfast": {
...
...
...
...
...
...
... "lunch" : {
...
...
...
...
...
... "dinner": {
...
...
...
...
...
... }
.
"hours": "7-11",
"items": {
},
"breakfast burritos": "$6.00",
"pancakes": "$4.00"
}
"hours": "11-3",
"items": {
"hamburger": "$5.00"
"hours": "3-10",
"items": {
"spaghetti": "$8.00"
} }
그런 다음 덤프()를 사용하여 데이터 구조(메뉴)를 JSON 문자열(메뉴_json)로 인코딩합니다.
>>> import json
>>> menu_json = json.dumps(menu)
>>> menu_json
'{"dinner": {"items": {"spaghetti": "$8.00"}, "hours": "3-10"}, "lunch": {"items": {"hamburger": "$5.00"}, "hours": "11-3"}, "breakfast": {"items": {"breakfast burritos": "$6.00", "pancakes": "$4.00"}, "hours": "7-11"}}'
이제 로드()를 사용하여 JSON 문자열 메뉴_json을 파이썬 데이터 구조(메뉴2)로 다시 전환해 보겠습니다.
>>> menu2 = json.loads(menu_json)
>>> menu2
{'breakfast': {'items': {'breakfast burritos': '$6.00', 'pancakes':
'$4.00'}, 'hours': '7-11'}, 'lunch': {'items': {'hamburger': '$5.00'},
'hours': '11-3'}, 'dinner': {'items': {'spaghetti': '$8.00'}, 'hours': '3-10'}}
menu와 menu2는 둘 다 키와 값이 같은 사전입니다. 표준 사전에서 항상 그렇듯이 키를 받는 순서도 다릅니다.
여기에 설명된 대로 날짜/시간( 다음포스팅의 "달력 및 시계"에 자세히 나와 있음)과 같은 개체를 포함하여 일부 개체를 인코딩하거나 디코딩하는 동안 예외가 발생할 수 있습니다.
>>> import datetime
>>> now = datetime.datetime.utcnow()
>>> now
datetime.datetime(2013, 2, 22, 3, 49, 27, 483336)
>>> json.dumps(now)
Traceback (most recent call last):
# ... (deleted stack trace to save trees)
TypeError: datetime.datetime(2013, 2, 22, 3, 49, 27, 483336) is not JSON serializable
이 문제는 JSON 표준에서 날짜 또는 시간 유형을 정의하지 않기 때문에 발생할 수 있습니다. 사용자가 날짜 또는 시간 유형을 처리하는 방법을 정의해야 합니다. 날짜 시간을 문자열 또는 epoch 값과 같이 JSON이 이해하는 것으로 변환할 수 있습니다(다음포스팅에 나와 있습니다).
>>> now_str = str(now)
>>> json.dumps(now_str)
'"2013-02-22 03:49:27.483336"'
>>> from time import mktime
>>> now_epoch = int(mktime(now.timetuple())) >>> json.dumps(now_epoch)
'1361526567'
날짜/시간 값이 정상적으로 변환된 데이터 유형 중간에 발생할 수 있는 경우 이러한 특수 변환을 수행하는 것이 귀찮을 수 있습니다. 상속을 사용하여 JSON 인코딩 방법을 수정할 수 있습니다. 상속은 다음포스팅의 "상속"에 설명되어 있습니다. 파이썬의 JSON 설명서는 JSON을 데드 플레이하게 만드는 복잡한 숫자에 대한 예를 제공한다. 날짜/시간에 맞게 수정해 보겠습니다.
>>> class DTEncoder(json.JSONEncoder):
>>> json.dumps(now, cls=DTEncoder)
'1361526567'
def default(self, obj):
# isinstance() checks the type of obj if isinstance(obj, datetime.datetime):
return int(mktime(obj.timetuple()))
# else it's something the normal decoder knows: return json.JSONEncoder.default(self, obj)
...
>>> json.dumps(now, cls=DTEncoder)
'1361526567'
새 클래스 DTENCoder는 JSONENCoder의 하위 클래스 또는 하위 클래스입니다. 날짜 시간 처리를 추가하려면 기본() 메서드를 재정의하기만 하면 됩니다. 상속을 수행하면 다른 모든 항목이 상위 클래스에서 처리됩니다.
isinstance() 함수는 개체 obj가 datetime.da time 클래스인지 확인합니다. Python의 모든 것은 개체이므로 isinstance()는 어디에서나 작동합니다.
>>> type(now)
<class 'datetime.datetime'>
>>> isinstance(now, datetime.datetime) True
>>> type(234)
<class 'int'>
>>> isinstance(234, int)
True
>>> type('hey')
<class 'str'>
>>> isinstance('hey', str)
True
JSON 및 기타 구조화된 텍스트 형식의 경우 미리 구조에 대해 아무것도 알지 못한 상태에서 파일에서 데이터 구조로 로드할 수 있습니다. 그런 다음 isinstance() 및 type에 적합한 방법을 사용하여 구조를 살펴볼 수 있습니다. 예를 들어 항목 중 하나가 사전인 경우 키(), 값() 및 항목()을 통해 내용을 추출할 수 있습니다.
YAML
JSON과 마찬가지로 YAML에는 키와 값이 있지만 날짜 및 시간과 같은 더 많은 데이터 유형을 처리합니다. 표준 파이썬 라이브러리에는 아직 YAML 처리가 포함되어 있지 않으므로, YAML이라는 이름의 타사 라이브러리를 설치해야 합니다. load()는 YAML 문자열을 파이썬 데이터로 변환하지만 dump()는 반대로 변환합니다.
다음 YAML 파일인 mcintyre.yaml에는 캐나다 시인 제임스 맥킨타이어에 대한 정보가 들어 있습니다.
name:
first: James
last: McIntyre
dates:
birth: 1828-05-25
death: 1906-03-31
details:
bearded: true
themes: [cheese, Canada]
books:
url: http://www.gutenberg.org/files/36068/36068-h/36068-h.htm
poems:
- title: 'Motto'
text: |
Politeness, perseverance and pluck,
To their possessor will bring good luck.
- title: 'Canadian Charms'
text: |
Here industry is not in vain,
For we have bounteous crops of grain,
And you behold on every field
Of grass and roots abundant yield, But after all the greatest charm
Is the snug home upon the farm,
And stone walls now keep cattle warm.
true, false, on 및 off와 같은 값은 Python Booans로 변환됩니다. 정수와 문자열은 파이썬 등가물로 변환됩니다. 다른 구문은 목록 및 사전 목록을 만듭니다.
>>> import yaml
>>> with open('mcintyre.yaml', 'rt') as fin:
>>> text = fin.read()
>>> data = yaml.load(text)
>>> data['details']
{'themes': ['cheese', 'Canada'], 'bearded': True} >>> len(data['poems'])
2
생성된 데이터 구조는 YAML 파일의 구조와 일치합니다. 이 경우에는 한 단계 이상의 깊이가 있는 위치입니다. 다음 받아쓰기/목록/독재 참조와 함께 두 번째 시의 제목을 얻을 수 있습니다.
>>> data['poems'][1]['title']
'Canadian Charms'
PyYAML은 문자열에서 Python 개체를 로드할 수 있으며 이는 위험합니다. 신뢰하지 않는 YAML을 가져오는 경우 load() 대신 safe_load()를 사용합니다. 항상 safe_load()를 사용하는 것이 좋습니다. 읽기 전쟁은 Ruby on Rails 플랫폼을 어떻게 조작했는지에 대한 설명을 위한 평화입니다.
보안 노트
이 장에 설명된 모든 형식을 사용하여 객체를 파일에 저장한 후 다시 읽을 수 있습니다. 이 프로세스를 악용하여 보안 문제를 일으킬 수 있습니다.
예를 들어, 위키피디아의 10억 likes 페이지에서 다음 XML 조각은 10개의 중첩된 엔터티를 정의하며, 각각 하위 수준을 10배 확장하여 총 10억 개의 확장을 수행합니다.
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
나쁜 소식은: 10억 번의 웃음이 이전 섹션에서 언급된 모든 XML 라이브러리를 날려버릴 것이라는 것이다. 해제된 XML은 이 공격 및 기타 공격과 파이썬 라이브러리의 취약성을 나열합니다. 링크에는 이러한 문제를 방지하기 위해 여러 라이브러리에 대한 설정을 변경하는 방법이 나와 있습니다. 또한 disusedxml 라이브러리를 다른 라이브러리의 보안 프런트엔드로 사용할 수 있습니다.
>>> # insecure:
>>> from xml.etree.ElementTree import parse >>> et = parse(xmlfile)
>>> # protected:
>>> from defusedxml.ElementTree import parse >>> et = parse(xmlfile)
구성 파일
대부분의 프로그램은 다양한 옵션 또는 설정을 제공합니다. 역동적인 것은 그램의 주장으로 제공될 수 있지만, 오래 지속되는 것은 어딘가에 보관될 필요가 있다. 빠르고 더러운 구성 파일 형식을 정의하려는 유혹이 강하지만 저항합니다. 그것은 종종 더러워지지만 그렇게 빠르지는 않다. 작성자 프로그램과 독서자 프로그램(파서라고도 함)을 모두 유지해야 합니다. 이전 섹션에 있는 것을 포함하여 프로그램에 참여할 수 있는 좋은 대안이 있습니다.
여기서는 Windows 스타일의 .ini 파일을 처리하는 표준 구성 파서 모듈을 사용합니다. 이러한 파일에는 키 = 값 정의 섹션이 있습니다. 다음은 최소 설정.cfg 파일입니다.
[english]
greeting = Hello
[french]
greeting = Bonjour
[files]
home = /usr/local
# simple interpolation: bin = %(home)s/bin
다음은 파이썬 데이터 구조로 읽는 코드입니다.
>>> import configparser
>>> cfg = configparser.ConfigParser()
>>> cfg.read('settings.cfg')
['settings.cfg']
>>> cfg
<configparser.ConfigParser object at 0x1006be4d0> >>> cfg['french']
<Section: french>
>>> cfg['french']['greeting']
'Bonjour'
>>> cfg['files']['bin']
'/usr/local/bin'
펑시어 보간법을 포함한 다른 옵션을 사용할 수 있습니다. 구성 도구 문서를 참조하십시오. 두 수준보다 깊은 내포가 필요한 경우 YAML 또는 JSON을 사용해 보십시오.
기타 교환 형식
이러한 이진 데이터 교환 형식은 일반적으로 XML이나 JSON보다 더 작고 빠릅니다.
• MsgPack
• Protocol Buffers
• Avro
• Thrift
이진수이기 때문에 텍스트 편집기를 사용하는 사람이 쉽게 편집할 수 있는 것은 없습니다.