Requests를 사용해서 파일 다운로드 경과를 표시하기

Requests는 파이썬 사용자들이 가장 많이 사용하는 HTTP 통신용 라이브러리일 것이다. 비록 파이썬 표준 라이브러리에 포함되지는 않았지만, 공식 문서에서조차 ‘Requests를 쓰는게 편리하니 추천한다’고 언급할 정도이니… 사실, 파이썬의 역사는 우리의 생각보다 훨씬 오래되었고, 그 와중에 HTTP와 관련된 공식 라이브러리에도 몇 가지 변화가 계속 있어왔다. 최초로 HTTP를 사용하여 데이터를 가져오는 기능은 urllib 에서 구현된 urlopen() 함수였는데, 이 때의 urlopen()은 단순히 open() 함수를 HTTP 너머로 작동하게 하는 것 이상도 이하도 아닌 간단한 구현체였다. HTTP와 관련된 명세는 제법 방대하고 명세 중에서 쓸만한 기능을 제작하기 위해 필요한 좀 더 좋은 구현체가 필요해졌고, 그에 대한 대안으로 제시된 것이 urllib2 이다.

urllib2 를 사용하는 시간이 쌓이면서 자연스럽게 본래의 urllib 은 제거되었고, 파이썬 3로 넘어오면서 urllib2는 방대한 내부 구조를 정리하여 다시 몇 개의 서브 패키지로 쪼개어졌다. 이렇게 정리되면서 본래의 이름인 urllib 을 되찾게 되었다. URL 상의 파일을 ‘여는’ 함수는 여전히 urlopen() 이지만, 이 함수는 이제 urllib.request.urlopen 이라는 곳에 위치하게 된다.

이와 별개로 urllib2 에서도 구현되지 않은 스펙에 대한 보충이나, 더 나은 디자인을 적용하는 등 다음 스텝으로 기능적인 완성도를 push하는 프로젝트가 있었는데, 이 프로젝트가 바로 urllib3 이다. requests 는 이 urllib3 의 기반 위에서 “인간이 이해하고 사용하기 쉬운” 인터페이스를 만들어 제공한다는 목표 아래에 진행되는 프로젝트이다.

따라서 통신의 중간과정에서 일어나는 여러 이벤트나 상황을 자동으로 제어하고, 사용자(개발자)는 최소한의 입력과 출력만 다루면 되도록 해 놓은 것이다. 그래서 requests.get() 이나 requests.post() 만 사용하면 서버와 통신하고 그 결과도 척척 손쉽게 가져와서 사용할 수 있는 것이다.

그런데 이렇게 “쓰기 쉽게” 만들어진 도구들은 그 복잡한 하부구조를 감추는 경향이 있기 때문에, 특정한 상황에서 특정한 방식으로 작동하는 방법을 일일이 지정하기가 곤란한 경우가 있다. 예를들어 파일 다운로드에 지금 몇 % 쯤 다운로드 받았는지를 보여주고 싶다거나 하는 경우 말이다.

requests.get() 함수는 리턴하는 시점에 HTTP 응답의 페이로드까지 모두 다운로드 받아서 필요한 후처리까지 완료하기 때문에, 파일 다운로드 경과를 보여주려 하면 곧바로 100%가 되어버린다. 만약 용량이 큰 파일을 요청하여 다운로드 받는다면 그만큼 시간이 오래 걸리는데 그 기간 동안 프로그램이 응답이 없는 상태가 될 수도 있다.

따라서 요청을 보낸 후 헤더만 미리 받아서 처리하고, 페이로드는 따로 다운로드 하고 싶다면 (이 방식은 기존의 urlopen() 을 통해서 데이터를 가져오던 방식과 비슷하다.) .get() , .post() 함수에 stream=True 옵션을 전달해서 호출한다. 이후 응답객체의 iter_content() 메소드를 사용하면 반복문을 통해서 특정 크기의 데이터 조각은 순차적으로 읽어올 수 있다.

따라서 아래 코드를 사용하면, 파일을 다운로드 받으면서 그 경과율을 출력하여 사용자로 하여금 몇 초간 프로그램이 멈추지 않고 계속해서 결과에 반응하는 것처럼 보이게 만들어주게 된다.

import requests

url = "https://...."
filename = "myfile.zip"

with open(filename, 'wb') as f:
  res = requests.get(url, stream=True)
  r_bytes : int = 0
  t_bytes : int = int(res.headers.get('Content-Length', 0))
  for chunk in res.iter_content(1024):
    f.write(chunck)
    r_bytes += len(chunk)
    print(f"\r{r_bytes / t_bytees : .2%}", end="")
print("\rDone.")

Read more

워드프레스에서 고스트로 이전

워드프레스에서 고스트로 이전

이 글을 쓰면서도 믿기 힘든 사실인데, 블로그라는 걸 처음 시작한지가 20년이 되었습니다. 이글루스에서 처음 시작했다가, SK컴즈가 인수한다고 발표함과 동시에 워드프레스로 플랫폼을 옮겼죠. 워드프레스오 옮긴 이후에는 호스팅 환경을 이리 저리 옮기긴 했지만 거의 18년 가까이 워드프레스를 사용해온 것 같습니다. 그 동안 워드프레스는 블로깅 툴에서 명실상부한 범용CMS로 발전했습니다. 사실 웬만한 홈페이지들은 이제

By sooop
띄어쓰기에 대한 생각

띄어쓰기에 대한 생각

업무 메일을 쓸 때 가장 많이 쓰는 말 중에 하나가 메일 말미에 ‘업무에 참고 부탁 드립니다.‘인데요, 어느 날부터 아웃룩에서 이 ‘부탁 드립니다’가 틀렸다고 맞춤법 지적을 하기 시작했습니다. 맞는 말은 ‘부탁드립니다’라고 붙여 쓰는 거라고. 사실 아래아한글 시절부터 이전의 MS워드까지, 워드프로세서들의 한국어 맞춤법 검사 실력은 거의 있으나 마나 한

By sooop

구글 포토에서 아이클라우드로 탈출한 후기

한 때 구글 포토가 백업 용량을 무제한으로 제공해 주겠다고해서, 구글 포토를 사용해서 사진을 백업해왔습니다. 물론 이 이야기의 결말은 저나 이 글을 읽고 있는 여러분이나 모두 알고 있습니다. 사실 AI에게 학습 시킬 이미지 데이터를 모으기 위한 것일 뿐이라거나 하는 이야기는 그 당시에도 있었습니다만, 에이 그래도 구글인데 용량은 넉넉하게 주겠지…하는 순진한

By sooop

Julia의 함수 사용팁

연산자의 함수적 표기 Julia의 연산자는 기본적으로 함수이며, 함수 호출 표기와 같은 방식으로 호출하는 것이 가능합니다. 또한 그 자체로 함수이기 때문에 filter(), map() 과 같이 함수를 인자로 받는 함수에도 연산자를 그대로 적용하는 것이 가능합니다. 특히 + 연산자는 sum() 함수와 같이 여러 인자를 받아 인자들의 합을 구할 수 있습니다. 2 + 3 # = 5 +(2,

By sooop