본문 바로가기

내용 복습/python

데이터 공부 with 파이썬 2일차

데이터베이스 접근 권한이 업격히 관리되거나, 민감한 개인정보가 있거나 아예 네트워크가 분리되어 물리적인 접근이 불가능할 수도 있다. 이럴 때 인증된 URL만 있으면 언제든지 필요한 데이터에 편리하게 접근할 수 있는 방식이 있다. 이럴때 API를 사용해 접근하면 되고 웹 기반의 API가 널리 쓰인다. API를 만드는 것은 프로그래머의 몫이고 데이터분석가는 API를 사용하는 법을 아는 게 중요하다.

 

1. 데이터 형식 다루기

  • 데이터분석가는 웹 기반 API에서 HTML 대신 CSV나 JSON, XML을 사용한다. 특히, CSV는 항목의 개수가 정확하게 맞지 않으면 읽을 수 없고 복잡한 데이터구조를 표현하기 어렵기에 JSON이 선호된다.
  • JSON은 마치 파이썬의 딕셔너리와 리스트를 중첩해놓은 것과 비슷하다. 키와 값을 콜론(:)으로 연결한다. 웹 기반 API로 데이터를 전달할 때는 파이썬 딕셔너리가 아니라 텍스트로 전달해야 한다.
  • json.dumps() 함수를 사용하면 파이썬 객체를 JSON 형식에 맞는 텍스트로 바꿀 수 있다. 
import json
 # json.dumps는 아스카 문자 외의 문자를 16진수로 출력하기에 False처리하여 한글로 출력할 수 있게 함
d_str = json.dumps(d, ensure_ascii=False)
print(d_str)

이제 print(type(d_str))로 출력해보면 str을 확인할 수 있다. json은 순수 문자열이라 딕셔너리와 같은 다른 형태로 표현할 수 있다. 딕셔너리라면 dict로 표시된다.

  • json.loads() 함수를 사용하면 JSON 문자열을 파이썬 객체로 다시 변환할 수 있다. 파이썬 딕셔너리를 먼저 만들고 json.dump() 함수를 이용해 문자열로 변환한 다음 json.load()을 사용하는 것은 번거로우니 JSON 문자열을 json.loads() 함수에 직접 전달하는 방식을 사용할 수도 있다.
  • JSON의 한 키 값에 여러 항목이 들어가도록 리스트와 같은 형태라도 잘 사용할 수 있다. 또한 JSON 객체를 대괄호 안에 나열하면 JSON 배열의 형태로 나타낼 수 있다.

만약 아래 형태를 json.load() 함수로 파이썬 객체로 변환하려면 문자열이 길기에 세겹따옴표(""")를 사용해 여러줄에 걸친 문자열을 만들어야 한다. JSON 배열이 파이썬 리스트로 변환된다. d4[0]은 파이썬 리스트의 첫번쨰 딕셔너리가 된다.

d4_str = """ 
[
  {"name": "나의 라임 오렌지나무", "author": "J.Mauro", "year": 1968},
  {"name": "해리포터와 불의잔", "author": "J.K.Rowling", "year": 1995}
]
"""  # 이렇게 쓰면 긴 문자열을 줄바꿈하여 입력할 수 있다.

d4 = json.loads(d4.str)
print(d4[0]['name'])

 

JSON 문자열을 데이터프레임으로 변환하려면 판다스가 제공하는 read_json() 함수를 사용하면 된다. 판다스 2.1 버전부터는 read_json() 함수와 read_xml() 함수에 파일 경로나 파일객체를 전달해야하며, 원시 문자열을 넣으면 경고가 발생한다. 이런 경우 StringIO 클래스로 감싸면 문자열을 파일객체처럼 다룰 수 있다.

from io import StringIO
import pandas as pd
pd.read_json(StringIO(d4_str))

 

JSON을 데이터프레임으로 바꾸는 또 다른 방법은 JSON 문자열을 파이썬 객체로 만든 다음 DataFrame 클래스를 사용하는 것이다. pd.DataFrame(d4)로 파이썬 객체를 데이터프레임으로 만들 수 있다. JSON은 쉽고 간결하기 때문에 웹 기반 API에서 많이 사용한다.

 

XML은 eXtensible Markup Language의 약자로 구조적이지 못해 API 전송에 적합하지 않은 HTML 대신에 고안된 포맷이다. 

 

book은 세개의 하위 엘리먼트를 가지고 있고 <book>를 부모 엘리먼트 혹은 부모 노드라고 부르며, 하위 엘리먼트를 자식 엘리먼트로 부른다. 태그 이름은 특수문자와 공백을 포함할 수 없고 '-', '.', 숫자로 시작할 수 없다. XML 문자열을 파이썬 객체로 변환하려면 fromstring() 함수를 사용할 수 있다. 파이썬에서 기본으로 제공되는 xml 패키지는 XML 문서를 읽고 쓸 수 있는 간편한 API를 제공한다. XML을 세겹따옴표 문자열로 만든 다음 파이썬의 xml 패키지를 사용해 읽을 수 있다.

# xml -> 문자열
x_str = """
<book>
  <name>해리포터와 불의 잔</name>
  <author>J.K.Rowling</author>
  <year>1995</year>
</book>
"""

# 문자열 -> 파이썬 객체
import xml.etree.ElementTree as et
book = et.fromstring(x_str)

# 변환됐는지 확인
print(type(book))

# 엘리먼트 이름(=최상위태그) 확인
print(book.tag)

 

json 패키지는 JSON 문자열을 파이썬 객체로 변환하지만, fromstring() 함수가 변환하는 객체는 단순한 파이썬 객체가 아니라 ElementTree 모듈 아래에 정의된 Element 클래스의 객체다.

 

XML 문서에서 추출하고 싶은 것은 도서명(name), 저자(author), 발행년도(year)인데, 이를 위해 <book> 엘리먼트의 자식 엘리먼트를 구한다음, 각각의 자식 엘리먼트에 담긴 텍스트를 읽을 수 있다.

# 자식 엘리먼트 구하기
book_childs = list(book) # 리스트로 변경
print(book_childs)

# 각 항목을 할당하고 text 속성으로 텍스트 출력
name, author, year = book_childs
print(name.text)
print(author.text)
print(year.text)

 

그러나 XML은 자식 엘리먼트 순서가 항상 일정하다는 것을 보장하지 않는다. 따라서 book_childs에서 순서대로 자식 엘리먼트를 찾는 것은 위험하다. book 객체의 findtext() 메서드를 사용하면 해당하는 자식엘리먼트를 탐색하여 자동으로 텍스트를 반환할 수 있다. 위의 함수는 아래처럼 바꿔 쓰는 것이 좋은 방법이다. 아래처럼 findtext() 메서드에 태그이름을 넣어주니 자동으로 해당 엘리먼트를 찾아 텍스트를 반환받았다. 이방식이 자식 엘리먼트의 순서가 어떻게 되어있든 상관없기에 안전하다.

name = book.findtext('name')
author = book.findtext('author')
year = book.findtext('year')
print(name)
print(author)
print(year)

 

JSON과 달리 XML은 배열 같은 구조가 없다. 대신 복수형으로 한번 더 감싸 아래와 같은 구조를 만들어보자. 여기서 books는 두 개의 book 엘리먼트를 포함한다. fromstring() 함수를 사용해 부모 엘리먼트를 확인하면 books를 정상적으로 반환한다.

x2_str = """
<books>
    <book>
      <name>해리포터와 불의 잔</name>
      <author>J.K.Rowling</author>
      <year>1995</year>
    </book>
    <book>
      <name>나의 라임 오렌지나무</name>
      <author>J.Mauru</author>
      <year>1968</year>
    </book>
</books>
"""

# 바뀐 부모엘리먼트 확인
books = et.fromstring(x2_str)
print(books.tag)

 

어러개의 자식 엘리먼트를 확인하려면 findall() 메서드와 for문을 사용하면 된다.

for book in books.findall('book'):
    name = book.findtext('name')
    author = book.findtext('author')
    year = book.findtext('year')
    print(name)
    print(author)
    print(year)

 

정리하면 JSON의 경우 API에서 전달한 텍스트를 json.loads() 함수를 사용해 파이썬 객체로 바꾸어 내용을 추출했다면, XML은 xml.etree.ElementTree 모듈에 있는 fromstring() 함수를 사용하여 부모(루트) 엘리먼트를 얻고, findall()메서드로 자식 엘리먼트에 담긴 텍스트를 추출할 수 있다.

 

2. 파이썬으로 API 호출하기

파이썬에서 URL을 호출하여 데이터를 받을 수 있는 방법이 많은데, 그중 가장 널리 활용되는 패키지는 requests 패키지이다. get() 함수가 반환하는 값은 API 호출의 결과를 담고 있는 requests 패키지의 Response 클래스 객체이다. 이 객체의 json() 메서드는 웹서버로부터 받은 문자열을 파이썬 객체로 변환하여 반환한다.

import requests
url = "http://..."
r = requests.get(url)
data = r.json()
print(data)

 

하나의 리스트가 하나의 딕셔너리만 갖고 있다면 계층구조를 표현하는 것 외에 큰 역할은 없다. 이 JSON 데이터를 판다스 데이터프레임으로 바꾸려면 data 딕셔너리를 하위계층인 books 딕셔너리로 변경할 수 있다. data['response']['docs']에 매핑된 리스트를 순회하면서 docs 키에 매핑된 딕셔너리를 추출한 후 빈 리스트에 추가하려면 for문을 이용할 수 있다.

books = []
for d in data['response']['docs']:
    books.append(d['docs])
    
 # 데이터프레임화
 books_df = pd.DataFrame(books)
 print(books_df)
 
 # JSON 파일로 저장
 books_df.to_json('20s_best_book.json')

'내용 복습 > python' 카테고리의 다른 글

FastAPI 공부 2일차  (0) 2025.04.26
FastAPI 공부내용 1일차  (0) 2025.04.25
데이터분석 with 파이썬 1  (0) 2025.02.06