つかいかた
$ sh your.sh -u "http://www.yourfilehost.com/media.php?cat=video&file=hoge.wmv"
分割ダウンロードする際の分割数はデフォルトで10。yourfilehost側が分割ダウンロードをどう思っているのか判らないが、リクエストヘッダにrangeを含めてもリクエストどおりダウンロードできるようだ。
引数に与えたURIから実際に取得するURIを引っ張るまでにhtmlファイル内容の解析を行っているが、grep等で書いているため、yourfilehost側の仕様変更があるとこのスクリプトは使えなくなる可能性大。
およそ1時間30分で3596308バイト。666KB/sec。
ソース[2008年 11月 25日 火曜日 19:50:49 JST]
とりあえず下のような感じ。保存してつかってくれい。使い方は上に書いたぞよ。スクリプトの版はこのセクションのタイトルに書いたぞよ。
#!/bin/bash -vx # Function to get filesize function FileSize () { if [ -f "$1" ] ; then SIZE=`ls -ld "$1" | awk '{print $5}'` echo $SIZE else echo 0 fi } # Function to output an error message and terminate function ErrorMessage () { echo -e "Error: $1" exit 1 } function usage() { local CMDNAME=`basename "$0"` echo "Usage ${CMDNAME} -u \"URI\" " 1>&2 } USER_AGENT='Mozilla/4.0 (compatible; MSIE 6.0; Windows XP)' URI="FALSE" URIVALUE= OPT= while getopts "u:" OPT do case ${OPT} in "u" ) URI="TRUE" URIVALUE="${OPTARG}" ;; * ) usage exit 1 ;; esac done if [ "${URI}" != "TRUE" ] ; then usage exit 2 fi CURL='curl -sSv --retry 10' CURL='curl -sSv --retry 10 -0 -C -' CURL='curl -sSv --retry 10 -0' #WGET='wget -t 10 -Sd --keep-session-cookies' ITERATION=10 URL1='' FILE='' i=0 while [ -z "${URL1}" -o -z "${FILE}" ]; do ${CURL} -A "${USER_AGENT}" -o tmp2.$$ -c cookie2.$$ ${URIVALUE} cat tmp2.$$ | grep -e 'param.*embed' > tmp0.$$ cat tmp0.$$ | cut -d "&" -f3 > tmp0.$$ cat tmp0.$$ | cut -d "=" -f2 > tmp0.$$ cat tmp0.$$ | sed 's/%3A/:/g;s/%2F/\//g;s/%3F/?/g;s/%26/\&/g;s/%3D/=/g' > tmp0.$$ URL1=$(cat tmp0.$$) cat tmp0.$$ | cut -d "&" -f5 > tmp0.$$ cat tmp0.$$ | cut -d "=" -f2 > tmp0.$$ cat tmp0.$$ | sed 's/%3A/:/g;s/%2F/\//g;s/%3F/?/g;s/%26/\&/g;s/%3D/=/g' > tmp0.$$ FILE=$(cat tmp0.$$) if [ ${i} -lt ${ITERATION} ]; then i=`expr $i + 1` sleep 1 else rm tmp2.$$ cookie2.$$ tmp0.$$ tmp3.$$ cookie3.$$ exit 1 fi done echo "# Successfully set URL1 of path to file including path to flash video and FILE meaning filename" echo $URL1 echo $FILE if [ -s "${FILE}" ]; then rm tmp2.$$ cookie2.$$ tmp0.$$ tmp3.$$ cookie3.$$ exit 1 else rm -f ${FILE} fi URL='' i=0 while [ -z "${URL}" ]; do ${CURL} -A "${USER_AGENT}" -o tmp3.$$ -c cookie3.$$ ${URL1} cat tmp3.$$ | cut -d "&" -f1 > tmp0.$$ cat tmp0.$$ | cut -d "=" -f2 > tmp0.$$ cat tmp0.$$ | sed 's/%3A/:/g;s/%2F/\//g;s/%3F/?/g;s/%26/\&/g;s/%3D/=/g' > tmp0.$$ URL=$(cat tmp0.$$) cat tmp3.$$ if [ ${i} -lt ${ITERATION} ]; then i=`expr $i + 1` sleep 1 else rm tmp2.$$ cookie2.$$ tmp0.$$ tmp3.$$ cookie3.$$ exit 1 fi done echo "# Successfully set URL to flash video" echo $URL FILESIZE='' i=0 while [ -z "${FILESIZE}" ]; do ${CURL} -A "${USER_AGENT}" -b cookie3.$$ ${URL} -I 2> /dev/null | sed -e "s/^$//" > "z_${FILE}.header" FILESIZE="`grep -i '^\(Content-Length: \)\?[0-9]\+' "z_${FILE}.header" | sed 's/^\(Content-Length: \)\?\([0-9]\+\)/\2/i'`" if [ ${i} -lt ${ITERATION} ]; then i=`expr $i + 1` sleep 1 else rm tmp2.$$ cookie2.$$ tmp0.$$ tmp3.$$ cookie3.$$ exit 1 fi done echo "# Successfully set FILESIZE of flash video" echo ${FILESIZE} NUM=10 FILESIZE="`echo ${FILESIZE} | sed 's/\r//g;s/\n//g;'`" WIDTH_BYTE=`expr ${FILESIZE} / ${NUM}` START_BYTE=0 END_BYTE=${WIDTH_BYTE} SLEEP_SEC=1; i=0; while [ ${i} -lt ${NUM} ]; do TEMP_FILE="${FILE}.$i" START_BYTE="`expr ${WIDTH_BYTE} '*' ${i}`" if [ ${i} -lt `expr ${NUM} - 1` ]; then END_BYTE="`expr ${START_BYTE} + ${WIDTH_BYTE} - 1`" else END_BYTE=`expr ${FILESIZE} - 1` fi FILE_BYTE=`expr ${END_BYTE} - ${START_BYTE} + 1` TEMP_FILE_BYTE=0; ( while [ ${FILE_BYTE} -ne ${TEMP_FILE_BYTE} ]; do echo "$i / ${NUM} : byte range ${START_BYTE}-${END_BYTE} ${FILE_BYTE}" ${CURL} -A "${USER_AGENT}" --range ${START_BYTE}-${END_BYTE} -o ${TEMP_FILE} -b cookie3.$$ ${URL} TEMP_FILE_BYTE="`FileSize "${TEMP_FILE}"`" sleep ${SLEEP_SEC} done echo "$i / ${NUM} : byte range ${START_BYTE}-${END_BYTE} ${FILE_BYTE} end" )& i=`expr $i + 1` done echo "Waiting for cURLs to finish..." wait echo echo "Assembling ${FILE}... " i=0; while [ ${i} -lt ${NUM} ]; do TEMP_FILE="${FILE}.$i" echo "cat ${TEMP_FILE} >> ${FILE}" cat ${TEMP_FILE} >> ${FILE} echo rm ${TEMP_FILE} rm ${TEMP_FILE} i=`expr $i + 1` done rm tmp2.$$ cookie2.$$ tmp0.$$ tmp3.$$ cookie3.$$ exit
呼び出しのリスト
100回も200回も呼び出す場合にはぜんぜん効率的じゃないので、リストを作っておいて、sedで適当に置換して、出来たシェルスクリプトを走らせる。
$ cat yourfilehost.list http://www.yourfilehost.com/media.php?cat=video&file=02111.wmv http://www.yourfilehost.com/media.php?cat=video&file=02112.wmv http://www.yourfilehost.com/media.php?cat=video&file=02113.wmv http://www.yourfilehost.com/media.php?cat=video&file=02114.wmv http://www.yourfilehost.com/media.php?cat=video&file=02115.wmv $ sed 's/^/sh hoge.sh -u "/;s/$/" >>log.txt/;' yourfilehost.list > yourfilehost.list.sh $ cat yourfilehost.list.sh sh hoge.sh -u "http://www.yourfilehost.com/media.php?cat=video&file=02111.wmv" >>log.txt sh hoge.sh -u "http://www.yourfilehost.com/media.php?cat=video&file=02112.wmv" >>log.txt sh hoge.sh -u "http://www.yourfilehost.com/media.php?cat=video&file=02113.wmv" >>log.txt sh hoge.sh -u "http://www.yourfilehost.com/media.php?cat=video&file=02114.wmv" >>log.txt sh hoge.sh -u "http://www.yourfilehost.com/media.php?cat=video&file=02115.wmv" >>log.txt $ sh yourfilehost.list.sh
呼び出し2
これでもまだ面倒だ、と言う人のために。ダウンロード用のスクリプトを呼び出すスクリプトを作ってみた。
#!/bin/bash trap 'echo "trapped."; exit 1' 1 2 3 15 declare -r listfile="hoge-list" declare -r lockfile="${listfile}.lock" function download { local -r url=$1 #local num=`expr ${RANDOM} % 100` sh hoge.sh -u "${url}" #sleep ${num} } function process { while [ `find ${listfile} -size +0` ]; do if lockfile -1 ${lockfile} then url=`head -n1 ${listfile}` sed -si 1d ${listfile} fi rm -f ${lockfile} download ${url} done } function main { local -i i=0 while [ ${i} -lt 2 ]; do ( process )& i=`expr ${i} + 1` done wait } main exit
こいつを適当な名前(hoge-list.sh)で保存。hoge.sh(ダウンロード用のスクリプト)とhoge-list(URLの書かれたリスト)と同じ場所に保存。
$ sh hoge-list.sh
2つのプロセスを平行に走らせて、同じリストファイルからURLを読んでいる。平行に走らせるプロセスの数は同時にダウンロードする数に対応している。ダウンロード用のスクリプト内における「分割数」と「プロセスの数」の積がホストに対して張るコネクションの数に対応しているので、増やしすぎると302 Foundでエラーページにリダイレクトされる。ダウンロード用のスクリプトの中でcURLに与えるリトライ数を増やしておけば対応可能だが、YourFileHost側からすれば迷惑な話だろう。分割数を減らすかプロセス数を減らすことをお勧めする。2プロセス走らせても2倍のスピードになるわけではない。実際、1.5倍くらいのスピードになった。
yourfilehostダウンローダの参考文献
- yourfilehostの裏API (Yusukebe::Tech)
- 2008-02-03 - 駆け馬に無知
- McURL - Multiple cURL
- cURL - How To Use (マニュアルページ日本語訳)
- cURL - Manual - Japanese Translation
- シェルスクリプトのデバッグ - UNIX & Linux コマンド・シェルスクリプト リファレンス
- curlの使い方
- 【コラム】OS X ハッキング! (33) それにつけてもダウンローダはcurl | パソコン | マイコミジャーナル
- curl リクエスト|request ヘッダ|header 保存 - Google 検索
- コマンドラインからでもブラウザからでもWgetでダウンロード - SourceForge.JP Magazine
- Linuxの使い方 - シェルスクリプトの作り方
- man lockfile(1)
技術情報
ここのセクションはメモ書きですな。IE6.0で動画再生してWireSharkでパケット解析したときの。
ブラウザで動画再生開始した際に流れたリクエストヘッダ
おそらく下に挙げたパケットがフラッシュビデオファイルをダウンロードする際のリクエストヘッダだろう。これと同じヘッダをcurlやwgetで発行できればflvファイルがダウンロードできるはずだ。
GET /unit1/flash8/cd/cd94412311d23d23d447497cdc5f5761.flv HTTP/1.1 Accept: */* Referer: http://www.yourfilehost.com/flash/flvplayer7.swf?autoStart=0&no_skin_menu=1&video=http%3A%2F%2Fwww.yourfilehost.com%2Fvideo-embed.php%3Fvidl x-flash-version: 9,0,28,0 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322) Host: cdn.yourfilehost.com Connection: Keep-Alive Cookie: media_views=2; yfh_cids=%7C1227611186%3A3adc5c53ef9234cdc53dc01d0b5d7a1b%7C%7C1227611609%3Acd94412311d23d23d447497cdc5f5761%7C; yfh_views=2; yfh_ac=active; yfh_af=off; __utma=83567518.757577051.1227611203.1227611203.1227611203.1; __utmb=83567518; __utmc=83567518; __utmz=83567518.1227611203.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none); cdnlock=ip%3D219.126.168.136%7Eexpires%3D1227611913%7Eaccess%3D%2Funit1%2F%2A%7Emd5%3Dd152af2c2fff38e8ba0e9b174ac7ec46; from=noref; lfrom=noref; idcheck=1227611204; count_visits=1
cookieがないと403 Forbiddenが返されてダウンロードできない。また、目的のflvのURL側からないとダウンロードできない。このCookieとURLを取得するのにもう一手間必要。
cookieとURL取得のためのリクエストヘッダ
GET /video-embed.php?vidlink=&cid=3adc5c53ef9234cdc53dc01d0b5d7a1b&adult=1&cat=video&file=rankouplay_part01.wmv&family=off&key=219.126.168&cdn=1&f=flash8&? HTTP/1.1 Accept: */* Referer: http://www.yourfilehost.com/flash/flvplayer7.swf?autoStart=0&no_skin_menu=1&video=http%3A%2F%2Fwww.yourfilehost.com%2Fvideo-embed.php%3Fvidl x-flash-version: 9,0,28,0 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322) Host: www.yourfilehost.com Connection: Keep-Alive Cookie: media_views=1; yfh_cids=%7C1227611186%3A3adc5c53ef9234cdc53dc01d0b5d7a1b%7C; yfh_views=1; yfh_ac=active; yfh_af=off; __utma=83567518.757577051.1227611203.1227611203.1227611203.1; __utmb=83567518; __utmc=83567518; __utmz=83567518.1227611203.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none)
URLは/media.php?cat=video&file=rankouplay_part02.wmvで送られてきた内容にかかれている。これで送られてきた内容を解析してダウンロードするflvのURLを決める。
ブラウザアドレス入力欄に書いたアドレスの取得時に流れたリクエストヘッダ
GET /media.php?cat=video&file=rankouplay_part02.wmv HTTP/1.1 Accept: */* Accept-Language: ja Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322) Host: www.yourfilehost.com Connection: Keep-Alive Cookie: __utma=83567518.757577051.1227611203.1227611203.1227611203.1; __utmz=83567518.1227611203.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none)
なぜかcookieがセットされている。あらかじめ消しておいたはずだが。
未ダウンロード部分へのシーク
未ダウンロード部分へのシークができればその際に流れたパケットをみて分割ダウンロード部分を書こうと思ったのだが、yourfilehostの場合シークできるのはダウンロード済みの部分だけ。と言うことで分割ダウンロードはyourfilehost側からすればあまりうれしくないことなのかもしれない。