사용자 도구

사이트 도구


project:youtubedownload

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판이전 판
다음 판
이전 판
project:youtubedownload [2013/02/14 22:31] – [Firefox로 탐색해보기] 127.0.0.1project:youtubedownload [2014/10/09 21:24] (현재) – 바깥 편집 127.0.0.1
줄 7: 줄 7:
  
 ===== 왜 막지 않을까?  ===== ===== 왜 막지 않을까?  =====
-유튜브가 동영상을 퍼가는 것을 제공하진 않지만, 굳이 막지는 않는 이유를 생각해 보았습니다. 100% 확신할 수는 없으나, 다음과 같은 유가 크게 작용하지 않았을까 생각니다.+유튜브가 동영상을 퍼가는 것을 제공하진 않지만, 굳이 막지는 않는 이유를 생각해 보았습니다. 이것은 제 생각을 적어 본 것이므로 사실과는 다를 수 있습니다.
  
 유튜브도 [[project:personalstreaming|HTTP Live Streaming]]을 합니다. 즉 동영상의 스트리밍이 웹페이지 파일 다운로드와 동일한 원리로 동작합니다. HTTP 프로토콜 설계 방식 상, 궁극적인 동영상의 추출 행위 차단은 매우 어렵습니다. 서버는 클라이언트에게 단순히 데이터를 제공하는 역할만을 하지, 그 이외의 것에 대해서는 별 관심 없습니다. 또 서버는 클라이언트가 왜 동영상을 받아가는지 알려고 하지 않습니다. 그리고 받아간 자료를 어떻게 사용할지도 알 수 없습니다. \\ 이를 변경하려 하는 것은 HTTP 프로토콜 설계 근본을 건드리는 것과 같습니다. 그러므로 클라이언트의 의도와 동작을 서버가 강제하려고 들기 시작하면 일이 무진장 피곤해지고, 부작용도 일파만파 번지겠죠. 그럴 바에는 HTTP 기반의 스트리밍을 포기하는 것이 나을 지도 모릅니다. 그러나 그러기엔 HTTP 기반 스트리밍의 장점이 더 컸기에 사용하였겠죠. 기술적인 이유 이외에 여러 이유가 분명히 있겠지만, 제가 유튜브의 관계자가 아니므로 그런 것까지는 다 다룰 순 없겠군요. 유튜브도 [[project:personalstreaming|HTTP Live Streaming]]을 합니다. 즉 동영상의 스트리밍이 웹페이지 파일 다운로드와 동일한 원리로 동작합니다. HTTP 프로토콜 설계 방식 상, 궁극적인 동영상의 추출 행위 차단은 매우 어렵습니다. 서버는 클라이언트에게 단순히 데이터를 제공하는 역할만을 하지, 그 이외의 것에 대해서는 별 관심 없습니다. 또 서버는 클라이언트가 왜 동영상을 받아가는지 알려고 하지 않습니다. 그리고 받아간 자료를 어떻게 사용할지도 알 수 없습니다. \\ 이를 변경하려 하는 것은 HTTP 프로토콜 설계 근본을 건드리는 것과 같습니다. 그러므로 클라이언트의 의도와 동작을 서버가 강제하려고 들기 시작하면 일이 무진장 피곤해지고, 부작용도 일파만파 번지겠죠. 그럴 바에는 HTTP 기반의 스트리밍을 포기하는 것이 나을 지도 모릅니다. 그러나 그러기엔 HTTP 기반 스트리밍의 장점이 더 컸기에 사용하였겠죠. 기술적인 이유 이외에 여러 이유가 분명히 있겠지만, 제가 유튜브의 관계자가 아니므로 그런 것까지는 다 다룰 순 없겠군요.
  
-다시 말하자면 유튜브의 영상은 설계상 1회 감상을 위한 다운로드와 영구 저장을 위해 다운로드를 구분하는 것이 불가능하며, 따라서 다운로드 받는 사용자의 의도를 일일이 구분하여 통제하는 것이 거의 불가능합니다. 또한 두 행위 자체는 기술적으로 별 차이가 없습니다. 제 상식으로는 오프라인을 위해 개인적으로 영상을 다운로드 받아 따로 저장하는 것은 큰 문제가 없는 것으로 알고 있습니다. 하지만 저작권이 걸린 동영상을 법이 지정한 범위 밖으로 함부로 이용할 경우만 문제가 됩니다(그리고 이건 정말 만의 하나지만, 대한민국이 아닌 어떤 곳에서는 그런 행위 자체를 문제삼을 수 있을지도 모릅니다). 칼이 요리를 할 때 쓰이면 유용한 도구지만, 흉악한 범죄에 이용되면 흉기가 되는 것과 같은 이치입니다. 사용하는 사람이 올바른 지식과 적합한 의도를 갖고 사용해야 합니다. +다시 말하자면 유튜브 서버는 설계상 1회 감상을 위한 다운로드와 영구 저장을 위한 다운로드를 구분하는 것이 거의 불가능하며, 따라서 다운로드 받는 사용자의 의도를 일일이 구분하여 통제하는 것 또한 거의 불가능합니다. 또한 두 행위 자체가 기술적으로 별 차이점이 없습니다. 
 + 
 +제 상식으로는 오프라인을 위해 개인적으로 영상을 다운로드 받아 따로 저장하는 것은 큰 문제가 없는 것으로 알고 있습니다. 하지만 저작권이 걸린 동영상을 법이 지정한 범위 밖으로 함부로 이용할 경우만 문제가 됩니다(그리고 이건 정말 만의 하나지만, 대한민국이 아닌 어떤 곳에서는 그런 행위 자체를 문제삼을 수 있을지도 모릅니다). 칼이 요리를 할 때 쓰이면 유용한 도구지만, 흉악한 범죄에 이용되면 흉기가 되는 것과 같은 이치입니다. 사용하는 사람이 올바른 지식과 적합한 의도를 갖고 사용해야 합니다. 
  
 ===== 그럼 어떻게 할까? ===== ===== 그럼 어떻게 할까? =====
줄 22: 줄 24:
 위 그림은 웹브라우저가 유튜브의 동영상 웹 페이지에 접근한 내역을 찾아 하이라이트시킨 것입니다. 확실히 text/html 문서로 응답을 받았습니다. 웹브라우저는 이제 이 문서를 파싱하여 문서가 요구하는 자바스크립트, 그림 등을 서버에서 받아 올 것입니다. 위 그림은 웹브라우저가 유튜브의 동영상 웹 페이지에 접근한 내역을 찾아 하이라이트시킨 것입니다. 확실히 text/html 문서로 응답을 받았습니다. 웹브라우저는 이제 이 문서를 파싱하여 문서가 요구하는 자바스크립트, 그림 등을 서버에서 받아 올 것입니다.
  
-웹브라우저가 받아오는 여러 리소스(항목) 중에는 아래 그림과 같은 항목도 발견할 수 있을 것입니다. 이것은 동영상 파일임을 쉽게 눈치챌 수 있습니다. 이 파일을 읽어들여 웹 페이지의 플래시 플레이어가 비디오를 재생하는 것입니다. 항목을 쉽게 발견하려면 동영상의 화질을 하나 선택해 고정하는 것이 좋습니다. 이유는 [[project:personalstreaming|HTTP Live Streaming]] 문서를 보면 알 수 있을 것입니다.+웹브라우저가 받아오는 여러 리소스(항목) 중에는 아래 그림과 같은 항목도 발견할 수 있을 것입니다. 이것은 동영상 파일임을 쉽게 눈치챌 수 있습니다. 이 파일을 읽어들여 웹 페이지의 플래시 플레이어가 비디오를 재생하는 것입니다. 보다 이 항목을 쉽게 발견하려면 동영상의 화질을 하나 선택해 고정하는 것이 좋습니다. 이유는 [[project:personalstreaming|HTTP Live Streaming]] 문서를 보면 알 수 있을 것입니다.
  
 {{ :project:youtubedownload:httpfox_02.png?nolink |}} {{ :project:youtubedownload:httpfox_02.png?nolink |}}
줄 32: 줄 34:
 이렇게 비디오가 받아집니다. 만일 지금 서버가 플래시 플레이어에서 재생할 목적으로 받는 것이 아님을 안다면, 그리고 그것을 강제할 의도라면 이런 일은 일어나지 않겠죠. 그러나 서버는 다행히도(?) 이를 구분할 방법이 없어 보입니다. 지금 이 결과를 보면 이렇게 생각할 수 있습니다. 이렇게 비디오가 받아집니다. 만일 지금 서버가 플래시 플레이어에서 재생할 목적으로 받는 것이 아님을 안다면, 그리고 그것을 강제할 의도라면 이런 일은 일어나지 않겠죠. 그러나 서버는 다행히도(?) 이를 구분할 방법이 없어 보입니다. 지금 이 결과를 보면 이렇게 생각할 수 있습니다.
  
-유튜브 서버와 웹브라우저는 별다른 숨김이나 꼼수 없이, HTML 및 관련 기술만으로 동영상을 재생합니다(플래시 플러그인이 별도로 사용된 건 예외입니다). 한편 HttpFox는 동영상의 명시적인 주소를 붙잡았습니다. 그렇다면 이 명시적인 주소는 HTML 소스를 잘 분석하면 금방 알아낼 수 있다 생각이 듭니다. 이제 HTML 소스를 살펴봅니다. 이제 파이어폭스의 '문서 검사' 기능이 활약할 시간입니다.+유튜브 서버와 웹브라우저는 별다른 숨김이나 꼼수 없이, HTML 및 관련 기술만으로 동영상을 재생합니다(플래시 플러그인이 별도로 사용된 건 예외입니다). 한편 HttpFox는 동영상의 명시적인 주소를 붙잡았습니다. 그렇다면 이 명시적인 주소는 HTML 소스를 잘 분석하면 분명 금방 알아낼 수 있을 것입니다. 그러니까 모든 수수께끼의 답은 애초에 미 HTML 소스가 가지고 있는 셈입니다. 사실 당연한 소리입니다. 무언가 저절로 하늘에서 뚝 떨어질 리 없지요 ;-) 우리는 이를 차근차근 해석해 내기만 하면 됩니다. 이제 파이어폭스의 '문서 검사' 기능이 활약할 시간입니다.
  
-바로 유튜브의 재생 화면을 클릭해 어떤 요소인지를 알아봅니다. 그러면 'movie_player'라는 id를 가진 ''embed'' 태그가 재생 화면을 담당하고 있음을 쉽게 알 수 있습니다. 그리고 이 태그의 ''flashvars'' 라는 속성에 매우 길게 무엇인가가 줄줄 적힌 것을 볼 수 있습니다.+문서 검사를 활성화하고 유튜브의 재생 화면을 클릭해 유튜브의 재생기가 HTML의 어떤 요소인지를 알아봅니다. 'movie_player'라는 id를 가진 ''embed'' 태그가 재생 화면을 담당하고 있음을 쉽게 알 수 있습니다. 그리고 이 태그의 ''flashvars'' 라는 속성에 매우 길게 무엇인가가 줄줄 적힌 것을 볼 수 있습니다.
  
 {{ :project:youtubedownload:docu_check.png?nolink |}} {{ :project:youtubedownload:docu_check.png?nolink |}}
줄 62: 줄 64:
 document.getElementById('watch7-player').innerHTML = swf; document.getElementById('watch7-player').innerHTML = swf;
 </code> </code>
-즉 id가 ''watch7-player''인 녀석의 내부 HTML 코드가 ''swf'' 변수의 내용이 됩니다. 위 그림을 보면 ''watch7-player'' id를 가진 ''div'' 태그의 자식이 ''embed''이므로, ''swf'' 라는 변수에 틀림없이 embed 태그의 존재가 있을 겁니다. 윗줄의 swf 변수는 무언가 굉장히 복잡한 암호문처럼 보이지만 '<'나 '>'같은 문자를 utf-8 인딩의 문자로 바꾸어 적은 것 밖에 없습니다. swf 변수의 내용을 보다 보기 쉽게 바꾸어 보려면 다음과 같은 파이썬 코딩을 하면 됩니다.+즉 id가 ''watch7-player''인 녀석의 내부 HTML 코드가 ''swf'' 변수의 내용이 됩니다. 위 그림을 보면 ''watch7-player'' id를 가진 ''div'' 태그의 자식이 ''embed''이므로, ''swf'' 라는 변수에 틀림없이 embed 태그의 존재가 있을 겁니다. 윗줄의 swf 변수는 무언가 굉장히 복잡한 암호문처럼 보이지만 '<'나 '>'같은 문자 유니드로 표현했을 때 그 문자에 대응되는 숫자를 16진수로 적은 것 뿐입니다. 이에 관해서는 '[[http://changwoo/doku/doku.php?id=project:mp3tagretrieval#encoding| Maniadb.com OpenAPI를 이용한 MP3 태그 검색기]]'의 인코딩 부분을 참고하세요. swf 변수의 내용을 보다 보기 쉽게 바꾸어 보려면 다음과 같은 파이썬 코딩을 하면 됩니다.
  
 <code python replace_swf_text.py> <code python replace_swf_text.py>
줄 83: 줄 85:
 <\/div><\/div><\/noembed> <\/div><\/div><\/noembed>
 </code> </code>
-아마 ''__flashvars__''란 문자열은 ''swf = swf.replace('__flashvars__', encoded.join('&'));''란 자바스크립트 코드에 의해 무언가 내용이 변화되었을 겁니다. 변화된 내용은 encoded라는 변수와 관련이 있을 것이고, 이것은 코드 좀 더 위에 있는 ''yt.playerConfig.args'' 란 부분과 깊은 관련이 있을 것입니다. +아마 ''%%__flashvars__%%''란 문자열은 ''swf = swf.replace('%%__flashvars__%%', encoded.join('&'));''란 자바스크립트 코드에 의해 무언가 내용이 변화되었을 겁니다. 변화된 내용은 encoded라는 변수와 관련이 있을 것이고, 이것은 코드 좀 더 위에 있는 ''yt.playerConfig.args'' 란 부분과 깊은 관련이 있을 것입니다. 
-그러면 HTML 문서에서 ''playerConfig''란 자바스크립트 변수를 찾아 보도록 하겠습니다. 위의 자바스크립트 코드 조금 에 아래와 같은 꽤 많은 양의 문자열을 어렵지 않게 발견할 수 있을 것입니다. 내용이 너무 길어 자세한 사항은 생략하겠습니다.+그러면 HTML 문서에서 ''playerConfig''란 자바스크립트 변수를 찾아 보도록 하겠습니다. 제시한 위 자바스크립트 코드의 조금 윗부분에 아래와 같은 꽤 많은 양의 문자열을 발견할 수 있을 것입니다. 내용이 너무 길어 자세한 사항은 생략하겠습니다.
 <code javascript> <code javascript>
 <div id="watch7-video-container"> <div id="watch7-video-container">
줄 100: 줄 102:
 여기서는 ''url_encoded_fmt_stream_map''의 값을 일컬어 '스트림 맵'이라고 부르겠습니다. 스트림 맵의 생짜 값(raw value)은 상당히 긴 문자열인데, 바로 여기에 우리가 원하는 동영상의 실제 주소가 숨겨져 있습니다. 이 문자열은 CSV 형태로 되어 있고, 각 필드의 값은 urlencode가 되어 있습니다. 일단 콤마(,)를 기준으로 문자열 분리를 해야 올바르게 하나의 의미를 가진 덩어리를 나눌 수 있습니다. 우선 당장 프로그래밍을 들이대지 말고 텍스트 편집기를 이용해 차근차근 손으로 해 보도록 하겠습니다. 여기서는 ''url_encoded_fmt_stream_map''의 값을 일컬어 '스트림 맵'이라고 부르겠습니다. 스트림 맵의 생짜 값(raw value)은 상당히 긴 문자열인데, 바로 여기에 우리가 원하는 동영상의 실제 주소가 숨겨져 있습니다. 이 문자열은 CSV 형태로 되어 있고, 각 필드의 값은 urlencode가 되어 있습니다. 일단 콤마(,)를 기준으로 문자열 분리를 해야 올바르게 하나의 의미를 가진 덩어리를 나눌 수 있습니다. 우선 당장 프로그래밍을 들이대지 말고 텍스트 편집기를 이용해 차근차근 손으로 해 보도록 하겠습니다.
  
-스트림 맵을 텍스트 편집기로 분석해 콤마 문자열을 하나 검색합니다. 처음 발견된 콤마를 기준으로 공백을 입력해 값을 두 덩어리로 떼어 냅니다. 추출해 낸 작은 덩어리의 처음은 ''itag=XX%%\u0026%%sig=XXX...'' 와 같이 시작하는 것을 확인할 수 있습니다. \u0026은 유니코드 문자임이 분명합니다.+스트림 맵을 텍스트 편집기로 붙여 넣습니다. 그리고 콤마 문자 하나를 검색합니다. 처음 발견된 콤마를 기준으로 공백을 입력해 값을 위, 아래 두 덩어리로 떼어 냅니다. 추출해 낸 윗부분 덩어리의 처음은 ''itag=XX%%\u0026%%sig=XXX...'' 와 같이 시작하는 것을 확인할 수 있습니다. \u0026은 유니코드 문자임이 분명합니다.
 <code python> <code python>
 print unichr(0x26) print unichr(0x26)
줄 115: 줄 117:
 </code> </code>
  
-이제 거의 다 왔습니다. url 부분이 보이죠? 바로 저기가 실제 동영상의 주소입니다. 나머지 분리하지 않은 덩어리도 같은 방식으로 나누어니다. 값을 다 살펴보면 아시겠지만 서버가 다양한 포맷과 해상도로 인코딩해 놓은 목록이 이 스트림 맵입니다. 결국 순수하게 HTML 코드 및 자바스크립트만 분석하면 동영상 주소 파악은 그리 어려운 것이 아니었네요. 물론 이 방법이 언제까지 통하리란 보장은 없습니다만, 그렇다고 동영상의 주소를 숨바꼭질하는 것처럼 꽁꽁 숨겨두지는 않을 겁니다.+이제 거의 다 왔습니다. url 부분이 보이죠? 바로 저기가 실제 동영상의 주소입니다. 나머지 분리하지 않은 덩어리도 같은 방식으로 나누어지므로 일일이 나누어 확인해 봅니다. 값을 다 살펴보면 아시겠지만서버가 다양한 포맷과 해상도로 인코딩해 놓은 것을 정리한 목록이 이 스트림 맵입니다. 결국 순수하게 HTML 코드 및 자바스크립트만 분석하면 동영상 주소를 찾아낼 수 있는 것이네요. 물론 이 방법이 언제까지 통하리란 보장은 없습니다만, 그렇다고 동영상의 주소를 숨바꼭질하는 것처럼 꽁꽁 숨겨두지는 않을 겁니다.
  
-마지막으로 첫번째 덩어리의 url 부분의 문자열을 한 번 [[http://meyerweb.com/eric/tools/dencoder/|urldecode]]하면 다음과 같이 웹브라우저에도 입력 가능한 url 주소가 됩니다. 단, 이 주소를 바로 넣으면 forbidden 에러가 납니다. ''sig''라는 키의 값을 붙여 넣어야 합니다. 서버에 요청할 때 왠일인지 키 이름은 ''sig''에서 ''signature''로 변경됩니다. 이렇게 signature까지 붙여 넣은 주소를 통해 우리는 동영상에 직접 접근 가능합니다. 그러므로 이 주소만 있으면 HTTP 스트림 재생을 지원하는 다른 재생기에서도 재생이 가능하며, 우리의 최종 목적인 동영상 다운로드도 가능합니다. 이제 프로그램을 작성하기 위한 모든 준비가 끝났습니다.+마지막으로 첫번째 덩어리의 url 부분의 문자열을 한 번 [[http://meyerweb.com/eric/tools/dencoder/|urldecode]]하면 웹브라우저에도 입력 가능한 url 주소가 됩니다. 단, 이 주소를 바로 넣으면 '403 forbidden에러가 납니다. 반드시 ''sig''라는 키의 값을 붙여 넣어야 합니다. 주소로 적을 는 왠일인지 키 이름은 ''sig''에서 ''signature''로 변경됩니다. 이렇게 signature까지 붙여 넣은 주소를 사용하면 동영상에 직접 접근이 가능합니다. 그러므로 이 주소만 있으면 HTTP 스트림 재생을 지원하는 다른 재생기에서도 재생이 가능하며, 우리의 최종 목적인 동영상 다운로드도 가능합니다. 이제 프로그램을 작성하기 위한 모든 준비가 끝났습니다.
  
 ===== 코드로 작성 ===== ===== 코드로 작성 =====
줄 124: 줄 126:
 <code python youtube_dn_proto.py> <code python youtube_dn_proto.py>
 # -*- coding: utf-8  -*- # -*- coding: utf-8  -*-
-import cookielib 
 import urllib import urllib
 import urllib2 import urllib2
project/youtubedownload.1360881090.txt.gz · 마지막으로 수정됨: 2014/10/09 21:23 (바깥 편집)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki