StringIO객체
데이터 크롤링을 연습하던 중, df = pd.read_html(browser.page_source)라는 코드를 입력하자, VScode가 다음과 같은 메세지를 보여줬다.
FutureWarning: Passing literal html to 'read_html' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object.
이 메시지는 FutureWarning으로, 앞으로 코드가 변경될 가능성을 미리 알려주는 경고이다. 메세지의 의미는 다음과 같다.
pd.read_html() 함수에 직접적으로 HTML 문자열을 넘기는 방식이 더 이상 지원되지 않을 예정이며, 대신에 HTML 문자열을 읽을 때는 StringIO 객체로 감싸서 처리해야 한다.
요컨데, pd.read_html()함수에서 문자열을 다룰 때에는 StringIO 객체로 문자열을 감싸야 한다는 소리였다.
StringIO 의 역할
- 입출력(I/O) 함수의 일관성 유지
파일을 처리하는 함수들은 보통 파일 객체를 요구한다. 하지만, 가끔 파일 대신 메모리에 저장된 문자열을 파일처럼 다루고 싶을 때가 발생할 수 있으며, 이때, StringIO는 문자열을 파일처럼 다룰 수 있게 해준다.- 예를 들어, pd.read_html()이나 pd.read_csv()와 같은 함수들은 파일에서 데이터를 읽을 때 주로 사용된다. 하지만 문자열 데이터를 파일처럼 다루고 싶을 때, StringIO를 사용하면 그 문자열을 파일처럼 읽을 수 있다.
- 효율성
실제 파일을 생성하지 않고도 메모리 내에서 파일 입출력처럼 작업할 수 있기 때문에, 파일을 디스크에 쓰는 시간과 자원을 절약할 수 있다. 즉, 임시 파일을 만들 필요 없이 빠르고 가볍게 작업할 수 있다. - 편리한 디버깅
파일 대신 문자열로 직접 데이터를 처리할 수 있으면 데이터 변환 및 테스트가 훨씬 쉬워진다. 디버깅이나 임시 데이터 처리가 필요할 때, 파일로 저장하는 번거로운 과정을 생략할 수 있다.
또한, df = pd.read_html(browser.page_source)라는 코드를 입력했을 때, StringIO와 관련된 FutureWarning메세지가 뜬 것으로 볼 때, browser.page_source는 문자열을 반환하는 코드라는 것을 알 수 있었다. browser.page_source 에 대해 검색하여 알게 된 내용은 아래와 같다.
browser.page_source는 HTML 소스 코드를 문자열 형태로 반환한다. 즉, 웹페이지의 HTML 코드를 하나의 큰 문자열로 취급하는 것과 같다. 따라서, pd.read_html(browser.page_source)와 같이 사용할 때 browser.page_source는 문자열로 간주되며, pandas.read_html()은 이 문자열에서 테이블 데이터를 추출하게 된다.
StringIO를 사용할 수 있는 상황은 아래와 같다.
1. API 응답에서 CSV 데이터를 처리할 때
어떤 API에서 CSV 데이터를 문자열로 응답할 경우, 이를 pandas 등으로 파일처럼 처리하려면 StringIO를 사용할 수 있다.
import pandas as pd
from io import StringIO
import requests
# 예시: API 응답이 CSV 문자열인 경우
response = requests.get('https://api.example.com/data.csv')
csv_data = response.text
# StringIO로 감싸서 pandas로 읽기
df = pd.read_csv(StringIO(csv_data))
2. 텍스트 데이터로 파일 입출력 흉내 내기
파일을 사용하지 않고, 메모리에서 바로 텍스트 데이터를 읽고 쓸 수 있다. 예를 들어 로그 데이터나 테스트용 데이터를 파일로 저장하지 않고 바로 처리할 수 있다.
from io import StringIO
data = """col1,col2,col3
1,2,3
4,5,6
7,8,9
"""
# StringIO로 메모리 내에서 파일처럼 사용
f = StringIO(data)
# pandas로 읽기
df = pd.read_csv(f)
3. 테스트 데이터 생성
코드를 테스트할 때, 임시로 파일을 만들지 않고 메모리 상에서 파일처럼 다뤄 테스트할 수 있다.
from io import StringIO
import pandas as pd
# 가상의 CSV 데이터를 문자열로 생성
test_data = "name,age,city\nAlice,30,New York\nBob,25,Chicago"
# StringIO로 감싸서 파일처럼 처리
df = pd.read_csv(StringIO(test_data))
# 데이터프레임 확인
print(df)
4. 데이터 처리 중간에 변환해야 할 때
데이터를 처리하는 과정에서 텍스트 파일이 아니라 메모리 내의 데이터일 때, 이를 파일처럼 처리할 필요가 있는 경우 사용할 수 있다.
위에 적어놓았듯이, StringIO는 문자열을 파일처럼 처리할 수 있도록 해주는 객체이다. 일반적으로 파일이 아닌 메모리에 있는 문자열 데이터를 파일처럼 읽고 쓸 때 사용하며, StringIO를 사용할 수 있는 함수 몇 가지를 아래에 정리해보았다.
1. pandas
- pandas.read_csv(): 파일 경로 대신 문자열 데이터를 읽어서 DataFrame으로 변환하고 싶을 때.
- pandas.read_html(): StringIO로 HTML 문자열을 읽을 수 있다.
import pandas as pd
from io import StringIO
data = """col1,col2,col3
1,2,3
4,5,6"""
df = pd.read_csv(StringIO(data))
print(df)
2. csv (표준 라이브러리)
- csv.reader(), csv.DictReader(): CSV 데이터를 파일에서 읽어오는 대신, 문자열을 직접 처리할 때.
import csv
from io import StringIO
data = """name,age,city
Alice,30,New York
Bob,25,Los Angeles"""
reader = csv.reader(StringIO(data))
for row in reader:
print(row)
3. json (표준 라이브러리)
- json.load(): 파일에서 JSON 데이터를 읽어올 때 사용되는데, 파일 대신 문자열을 처리할 경우 StringIO를 사용할 수 있다.
import json
from io import StringIO
data = '{"name": "Alice", "age": 30}'
json_obj = json.load(StringIO(data))
print(json_obj)
4. 그 외(XML 파싱, INI 파일 처리, 엑셀 파일 처리)
- ElementTree.parse(): 파일에서 XML 데이터를 읽어오는데, 문자열에서 XML을 파싱할 때는 StringIO를 사용한다.
- ConfigParser.read(): INI 파일을 읽어오는 대신, 문자열 데이터를 읽어서 설정값을 처리할 때 StringIO를 사용할 수 있다.
- openpyxl.load_workbook(): 엑셀 파일을 읽어오는데, 파일이 아닌 문자열 형태의 엑셀 바이너리 데이터를 읽어오고 싶을 때.