🍋 eXtensible Markup Language은 데이터 저장 및 전달을 위해 만든 다목적마크업 언어 Markup Language 📌 마크업 언어는 일반적인 텍스트와 구분되는 태그 Tag를 이용해 문서나 데이터를 구조화 하는 언어
📌 JSON과 마찬가지로 데이터 전달을 목적으로 만든 구조화된 텍스트 형식 📌 대표적인 마크업 언어로는 HTML HyperText Markup Language
🍋 HTML의 경우는 태그가 미리 정해져 있지만 XML은 자신이 태그를 정의해서 사용할 수 있음 ➡️ 단 XML 문서의 규칙을 따라야 함
🍋 태그는 '<문자열>'로 시작해서 '</문자열>'로 끝나야 한다 📌 시작과 끝 태그의 문자열은 같아야 하며 대소문자를 구분
📌 중첩해 여러 개를 이용할 수 있는데 이때 태그는 반드시 올바른 순서대로 이용해야 함
ex. '<abc><def> ~ </def></abc>' 와 같이 나중에 나온 시작 태그에 대응하는 끝 태그가 먼저 나와야 함
📌 XML 문서에서는 시작 태그에서 끝 태그까지로 이루어진 것을 요소 element라고 함
🍋 XML 문서에는 반드시 최상위 root 요소가 있어야 함
📌 최상위 요소는 시작과 끝 태그로 다른 모든 요소를 감싸야 함
🍋 주석은 '<!--'와 '-->'로 문자열을 감싸서 표시. 즉 '<!-- 이것은 주석입니다 -->'와 같이 사용
🍋 xml을 지정하는 모듈은 내장 모듈로 xml이 있고, 외부 모듈의 경우 xmltodict 사용 📌 xml 데이터를 파이썬의 딕셔너리 타입으로 바로 변환
🍋 xmltodict 라이브러리에서 XML 형식의 데이터를 파이썬의 딕셔너리 타입으로 변환하는 함수 📌 xmltodict.parse(xml_input, [ xml_attribs=True 혹은 False ]) 📌 xml_input은 XML 타입의 데이터 📌 xml_attribs은 기본 값은 True ➡️ False 이면 XML 형식의 데이터를 딕셔너리 타입으로 변환할 때 속성을 무시
🍋 pprint.pprint() 함수를 사용하면 내용이 들여쓰기 된 상태로 보기좋게 출력 가능
import xmltodict
import pprint
with open('../input_2/data.xml', 'r', encoding='utf-8') as xml_file:
dict_data = xmltodict.parse(xml_file.read(), xml_attribs=True) # xml 데이터를 딕셔너리로 변경
#print(dict_data)
#print(dict_data['response']['body']['items'])
datas = dict_data['response']['body']['items']
pprint.pprint(datas)
# 2. 필요한 데이터 추출하기
data_list = dict_data['rss']['channel']['item']
new_datas:list[dict] = list() # 필요 내용만 담을 리스트 필요
for k in data_list:
new_data = dict()
new_data['title'] = k['title']
new_data['link'] = k['link']
new_datas.append(new_data)
pprint.pprint(new_datas)
실행 결과
# 3. 현재 날짜로 파일명 지정해서 저장하기
today = datetime.now().strftime('%y%m%d') # 현재 날짜 구하기
print(today)
with open(f'../output_02/daeguoperahouse_{today}.csv', 'wt', newline='', encoding='utf-8') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=['title', 'link'])
writer.writeheader()
for k in new_datas:
writer.writerow(k)
print('파일 생성을 성공했습니다.')
📌 대구에 있는 측정소의 측정소 명, 측정소 주소, 위도, 경도, 설치년도를 csv 파일로 저장
import requests
import json
# 1. 초기값 설정
# 1) 서비스 키: requests 사용시 자동으로 인코딩되어 decoding된 키를 사용
service_key:str = 'uAhO32pV0qa7BDOmkJLhw2ZyOB9i5bGj7cMN8cuuHmKIwyyf29lHLjym8hBXdIaXyvAI1IyLvQZopk5HW233qQ=='
# 2) url 설정
url = 'http://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList'
parameter = f'?serviceKey={service_key}&returnType=json&numOfRows=100&pageNo=1&addr=대구'
print(url+parameter)
일반 인증키를 서비스 키로 가져온다
사이트에 올라와 있는 zip 파일에 서버에 소스 요청할 때 요구하는 메세지 형식이 나와있다.
# 2. 서버에서 요청값을 받은 후 파싱(parsing)(= 구문 분석)
# 딕셔너리 데이터를 분석해서 원하는 값을 추출
response = requests.get(url + parameter)
# requests.get() 메서드를 사용하여 url 주소로 GET 요청을 보내고, 응답을 response 변수에 저장
json_data = response.text
# head 값 외 text 값만 들고오기 위함
dict_data = json.loads(json_data)
# JSON 형식의 데이터를 파이썬 객체로 변환하여 data 변수에 저장
print(dict_data)
# 3. 필요 정보 추출
count_datas = dict_data['response']['body']['items']
#print(count_datas)
for k in range(len(count_datas)):
count_data = count_datas[k]
if count_data['addr'][0:2] == "대구":
print(f'측정소 명 : {count_data["stationName"]}')
print(f'측정소 주소 : {count_data["addr"]}')
print(f'위도 : {count_data["dmX"]}')
print(f'경도 : {count_data["dmY"]}')
print(f'설치년도 : {count_data["year"]}')
print('=' * 20)
'''
실행결과)
측정소 명 : 내당동
측정소 주소 : 대구광역시 서구 서대구로3길 46 내당4동 행정복지센터 3층 옥상 (내당동)
위도 : 35.859
경도 : 128.55183
설치년도 : 2020
====================
측정소 명 : 침산동
측정소 주소 : 대구광역시 북구 옥산로17길 21 대구일중학교 (침산동)
위도 : 35.88761
경도 : 128.58434
설치년도 : 2020
... 생략...
'''
# 4. csv 파일 저장
import csv
data_list: list[str] = ['측정소 명', '측정소 주소', '위도', '경도', '설치년도']
daegu_list:list[dict] = list()
for k in range(len(count_datas)):
count_data = count_datas[k]
if count_data['addr'][0:2] == "대구":
new_data: dict = dict()
new_data[data_list[0]] = count_data["stationName"]
new_data[data_list[1]] = count_data["addr"]
new_data[data_list[2]] = count_data["dmX"]
new_data[data_list[3]] = count_data["dmY"]
new_data[data_list[4]] = count_data["year"]
daegu_list.append(new_data)
with open('../output_02/daegu_air_list.csv', 'w', newline='', encoding='UTF-8') as csvfile:
csv_writer = csv.DictWriter(csvfile, data_list)
csv_writer.writeheader()
for data in daegu_list:
data:dict
csv_writer.writerow(data)
print(f'파일이 생성되었습니다.')
🚀 test, tom ➡️ 인덱스를 사용하는 것처럼 위치값으로 파악 🚀 빈값이나, 값에 구분자가 들어가는 경우 어려움등의 문제로 사용 빈도 낮음
2) xml 방식
🚀 <id>test</id> <name>tom</name> ➡️ 마크업 방식, 속성값이 반복되어서 상대적으로 데이터의 크기가 커짐
3) json 방식
🚀 'id': 'test', 'name': 'tom' ➡️ 객체 속성 표현 방식. 상대적으로 간결한 표현
2. JSON 파일 입출력
1) JSON 파일이란?
👾 JavaScript Object Notation의 약자로JavaScript의 객체 표현 방법이라는 뜻
👾 데이터를 전달할 때 사람이 읽고 쓰기 쉽도록텍스트 형식을 정해 놓은 개방형 데이터 표준 형식 👾 많은 양의 데이터를 처리할 때는 속도가 느린 단점이 있으므로 경량의 데이터를 주고 받을 때 주로 사용
2) JSON 데이터 형식
👾 속성 attribute - 값 value 쌍으로 구성된 데이터들이 순서없이 모여 있는 구조 ⚡️ 중괄호를 이용해서 전체 JSON 데이터를 묶어 주고 속성과 값 사이에 콜론(:)을 붙여 줌 ⚡️ 속성과 값으로 구성된 각 데이터의 구분은 쉼표(,)를 이용 ∴ 즉, 파이썬의 딕셔너리와 같은 모습
3) JSON 파일 생성
👾 JSON 데이터 처리를 위해서json 모듈 제공 ⚡️ json 모듈을 import 하면 리스트나 딕셔너리와 같은 파이썬 객체를 JSON 데이터로 변환하거나, JSON 데이터를 다시 파이썬 객체로 변환할 수 있음
👾 JSON 인코딩 encoding : 파이썬 객체를 JSON 문자열로 변환하는 것 ⚡️ 변환된 문자열을 파일에 기록하면 확장자가 json인 JSON 파일 (*.json)을 만들 수 있음 ⚡️ json.dumps() 메소드를 이용해서 JSON 인코딩을 처리할 수 있음
📁 json 파일을 read() 메소드로 한 번에 전체를 읽어 들여 json_reader에 저장 📁 json.loads(json_reader)를 통해서 디코딩이 이뤄지면 파이썬 객체인 dict_list가 생성 📁 dict_list는 리스트 내부에 2개의 딕셔너리가 저장된 구조이기 때문에 for 문을 이용해서 그 내용을 모두 처리 가능
3. JSON과 API
🚀 JSON은 데이터를 사람이 읽을 수 있는 문자열(바이너리가 아니라 텍스트)로 나타내는 방식 🚀 많은 웹 사이트에서 프로그램이 웹 사이트와 상호 작용하는 데 JSON 형식의 내용을 제공 ➡️ 이를 API 제공이라고 하고, API로 접근하는 것은 URL을 통해 일반 웹 페이지에 접근하는 것과 동일 ➡️ 차이점은 API가 반환하는 값은 기계를 위한 JSON 형식으로 되어 있음
🚀 원하는 데이터를 얻으려면 프로그램이 요청해야 할 URL 뿐만 아니라 반환되는 JSON 데이터 구조의 일반적인 형식에 대한 문서도 찾아 봐야 되는데, API를 제공하는 모든 사이트는 이 문서를 제공
1) JSON 모듈
👾 JSON 모듈은 json.loads(), json.dumps() 함수로 JSON 데이터와 파이썬 값을 서로 변환하는 모든 세부 사항을 처리 👾 JSON은 문자열, 정수, 부동 소수점 수, 불, 리스트(배열) '[]', 딕셔너리(객체) '{}', NoneType 자료형 값만 저장 가능
import json
2) loads() 함수를 사용하여 JSON 읽기
👾 JSON 데이터가 들어 있는 문자열을 파이썬 값으로 변환하려면, 이를 json.loads() 함수에 전달 👾 JSON 문자열은 큰 따옴표를 사용
🚀 Comma Separated Values의 약자로 '콤마로 분리한 값들' 🚀 데이터베이스나 스프레드시트 데이터를 저장하기 위해서 사용하는 형식
⚡️ 내부는 실제 콤마(,)를 이용해서 모든 항목이 구분되어 있으며
콤마로 구성된 각 항목들이 테이블을 구성하는 각각의 데이터가 되는 방식 ⚡️ 메모장에서는 텍스트로, 엑셀에서는 각 셀로 나누어서 보임
텍스트 파일엑셀 파일
🚀 utf-8 형식으로 저장된 csv의 경우, 엑셀에서는 기본읽기로는 한글이 깨지는 경우가 있음 ➡️ 한컴 cell에서는 에러없이 잘 열림
1) 단점 ( 엑셀 형식의 파일과 비교했을 때 )
a. 값에 유형이 없음. 모든 것은 다 문자열 b. 글꼴 크기나 색상을 설정할 수 없음 c. 여러 개의 워크시트를 가질 수 없음 d. 셀의 너비나 높이를 지정할 수 없음 e. 셀을 병합할 수 없음 f. 그림이나 차트를 포함할 수 없음
2) 장점
a. 단순함 b. 많은 프로그램에서 지원을 하고, 텍스트 편집기에서 볼 수 있으며, 스프레드 시트 데이터를 표현하는 간단한 방법
3) 주의점
⚡️ 텍스트로 구성되어 있어서, 각 줄에 대해 split(',')을 호출하여 쉼표로 구분된 값에서 문자열 리스트를 얻을 수 있음 ➡️ csv 파일의 모든 쉼표가 두 셀의 경계를 나타내지 않고, 값의 일부인 경우도 있음 ➡️ 이런 잠재적인 문제 때문에 csv 파일을 읽거나 쓸 때 csv 모듈을 사용하는 것이 좋음
2. CSV 파일 읽기
🚀 csv 파일은 쉼표(,)로 데이터가 구분되어 있어서 문자열 처리 메소드를 활용하면 별도의 외부 모듈이 없이도 충분히 읽을 수 있음 🚀 한 줄에 한 데이터가 있기 때문에 readline() 메소드로 한 줄씩 읽음 🚀 콤마(,)로 분리하기 위해서 split() 메소드를 이용
학생명단.csv
student_list = []
with open('../input_2/학생명단.csv', 'rt', encoding='cp949') as file:
file.readline() # 학번, 이름, 주소, 연락처
# readlines()는 한꺼번에 모두 읽고 커서가 맨 끝에 있어 더 이상 읽을 게 없어짐
while True:
line = file.readline()
if not line: # 더 이상 읽을 내용이 없으면 반복문을 빠져 나옴.
break
student = line.split(',') # 안 넣으면 하나의 문자열로 생성되기 때문
student_list.append(student)
print(student_list) # 리스트 안에 리스트가 들어가 있는 형태
'''
[['10101', '김승별', '서울시 영등포구', '010-1111-1111\n'],
['10102', '박나라', '서울시 여의도구', '010-2222-2222\n'],
['10103', '최태욱', '서울시 강남구', '010-3333-3333\n'],
['10104', '민기홍', '인천시 계양구', '010-4444-4444\n'],
['10105', '이명숙', '경기도 과천시', '010-5555-5555\n']]
'''
📌 csv 파일을 사용하다 보면 간혹 큰 따옴표(")를 이용해서 텍스트를 묶는 경우가 있음 ➡️ 큰 따옴표를 제거하기 위해서 회원명에 추가로 strip() 메소드를 사용
회원명단.csv
member_list = []
with open('../input_2/회원명단.csv', 'rt', encoding='cp949') as file:
file.readline()
while True:
line = file.readline()
if not line:
break
member = line.split(',')
member[0] = member[0].strip('"') # 큰 따옴표를 제거
member_list.append(member)
print(member_list)
'''
[['강나라', '필라테스', '25일\n'], ['나유라', '수영', '25일\n'], ['이상기', '헬스', '15일\n']]
'''
👾 member[0]에는 큰 따옴표가 포함된 회원명이 저장되어 있기 때문에 member[0].strip('"')으로 큰 따옴표를 제거
📌 인코딩 시 영어는 문제가 없는데 한글의 경우 표현하는 방식이 2가지 - cp949 : 윈도우의 기본 인코딩. 예전 방식. 한글에만 특수화된 한국에서만 사용. 모든 한글을 표현하지 못함. - uft-8 : 파이참의 기본 인코딩. 상대적으로 새로운 방식. 한글 및 기타 외국어 문자를 하나의 인코딩으로 모두 표현하기 개발
3. CSV 모듈
1) csv 모듈로 csv 파일 생성하기
🚀 import csv 를 통해서 csv 파일을 쉽게 처리할 수 있는 csv 모듈을 사용
import csv
# w모드로 열고 생성된 파일 객체를 csv.writer() 메소드에 전달
# 그러면 csv 파일을 생성할 수 있는 객체가 생성되는데 이 객체를 이용해서
# writerow() 메소드를 호출하면 csv 파일에 데이터를 저장할 수 있음
with open('../output_02/차량관리_01.csv', 'w', newline='', encoding='cp949') as file:
# delimiter=',' : ,콤마로 데이터 구분. tab을 사용하는 경우도 있음.tsv.
csv_maker = csv.writer(file, delimiter=',')
csv_maker.writerow([1, '08러1234', '2020-10-20,14:00'])
csv_maker.writerow([2, '25다1234', '2020-10-20,14:10'])
csv_maker.writerow([3, '28하1234', '2020-10-20,14:20'])
print('차량관리_01.csv 파일이 생성되었습니다.')
2) csv 모듈로 csv 파일 읽기
🚀 csv 파일을 읽기 위해서는 r 모드로 파일을 열고 생성된 파일 객체를 csv.reader() 메소드에 전달 🚀 csv.reader() 메소드는 csv 파일을 읽고 그 내용을 읽고 저장한 객체 iterator를 반환
with open('../output_02/차량관리_01.csv', 'r', newline='', encoding='cp949') as file:
csv_reader = csv.reader(file, delimiter=',')
for line in csv_reader:
print(line)
'''
['1', '08러1234', '2020-10-20,14:00']
['2', '25다1234', '2020-10-20,14:10']
['3', '28하1234', '2020-10-20,14:20']
'''
차량관리_01.csv
3) reader 객체
🚀 csv 모듈은 별도의 설치없이 사용가능
🚀 csv 모듈을 사용해서 csv 파일을 읽으려면 다른 텍스트 파일처럼 open() 함수로 파일을 열어야 함
import csv
example_file = open('../input_2/example.csv', encoding='cp949') # mode를 생략하면 rt가 기본값
# read() 대신 csv.reader() 함수에 전달. reader() 객체가 반환.
example_reader = csv.reader(example_file)
# list로 변환하여 값에 접근.
print(example_reader) # <_csv.reader object at 0x100eadcb0>
example_data = list(example_reader)
print(example_data)
print(example_data[0][1]) # Apples
example_file.close() # with 사용한 것이 아니기 때문에 닫아줘야 함
4) for 반복문을 활용해 reader 객체로부터 데이터 읽기
🚀 csv 파일이 큰 경우에는 전체 파일을 한 번에 메모리에 로드하지 않고 for 반복문에서 reader 객체 사용 🚀 reader 객체는 한 번만 사용가능하기 때문에 다시 사용하려면 새로 reader 객체 생성
example_file = open('../input_2/example.csv', encoding='cp949')
example_reader = csv.reader(example_file)
print('example.csv 출력')
for row in example_reader:
# 각 행의 번호를 얻으려면 reader 객체의 line_num 변수를 사용
print(f'Row #{str(example_reader.line_num)} {str(row)} {str(row[0])}')
example_file.close()
print('=' * 20)
🚀 탭문자로 셀을 구분하고 줄 간격을 한 줄씩 띄우려는 상황이라면 구분자(delimiter)와 줄 끝 문자(lineterminator)를 변경 🚀 delimiter의 기본값은 쉼표이고, lineterminator의 기본값은 개행문자 🚀 셀들이 탭으로 구분되어 있기 때문에 탭으로 구분된 값을 의미하는 .tsv 파일 확장자 사용
# mutable
# 할당받는 메모리에 저장된 값을 다른 값으로 바꿀 수 있음
# id() : 객체의 고유값 반환. 메모리 주소를 구별하기 위한 용도로 사용
me = [1, 2, 3]
print(id(me)) # 4370596288
me.append(4)
print(id(me)) # 4370596288 / 할당된 메모리 주소는 변경이 되지 않음
# immutable
# 한 번 생성하면 최초로 저장된 값을 다른 값으로 바꿀 수 없음
obj = 10
print(id(obj)) # 4343431696
obj = obj + 1
print(obj) # 11
print(id(obj)) # 4343431728 / 할당된 메모리 주소가 변경이 됨
# 메모리에 저장된 데이터를 수정하는 것이 아니라, 새로 할당을 받아서 데이터를 저장
2. % 연산자
%s : string
%f : float
%d : decimal (십진법)
name = 'Kai'
print('내 이름은 %s 입니다.' % name) # 내 이름은 Kai 입니다.
print('내 이름은 ', name, '입니다.', sep='')
print('내 이름은 ' + name + '입니다.')
height = 120.5
print('내 키는 %fcm입니다.' % height) # 내 키는 120.500000cm입니다.
weight = 23.55
print('내 몸무게는 %.1fkg입니다.' % weight) # 내 몸무게는 23.6kg입니다.
year, month, day = 2014, 8, 25
print('내 생일은 %d년 %d월 %d일 입니다.' % (year, month, day))
# 내 생일은 2014년 8월 25일 입니다.
3. 삼항 조건 연산자
👩🏻💻 일반적인 삼항 조건 연산자 : 조건식 ? 참 : 거짓 👩🏻💻 파이썬 삼항 조건 연산자 : 참 if 조건식 else 거짓
a = 10
b = 20
result = (a - b) if (a >= b) else (b - a)
print('{}과 {}의 차이는 {}입니다.'.format(a, b, result)) # 10과 20의 차이는 10입니다.
4. 지역변수/전역변수
1) 지역 변수 local variable
📁 함수 내부에서 선언한 변수는 함수 내부에서만 사용. 함수 외부에서는 지역변수에 접근할 수 없음
def f():
a = 10
print(f'f()내부 : {a}')
f() # f() 내부 : 10
# print(f'f() 외부 : {a}') # 함수 외부에서는 a 변수를 사용할 수 없음
2) 전역변수 global variable
📁 함수 외부에서 선언한 변수는 함수 내부나 함수 외부에서 모두 사용할 수 있음
def f():
print(f'f() 내부 : {b}')
b = 10 # 전역변수
f() # f() 내부 10
print(f'f() 외부 : {b}') # f() 외부10
# 1) 전역변수를 단순히 참조만 하는 경우 -> 읽기만 하는 경우
a = 0
def f():
print(a) # 함수 f() 내부에서 전역변수 a를 참조
f() # 0
print(a) # 0 / 함수 f() 외부에서 전역변수 a를 참조
# 2) 전역변수의 값을 변경하는 경우
a = 0
def f():
global a # 전역변수 a를 사용
a = 10 # 전역변수 a의 값을 변경
f() # 함수 f() 실행
print(a) # 10 / 전역변수의 변경된 값을 확인