こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

締切り済みの質問

curlで外部ファイル記載を1行ずつ処理するには?

いつもお世話になっております。
よろしくお願い致します。

プログラムの知識が無いため、自力で調べて何とか下記の所までテストが出来ております。
curl 'https://www.hogehoge.com/aaaaa/bbbbb/ccccc/ddddd/page[8-12]' -H 'Host: www.hogehoge.com' -o "page#1.html"
※実際は上記には「Referer」や「User-Agent」など付いていますが、長くなってしまうのでここでは削除してあります。


実際にやりたい事は、上記の[8-12]の個所に、連番では無い様々な数字を入れて実行したいと思っています。
入力する数字が1行ごとに記載されている外部ファイルを読み込んで、[8-12]の個所に代入して実行するにはどのようにすれば良いでしょうか?
例えば外部ファイル(test.txt)には下記のように記載されています。
41433135
25342545
54236462
33436336
57375353
※もっと続きます

1行ずつ読み込んで「41433135.html」「25342545.html」「54236462.html」などと、それぞれファイルに保存するようにしたいです。


***.shと言うファイルにプログラムを作って記述すれば、上記の事ができそうな気がしますが、いろいろと調べてみても私ではどのように応用して記述すれば良いのか分からずお手上げな状態です。

おそらくプログラムに詳しい方であれば、この問題はすぐに解決できるレベルのような気がしますが、記述方法を教えて頂ければ助かります。
私の環境はMacで、ターミナルを利用しております。

すでにこの問題で2週間ほど調べてみましたが、もう限界な状態です。
どなたかお助け下さい。

何卒、よろしくお願い致します。

投稿日時 - 2018-10-24 12:11:55

QNo.9550814

困ってます

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(7)

ANo.7

> 上記を参考にして、まずは下記のように実行文を書いてみました。
> sh test.sh -h ./h.txt -u 'https://www.bar.com/dir/base' ./test.txt
> h.txtとtest.txtのパスを併せただけです。

あ、ごめん。間違ってました。-h (小文字) じゃなく -H

> 次に下記のように書いてみました。
> sh test.sh -H h.txt test.txt

が正しいです。

> 上記の分で実行してみると下記のように表示されました。
> 41433135
> sed: --: No such file or directory

あー sed の仕様違いが原因でした。
args="$(sed -n "$(rand 1 $hnum)p" -- "$htxt")"
の行 -- が余分、もしくは < に修正してください
args="$(sed -n "$(rand 1 $hnum)p" < "$htxt")"
Linux などの GNU sed では -- 入れてても警告出ないので気付いてませんでしたが、厳密には書き方が悪いので警告されても致し方ない感じです。MacOS や Darwin の sed は元々 FreeBSD の sed なのでそのへん少し restrict なんです。

> 1時間ほどいろいろと試してみましたが、上手く出来ず頭から煙が出てきました(>_<)

ごめんなさい。何箇所か直したやつここに置いときます
https://paiza.io/projects/KA4T3BQdW1b4J8Q8aRqEsg

> アクセススピード以外に、どうやら制限内容の中には「ウェブブラウザのJavaScript設定が無効」とみなされると制限にひっかかってしまうようです。

これはやむをえないですね。

> trapezium様にはこんな依頼に付き合って頂き大変恐縮しております。

まあ平気です。ただ責任は持てないのでそこらへんは...

投稿日時 - 2018-11-05 18:57:16

お礼

お返事が遅れてしまい、申し訳ありません。
修正版のプログラムありがとうございます。

ここ数日いろいろと試してみましたが、やはりしばらくアクセスすると制限にひっかかってしまいました。
ブラウザからの連続アクセスでは、ほとんど制限にはひっかからないので、もしかしたらJavaScript可否による制限を受けている可能性が高いかもしれません。
でも最初は問題なくアクセス出来ているので、その他の要因も考えられますが、今の私には分かりません。

どのサイトなのかtrapezium様に直接お伝え出来れば、trapezium様であれば何か解決策が分かるかもしれません。
ただお伝えする術が無いのが非常に残念でなりません。

ご尽力に感謝致します。
もう少しこのまま模索してみて、またご連絡を差し上げたいと思います。

投稿日時 - 2018-11-08 14:07:21

ANo.6

> 例えば+5の個所を6にすれば6~9秒になるのでしょうか。

そうです。同じように 4 のところ 10 にすれば 5-14 となります。

> test.txtを上から1行ずつ読み込むと同時に、h.txtからランダムに-H一組を読み込みアクセスする、と言った具合です。

なるほど、ちょっと面白そうなので適当に実装してみました。
------cut here----
#!/bin/sh

curl="curl"
#curl="sh b"
curlopts="-s"
url='https://www.hogehoge.com/aaaaa/bbbbb/ccccc/ddddd/page'
dry=
htxt="h.txt"

rand() {
local r=${RANDOM:-$(dd if=/dev/urandom bs=2 count=1 2>/dev/null | od -d -An)}
test -z "$r" && echo "$0: Don't generate random number" >&2
test $# -gt 1 && : $((r %= $2))
echo $((r + $1))
}
usage() {
echo "Usage: $0 [-d] [-o curlopts] [-u URL] [-H h.txt] files" 1>&2
exit 1
}

while getopts "dhH:o:u:" opt; do
case $opt in
d) dry="echo" ;;
o) curlopts="${OPTARG}" ;;
u) url="${OPTARG}" ;;
H) htxt="${OPTARG}" ;;
h) usage ;;
esac
done
shift $((OPTIND - 1))

hnum=$(wc -l < "$htxt")
test -z "$hnum" -o "$hnum" = 0 && usage
grep -v '^[[:blank:]]*$\|^[[:blank:]]*#' -- $@ | while IFS= read i; do
echo "$i"
args="$(sed -n "$(rand 1 $hnum)p" -- "$htxt")"
eval $dry "$curl $curlopts $args" '-o "${i}.html" -- "${url}$i"'
$dry sleep $(rand 6 4)
done
------cut here------
h.txt にいつだかの
-H 'Host: ~' -H 'User-Agent: ~' -H 'Referer: ~' -H 'Cookie: ~'
の形式で空行なく準備しておき

./test.sh -h
Usage: test.sh [-d] [-o curlopts] [-u URL] [-H h.txt] files
-d: テスト実行
-o: curl -h のオプションを直接指定。-o '-v --proxy "http://proxy.foo.com:8080"' などで一時的に試せます。スクリプト内の変数 curlopts をオーバーライド
-u: URL 指定。-u 'https://www.bar.com/dir/base' これも変数 url を上書き
-H: h.txt のパス指定。-h ../dir/file

実際に指定するときは
./test.sh -h ../dir/h.txt -u 'https://www.bar.com/dir/base' ../dir/test.txt

動作テストはほぼ -d つきで試しただけ
h.txt から sed 使ってるけど、bash 専用なら配列使った方が簡単かもしれない
test.txt, h.txt 共に日本語含む場合は文字コードに気をつける

投稿日時 - 2018-11-04 10:41:41

お礼

引き続きアドバイスありがとうございます。

お時間を多く割いて、新しくプログラムを書いて頂きありがとうございます。
私にはほとんど理解不能の領域になってますが、頑張って付いて行こうと思ってます。

test.shに教えて頂いたプログラムを記載して保存。
test.txtにはアクセス先のページの番号のリスト。
h.txtには-H一式を各行に記載。
上記3ファイルを同階層においている状態です。

>実際に指定するときは
>./test.sh -h ../dir/h.txt -u 'https://www.bar.com/dir/base' ../dir/test.txt
上記を参考にして、まずは下記のように実行文を書いてみました。
sh test.sh -h ./h.txt -u 'https://www.bar.com/dir/base' ./test.txt
h.txtとtest.txtのパスを併せただけです。

これを実行したら下記のように表示されました。
Usage: test.sh [-d] [-o curlopts] [-u URL] [-H h.txt] files
ん~、何か私のやり方が間違っていそうですね。
と言うより、間違っているから上記のように表示されるのですよね。

次に下記のように書いてみました。
sh test.sh -H h.txt test.txt
※[-d] [-o curlopts] [-u URL]の個所は実際に実行する時は必要が無いと思い削除しました。

上記の分で実行してみると下記のように表示されました。
41433135
sed: --: No such file or directory
25342545
sed: --: No such file or directory
※これが続くのですぐに終了させました

「No such file or directory」と言う事は、test.shと同階層に置いているh.txtやtest.txtのパスの指定の仕方が悪いのでしょうか。
1時間ほどいろいろと試してみましたが、上手く出来ず頭から煙が出てきました(>_<)

◆追加情報
追加で制限についても調査してみました。
アクセススピード以外に、どうやら制限内容の中には「ウェブブラウザのJavaScript設定が無効」とみなされると制限にひっかかってしまうようです。

その他、試しにブラウザ上からページのリンクを連続して100ページくらい開いてみましたが、1ページのみ制限にひっかかってしまっただけで、その他は無事に表示されました。
アクセススピードよりも、curlではJavaScriptが有効になっていないから、制限にひっかかってしまう可能性もあるのかなぁと思いました。
でも-Hでいろいろと付加させているからJavaScriptの件は問題ないのかな。

難しい。。。頭が痛くなってきます。

trapezium様にはこんな依頼に付き合って頂き大変恐縮しております。
申し訳ありません。

投稿日時 - 2018-11-05 01:58:24

ANo.5

余談みたいなものですが、

> 一定時間ごとにアクセスするのがダメなのかな。

それも出来無くはないですけど、一定時間の接続数上限かな。サーバの混雑具合にもよるかもしれませんし、まあ相手サイトの管理ポリシー次第でなんとも言えんですね。

> sleep時間を例えば5~8のランダムにすると言う事は可能でしょうか?

sleep の行を以下の二行に、

sec=${RANDOM:-$(dd if=/dev/urandom bs=2 count=1 2>/dev/null | od -d -An)}
sleep $((${sec:-7} % 4 + 5))

sec に 15 or 16 bit の乱数、4 の剰余 (0-3) に 5 足して 5-8 です。

あと乱数ジェネレータですけど、Mac や Linux なら大抵は bash なんで $RANDOM 直接でもいい。/dev/urandom 見てるのは sh の場合の用心ですが、どちらも POSIX 拡張機能なので使えない OS もあります。(移植するとき考慮)

投稿日時 - 2018-11-02 22:03:31

お礼

アドバイスありがとうございます。
例えば+5の個所を6にすれば6~9秒になるのでしょうか。

sleep時間をランダムにして実行してみました。
最初はtest.txtの58行目まで出来ました。
次に-Hの個所を書き換えて実行した所、40行目まで実行出来ました。

ここから推論すると、-Hを書き換えるれば制限解除できる事から、一定時間内の接続上限は無いように思います。
別ファイル(例:h.txt)に-Hの内容を一式を一組として、1行に一組ずつ記載するようにして、あらかじめh.txtに十数行書き込んだ状態にして、$curl $curlopts以降に今まで記載していた-H内容を、h.txtからランダムに読み込んでアクセスする方法は出来るでしょうか。
test.txtを上から1行ずつ読み込むと同時に、h.txtからランダムに-H一組を読み込みアクセスする、と言った具合です。

毎回-Hの内容を変えてアクセスできれば、より手動的なアクセスに近い状態になり、制限を回避しやすくなると思います。

試してみる価値はあると思うのですが、h.txtからランダムで選択する事は技術的に難しいでしょうか。

よろしくお願い致します。

投稿日時 - 2018-11-03 16:44:43

ANo.4

> test.txtの内容を上から1秒に1回1行ずつ読み込むなどの応用は出来るのでしょうか?

これだけで回避可能なのであれば done の前の行に
sleep 1
を入れればいいでしょう。(1秒待ち)

> 上記の理由により、アクセススピードによって制限にひっかかってしまうのだと推論しました。

ただアクセス制限には、接続数、スピード、連続アクセス数など複数レベルある場合もありますし、ダウンロード繰替えしてると自動的(手動でも)に制限強化される場合ありますから注意してください。(相手サイトと話が付いているなら問題ないが、最悪ブラックリスト入れられて IP で落とされる)

投稿日時 - 2018-10-29 17:48:39

お礼

お返事が遅れてしまい、大変申し訳ありません。

trapezium様のご教授により、当初考えていたような動作をさせる事が出来ました。
ただアクセス先の制限も予想以上に厳しいもので、sleepを通常アクセスであり得る範囲の7まで引き上げて実行してみましたが、途中で制限にひっかかってしまいました。

接続数、接続スピードはクリアしていると思われるのですが、なかなか難しいですね。
アクセス先のURLも全て異なるので、手動でアクセスする場合でもあり得る範囲だと思うのですが、他にも分からない事で制限があるのかもしれないですね。

一定時間ごとにアクセスするのがダメなのかな。
sleep時間を例えば5~8のランダムにすると言う事は可能でしょうか?

投稿日時 - 2018-11-02 06:37:49

ANo.3

> 下記のような記述で良いのでしょうか?
> この記述では上手くヘッダーを付けてアクセス出来ていないような気が。。。
> header= -H 'Host: ~' -H 'User-Agent: ~' -H 'Referer: ~' -H 'Cookie: ~'

あー前回も書きましたけど、こういう書き方は正しくないです。
変数に値を設定するのには '' and "" で一括り
curl="curl -s"
header='Host: www.hogehoge.com'
後ろに何か続けると全く違う意味になります。

やるなら curl の引数に直接
$curl $curlopts -H 'Host: ~' -H 'User-Agent: ~' -H 'Referer: ~' -H 'Cookie: ~' -o "${i}.html" -- "${url}$i"
指定するか、前回の回答みたいに
$curl $curlopts -H "$header" -A "$agent" -e "$referer" -b "$cookie" -o "${i}.html" -- "${url}$i"
みたいにしないといけません。(他にもやりようはあるけど、詳細はそれこそシェルスクリプトの基本は最低限理解されていないと説明のしようありません)

投稿日時 - 2018-10-26 22:24:35

お礼

お返事が遅れてしまい、申し訳ありません。
記述方法の意味がやっと分かりました。

-Hを使って直接記述する場合は「$curl $curlopts」の後に記述しなければならなかったのですね。
修正して記述したら、動作する事が出来ました。
ありがとうございます。

ただアクセス先の制限なのか、ある程度の早さでアクセスするとエラーとなって正しい内容でページが表示されない(ページ内容をDLできない)ようになってしまっていました。

test.txtの内容を上から1秒に1回1行ずつ読み込むなどの応用は出来るのでしょうか?
何回か実行してみましたが、毎回読み込みのスピードが異なり、早く読み込まれた場合は2~3行目くらいから制限にひっかかってしまう場合もありますが、たまたま読み込みが遅い時は3~4行目くらい無事に正しくDL出来る事もありました。

上記の理由により、アクセススピードによって制限にひっかかってしまうのだと推論しました。

時間的な制御が出来るのか分かりませんが、何か制御方法があればご教授頂ければ幸いです。

よろしくお願い致します。

投稿日時 - 2018-10-29 09:39:53

ANo.2

> ですが、下記のようなエラーが表示されてしまいました。
> test.sh: line 7: unexpected EOF while looking for matching `''

このエラーは '' or "" のペアが一致してないということなので、表示された line 7 より前に問題あることが多いです。

> test.sh: line 11: syntax error: unexpected end of file

これも最初の問題に付随するもの

> また「header='Host: www.hogehoge.com'」の個所についてですが、その他の「Referer」や「User-Agent」も付ける場合は下記のようにスペースを開けて記述すれば良いのでしょうか。
> header='Host: www.hogehoge.com' 'User-Agent: Mozilla~' 'Referer: ~' 'Cookie: ~'など

いや良くないです。構文エラーかコマンドとして実行しようとしてるかも、ここらが原因で上のエラー起してると思う。

複数のヘッダ渡すなら -H を並べるか、この三つなら専用オプション使うか
User-Agent/-A, --user-agent, Referer/-e, --referer, Cookie/-b, --cookie

agent='Mozilla~'
referer='http://www.hogehoge.com'
cookie='foo=n; bar=m'
しておき curl の引数に -A "$agent" -e "$referer" -b "$cookie" 追加するとか、変数使わず -A 'Mozilla~' とかしても悪くはない。ただ行が長くなってちょっと見難いだけ

-H 使うときは "User-Agent: " などが頭に必要
あと () や ; のエラーというのも、ちゃんと "" or '' で囲まれてないから

そういえば grep の ^[[:blank:]]*$ は空行、^[[:blank:]]*#' は # で始まる行をコメントと見倣すものです。どちらもいらぬ心配なら cat $@ に置き換えを、片方削除するなら \| を含めて削ればいいです。

投稿日時 - 2018-10-25 13:44:24

お礼

引き続きご教授ありがとうございます。

headerについては単純に-Hを並べる事にしました。
下記のような記述で良いのでしょうか?
この記述では上手くヘッダーを付けてアクセス出来ていないような気が。。。
header= -H 'Host: ~' -H 'User-Agent: ~' -H 'Referer: ~' -H 'Cookie: ~'


上記のように記述して実行すると、前回のようなエラーは出なくなったのですが、アクセス先のページのソースがターミナルの中に表示されるだけで、「41433135.html」「25342545.html」「54236462.html」というようにファイル保存されませんでした。

>そういえば grep の ^[[:blank:]]*$ は空行、^[[:blank:]]*#' は # で始まる行をコメントと見倣すものです。
これは念のために削除しないで記述したままにしておこうと思います。


アクセス先には「Host」や「User-Agent」などによって表示内容が変わる個所があり(ある意味アクセス制限に近い仕組みなのかも)、上記の方法では上手く「Host」や「User-Agent」などの情報を渡せていないページの内容になっていました。


単発的に下記のように記述すると、希望する内容のページが表示されるのは確認出来ています。
curl 'https://www.hogehoge.com/aaaaa/bbbbb/ccccc/ddddd/page{41433135}' -H 'Host: www.hogehoge.com' -o "page#1.html"
※実際には「Referer」や「User-Agent」などが付いています


そのため、「header= -H 'Host: ~' -H 'User-Agent: ~' -H 'Referer: ~' -H 'Cookie: ~'」と言う記述では、上手く情報を渡せていないと考えた理由です。
もし上記の記述方法に問題が無ければ、その他の要因で上手く希望するページ内容が表示されない事になりますが、trapezium様のご意見をお聞きするしか私には方法はありません。


何度も申し訳ありません。
よろしくお願い致します。

投稿日時 - 2018-10-25 18:53:33

ANo.1

要はシェルスクリプトにしたいということなんですよね?
やり方は色々あるけど例えばこんな感じ
--------
#!/bin/sh

curl="curl -s"
url='https://www.hogehoge.com/aaaaa/bbbbb/ccccc/ddddd/page'
header='Host: www.hogehoge.com'

grep -v '^[[:blank:]]*$\|^[[:blank:]]*#' $@ | while IFS= read i; do
echo "$i"
$curl $curlopts -H "$header" -o "${i}.html" -- "${url}$i"
done
----------

実行は
sh test.sh test.txt
あるいは一度
chmod +x test.sh しておけば
./test.sh test.txt

投稿日時 - 2018-10-24 22:51:11

お礼

ご回答ありがとうございます。
早速試してみました。

ですが、下記のようなエラーが表示されてしまいました。
test.sh: line 7: unexpected EOF while looking for matching `''
test.sh: line 11: syntax error: unexpected end of file

エディターで見ると7行目は「grep -v '^[[:blank:]]*$\|^[[:blank:]]*#' $@ | while IFS= read i; do」の個所で11行目はdone(doneは10行目)の後の何も無い行です。

また「header='Host: www.hogehoge.com'」の個所についてですが、その他の「Referer」や「User-Agent」も付ける場合は下記のようにスペースを開けて記述すれば良いのでしょうか。
header='Host: www.hogehoge.com' 'User-Agent: Mozilla~' 'Referer: ~' 'Cookie: ~'など

またUser-Agentに「()」が含まれているとエラーが発生してしまいましたが、()を削除したらUser-Agentあたりのエラーはなくなりました。
上記のことで関連して、その他気になったのはヘッダーに付け加える「Accept-Language」や「Cookie」には「;」が含まれていますが、特にエラー表示されていないので、「;」が含まれていても問題ないと言う認識で良いでしょうか。

お手数をおかけして、申し訳ありません。
ご教授をお願い致します。

投稿日時 - 2018-10-25 08:48:03

あなたにオススメの質問