77 — Network: downloading files
Guide 55 fetched data and showed it; 76 fetched it again and again. This guide fetches a file once and saves it — the whole point of a downloader. Reading a web page and downloading a file are the same call; the only new ideas are reading in chunks and writing to disk.
Steps
Create a file to download.
story.txtis the file you would normally fetch from a real website:Once upon a time, a learner opened a terminal. The terminal asked for one small program at a time. One day the learner wrote a downloader, and the file arrived safely on disk.Serve the folder with Python's built-in HTTP server, as in guide 55:
python3 -m http.server 8000urlopengives you a response object. Its.read()call returns the whole body at once:
run it →from urllib.request import urlopen url = "http://localhost:8000/story.txt" data = urlopen(url).read() text = data.decode("utf-8")The response is raw bytes;
.decode("utf-8")turns them into text, the same call that decoded JSON in guide 32.Saving the text is one
file_writecall — guide 13. The full program downloads and saves. Savedownload.nme:
run it →# download.nme — fetch a file from the local server and save it. # Run: nme r download # Serve this folder first: 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"Run it while the server is running:
nme r downloaddownloaded 176 characters saved as story-copy.txtOpen
story-copy.txt: it is an exact copy of the original, written by your program — your first downloaded file.A big download should not vanish into silence. The response can be read in chunks, and
Content-Length(a header the server sends) tells you the total size. The chunks stay raw bytes until the end: a 64-byte slice can cut a Korean or emoji character in half, and decoding a half-cut character fails. Keep bytes whole, decode once. Savedownload-progress.nme:
run it →# download-progress.nme — download in chunks and report progress. # Run: nme r download-progress # Serve this folder first: 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"Reading 64 bytes at a time means the progress line updates as data arrives;
breakstops when an empty chunk says the file is over.b"".join(chunks)glues the byte pieces, and.decode("utf-8")runs once, on the complete file. The loop is the same pattern that polls a server in guide 76, but here the server sends until it is done.Run it:
nme r download-progresssize: 176 bytes received 64 / 176 received 128 / 176 received 176 / 176 saved
Try it yourself
Add content.txt and notes.txt next to story.txt and make the program
download every file it finds — guide 65 lists a
folder, and each file name becomes one urlopen call. Or download a page
that changes (like status.json in guide 76) and save one
snapshot per poll.
What you learned
- A download is an
urlopencall plus afile_writecall. - The response body is bytes;
.decode("utf-8")makes text from it. Content-Lengthtells you the size before the data arrives.- Reading in chunks lets the program report progress while it works.
- A chunk can cut a character in half — decode bytes only after joining them all.
- The same loop-and-
breakpattern that polls servers also receives files.