綾小路龍之介の素人思考

[Services] YourFileHost ダウンローダ

エロりましょうぜ奥さんや。それはそれとて何個目の車輪なのかわからんが YourFileHost.com からダウンロードするスクリプトを書いてみた。とりあえず晒してみよう。wget では分割ダウンロードに対応して無いようなので、curl を使ってみた。ダウンロードできないものがたまにあるが、そこは気にせず。

目次

つかいかた

$ 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ダウンローダの参考文献

  1. yourfilehostの裏API (Yusukebe::Tech)
  2. 2008-02-03 - 駆け馬に無知
  3. McURL - Multiple cURL
  4. cURL - How To Use (マニュアルページ日本語訳)
  5. cURL - Manual - Japanese Translation
  6. シェルスクリプトのデバッグ - UNIX & Linux コマンド・シェルスクリプト リファレンス
  7. curlの使い方
  8. 【コラム】OS X ハッキング! (33) それにつけてもダウンローダはcurl | パソコン | マイコミジャーナル
  9. curl リクエスト|request ヘッダ|header 保存 - Google 検索
  10. コマンドラインからでもブラウザからでもWgetでダウンロード - SourceForge.JP Magazine
  11. Linuxの使い方 - シェルスクリプトの作り方
  12. 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側からすればあまりうれしくないことなのかもしれない。

ソーシャルブックマーク

  1. はてなブックマーク
  2. Google Bookmarks
  3. del.icio.us

ChangeLog

  1. Posted: 2003-10-19T06:16:04+09:00
  2. Modified: 2003-10-19T13:42:30+09:00
  3. Generated: 2017-07-09T23:09:23+09:00