파이썬에서 원격지 파일 다운로드를 받는 상황이다. 수 기가로 크다. 이걸 메모리에 받아서 넣자니 가상 서버가 후달달거린다.
이럴때는 스트리밍으로 받으며 바로 디스크에 써넣으면 메모리를 쓰지 않는다.
너무 당연해서 맥주를 시원하게 먹고 싶으면 얼음을 넣어서 일년 후 한국은 공산화에 베네주엘라 되는 소리 같다.
문제는 유니코드 인코딩이다.
파일 공급자가 euc-kr을 쓴다. 역사가 보인다. 우리는 젊디 젊은 X세대니까 utf-8을 쓴다. 코딩을 바꿔야 한다.
스트리밍으로 이걸 한 번에 하고 싶다. 어쩐다? 유니코드의 세계는 1글자가 1바이트가 아니다. 예를 들어 스트리밍으로 받는 청크 단위가 4바이트인데 한글 1글자(3바이트)가 나왔다. 1바이트가 남는다. 남는 1바이트가 또 한글 3바이트의 첫번째 바이트다. 얼라인(=경계)에 걸리고 이런저런 인코딩 디코딩 함수들이 메롱거린다.
그리하야 우리는 이것을 상태머신이든 뭐든 드릅게 바이트 단위로 보면서 처리하는 것을 짜거나 몽창 메모리에 넣어두고 제공 함수를 돌리거나 해야 한다. 아이고 귀찮아.
codec 모듈을 써라 파이썬3 기능을 써라 하는데 뭔가 귀찮고 복잡하고 막상 뭔가 잘 안 되고 해서 그냥 api 레퍼런스 까봤다. 뭐 숨겨진 플래그라도 있는 거 아녀?
정답.
r = request.get(url, stream = True) # 스트림 다운로드
r.encoding = 'euc-kr' # 지금 받는 파일의 코덱을 명시, 안 해도 무관할 듯.
with open(fname, 'wt', encoding='utf-8') as f:
for chunk in r.iter_content(chunk_size=1024 * 1024, decode_unicode=True):
print (chunk, file=f)
블라블라..
decode_unicode=True 하나면 된다는 것. 실제로 바이트 길이로 찍어보면 1024 * 1024가 아니라 들쭉날쭉하다.
현재까지는 잘 되는 것 같다.
chunk_size는 적당히 커야 cpu를 덜 먹는다. OS를 눈꼽만큼 이해하고 있다면 자동 이해. 같은 맥락에서 next 이터레이터 등으로 1바이트씩 순회하며 디코딩하는 트릭이 있는데... cpu 100% 친다.