77 — 네트워크: 파일 받아 저장하기
55는 데이터를 가져와 보여 주었고, 76은 같은 것을 반복해서 가져왔습니다. 이번 가이드는 파일을 한 번 받아서 저장합니다 — 다운로더의 핵심입니다. 웹 페이지를 읽는 것과 파일을 받는 것은 같은 호출이며, 새로 나오는 개념은 조금씩 읽어 나가는 것과 디스크에 쓰는 것 두 가지뿐입니다.
단계
받을 파일을 만드세요.
story.txt는 실제 웹사이트에서 가져온다고 상상하는 파일입니다:
실행해 보기 →옛날옛날에, 한 학습자가 터미널을 열었습니다. 터미널은 한 번에 작은 프로그램 하나씩만 물었습니다. 어느 날 학습자는 다운로더를 만들었고, 파일은 무사히 디스크에 도착했습니다.55에서처럼 폴더를 Python 내장 HTTP 서버로 띄우세요:
python3 -m http.server 8000urlopen은 응답 객체를 줍니다..read()가 본문 전체를 한 번에 돌려줍니다:
실행해 보기 →from urllib.request import urlopen url = "http://localhost:8000/story.txt" data = urlopen(url).read() text = data.decode("utf-8")응답은 원시 바이트라서
.decode("utf-8")로 텍스트로 바꿔야 합니다. 32에서 JSON을 풀 때 쓴 것과 같은 호출입니다.저장은
file_write한 번입니다 — 13. 전체 프로그램이 다운로드하고 저장합니다.download.nme로 저장하세요:
실행해 보기 →# download.nme — 서버에서 파일을 받아 저장하기. # 실행: nme 실행 download # 먼저 이 폴더에서 python3 -m http.server 8000 실행 use file latest from urllib.request import urlopen url = "http://localhost:8000/story.txt" text = urlopen(url).read().decode("utf-8") show f"downloaded {len(text)} characters" file_write("story-copy.txt", text) show "saved as story-copy.txt"서버가 켜져 있는 동안 실행하세요:
nme 실행 downloaddownloaded 99 characters saved as story-copy.txtstory-copy.txt를 열어 보세요. 원본과 똑같은 복사본이고, 여러분의 프로그램이 받아 쓴 것입니다 — 첫 번째 다운로드 파일입니다.큰 파일을 받을 때 조용히 기다리는 것은 좋지 않습니다. 응답은 조금씩 읽을 수 있고, 서버가 보내는 헤더
Content-Length가 전체 크기를 알려줍니다. 조각은 마지막까지 원시 바이트로 두세요. 64바이트 조각은 한글 한 글자를 중간에서 자를 수 있고, 반으로 잘린 문자를 풀면 실패합니다. 바이트를 통째로 모은 뒤 한 번만 풉니다.download-progress.nme로 저장하세요:
실행해 보기 →# download-progress.nme — 조금씩 받으며 진행 상황 보여 주기. # 실행: nme 실행 download-progress # 먼저 이 폴더에서 python3 -m http.server 8000 실행 use file latest from urllib.request import urlopen url = "http://localhost:8000/story.txt" response = urlopen(url) size = int(response.headers["Content-Length"]) show f"size: {size} bytes" chunks = [] received = 0 while True: chunk = response.read(64) if not chunk: break received = received + len(chunk) chunks.append(chunk) show f"received {received} / {size}" text = b"".join(chunks).decode("utf-8") file_write("story-copy.txt", text) show "saved"64바이트씩 읽으므로 데이터가 도착할 때마다 진행 줄이 갱신되고, 빈 조각이 오면
break로 멈춥니다.b"".join(chunks)가 바이트 조각을 이어 붙이고,.decode("utf-8")은 완전한 파일에 대해 한 번만 실행됩니다. 이 루프는 76에서 서버를 폴링한 것과 같은 패턴인데, 여기서는 서버가 끝날 때까지 보냅니다.실행하세요:
nme 실행 download-progresssize: 245 bytes received 64 / 245 received 128 / 245 received 192 / 245 received 245 / 245 saved
직접 해보기
story.txt 옆에 content.txt와 notes.txt를 만들고, 프로그램이 폴더에
있는 파일을 전부 받도록 바꿔 보세요 — 65가 폴더를
나열하는 방법을 보여 주고, 파일 이름마다 urlopen 호출 하나가 됩니다.
또는 76의 status.json처럼 변하는 페이지를 받아서
폴링할 때마다 스냅샷을 저장해 보세요.
배운 것
- 다운로드는
urlopen호출 하나와file_write호출 하나입니다. - 응답 본문은 바이트이고,
.decode("utf-8")가 텍스트로 바꿉니다. Content-Length는 데이터가 오기 전에 크기를 알려 줍니다.- 조금씩 읽으면 프로그램이 작업 중에도 진행 상황을 알려 줄 수 있습니다.
- 조각 하나가 글자를 반으로 자를 수 있으므로, 바이트를 모두 모은 뒤에만 풀어야 합니다.
- 서버를 폴링한 루프-
break패턴이 파일을 받는 데도 그대로 쓰입니다.