curlコマンドはURLでデータ通信を行うことができます。サポートしているプロトコルはHTTP,FTP,SMTP等で、様々な通信に対して利用することができます。ここでは主にHTTPでのWebページ取得方法について紹介します。
目次
curlコマンドの紹介の前に
この記事では、curlコマンドの利用するために、CGIが動作する簡単なWebサーバをローカル環境で構築しています。CGIが動作する簡単なWebサーバはpythonのバージョン2系のCGIHTTPServerモジュールを用いて構築します。
構築方法は単純で
1 |
python -m CGIHTTPServer 3000 |
と実行するだけで、カレントディレクトリをドキュメントルートとしてローカル環境に簡単なWebサーバを構築できます。また、3000の数字はポート番号を表しています(ポート番号を省略するとデフォルトでは8000番ポートになります)。
CGIが動作するディレクトリは決められていて、cgi-bin/又はhtbin/の中に存在するファイルがCGIスクリプトとして扱われます。
python3では、http.serverモジュールを用いてWebサーバを構築できます。--cgiオプションを用いると同様にCGIが動作するWebサーバを構築できます。
1 |
python3 -m http.server --cgi 3000 |
curlコマンドの利用例
GETメソッドによるWebページの取得
curlコマンドは、引数にURLを入力することで、GETメソッドでWebページを取得することができます。wgetコマンドと違い、Webページは標準出力されます。そのため、パイプでコマンドを繋げることができ、sedやawk等のコマンドと組み合わせて、結果の出力の編集を行うことが出来ます。
コマンド例
1 |
curl http://localhost:3000/test.html |
test.html
1 2 3 4 5 6 7 8 9 10 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>curl command Test</title> </head> <body> <h1>Hello World</h1> </body> </html> |
実行結果
1 2 3 4 5 6 7 8 9 10 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>curl command Test</title> </head> <body> <h1>Hello World</h1> </body> </html> |
test1.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>test1.html</title> </head> <body> <p>test1.html</p> </body> </html>
test2.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>test2.html</title> </head> <body> <p>test2.html</p> </body> </html>
コマンド例と実行結果
$curl "http://localhost:3000/test{1,2}.html"[1/2]: http://localhost:3000/test1.html --> <stdout> --_curl_--http://localhost:3000/test1.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>test1.html</title> </head> <body> <p>test1.html</p> </body> </html>[2/2]: http://localhost:3000/test2.html --> <stdout> --_curl_--http://localhost:3000/test2.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>test2.html</title> </head> <body> <p>test2.html</p> </body> </html>
取得したWebページをファイルに保存
(-oオプション)
-oオプションを用いることで、取得したWebページを指定したファイルに保存することができます。
出力がファイルやパイプ等のように端末でない場合、curlコマンドは基本的に進捗バーを表示します。
コマンド例と実行結果
1 2 3 4 5 6 7 |
$curl -o file.html http://localhost:3000/test.html % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 130 100 130 0 0 85022 0 --:--:-- --:--:-- --:--:-- 126k $ $ls file.html |
また、'{}'や'[]'を用いて、複数のファイルをダウンロードする場合、使用した括弧の数に従って、'#1','#2','#3'...を用いることで、置き換えられる文字列を使用して、ファイルをダウンロードできます。また、'{}'や'[]'を用いる場合はシェルに解釈されないように引用符(「' '」や「" "」)で囲います。
コマンド例と実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
$curl -o "#1test#2.html" "http://localhost:3000/{aaa,bbb,ccc}test[1-2].html" [1/6]: http://localhost:3000/aaatest1.html --> aaatest1.html % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 130 100 130 0 0 88797 0 --:--:-- --:--:-- --:--:-- 126k [2/6]: http://localhost:3000/aaatest2.html --> aaatest2.html 100 130 100 130 0 0 209k 0 --:--:-- --:--:-- --:--:-- 209k [3/6]: http://localhost:3000/bbbtest1.html --> bbbtest1.html 100 130 100 130 0 0 234k 0 --:--:-- --:--:-- --:--:-- 234k [4/6]: http://localhost:3000/bbbtest2.html --> bbbtest2.html 100 130 100 130 0 0 217k 0 --:--:-- --:--:-- --:--:-- 217k [5/6]: http://localhost:3000/ccctest1.html --> ccctest1.html 100 130 100 130 0 0 202k 0 --:--:-- --:--:-- --:--:-- 202k [6/6]: http://localhost:3000/ccctest2.html --> ccctest2.html 100 130 100 130 0 0 196k 0 --:--:-- --:--:-- --:--:-- 196k $ $ls aaatest1.html bbbtest1.html ccctest1.html aaatest2.html bbbtest2.html ccctest2.html |
GETメソッドによるクエリ文字列の送信
GETメソッドでデータを送信する方法として、URLにクエリ文字列を追加して送信する方法があります。記述方法はhttp://www.example.com/?aaa=testのように、URLの後に'?'を記述し、その後にname=value&name2=value2のように記述することでデータを送信することができます。
curlコマンドでクエリ文字列を記述する場合は、シェルに解釈されないように一部の文字列はエスケープする必要があります。
コマンド例
1 |
curl http://localhost:3000/cgi-bin/test.cgi/?aaa=test'&'name=value'&'name2=value2 |
test.cgi
1 2 3 4 5 6 7 |
#!/bin/bash echo "Content-Type:text/plain" echo echo "QUERY_STRING: $QUERY_STRING" echo "REQUEST_METHOD: $REQUEST_METHOD" |
実行結果
1 2 |
QUERY_STRING: aaa=test&name=value&name2=value2 REQUEST_METHOD: GET |
POSTメソッドによるデータの送信
-Xオプションにより、HTTPサーバ(Webサーバ)へのリクエストメソッド(request method)を指定することができます。この-Xオプションを用いて、GETメソッドの他にPOST,PUT,DELETE等のリクエストメソッドを指定することができます。
POSTメソッドはHTMLのフォームのデータ送信、掲示板でのメッセージ投稿処理などに利用されます。
ここでは、例としてCGIプログラムにPOSTメソッドを用います。
コマンド例
1 |
curl -XPOST -d'name=taro&value=87' http://localhost:3000/cgi-bin/test2.cgi |
test2.cgi
1 2 3 4 5 6 7 8 9 |
#!/bin/bash echo "Content-Type:text/plain" echo if [ "$REQUEST_METHOD" = "POST" ]; then read -N $CONTENT_LENGTH postdata echo $postdata fi |
実行結果
1 |
name=taro&value=87 |
Cookieの保存
-cオプションを用いることでWebサイトから送信されるCookieを保存することができます。
Cookieとは、HTTPで状態(state)を保存するために利用されます。状態とは、あるサイトでのログイン状態や言語設定などが挙げられます。
ここでは、CGIプログラムにCookieヘッダーをセットし、それを保存する例を示します。
cookie.cgi
1 2 3 4 5 6 |
#!/bin/bash echo "Set-Cookie: name=taro" echo "Set-Cookie: value=123" echo "Content-type: text/plain" echo echo "cookie test" |
コマンド例と実行結果
1 2 3 4 5 6 7 8 9 10 |
$curl -c cookiefile http://localhost:3000/cgi-bin/cookie.cgi cookie test $ $cat cookiefile # Netscape HTTP Cookie File # http://curl.haxx.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. localhost FALSE /cgi-bin/ FALSE 0 name taro localhost FALSE /cgi-bin/ FALSE 0 value 123 |
Cookieの利用
-bオプションを用いることで、Cookieとしてデータを送信することができます。Cookieとしてデータを送る場合、文字列またはファイルを指定します。
show-cookie.cgi
1 2 3 4 |
#!/bin/bash echo "Content-type: text/plain" echo echo Cookie: "$HTTP_COOKIE" |
cookiefile
1 2 3 4 5 6 |
# Netscape HTTP Cookie File # http://curl.haxx.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. localhost FALSE /cgi-bin/ FALSE 0 name taro localhost FALSE /cgi-bin/ FALSE 0 value 123 |
コマンド例と実行結果
1 2 3 4 5 |
$curl -b cookiefile http://localhost:3000/cgi-bin/show-cookie.cgi Cookie: name=taro; value=123 $ $curl -b 'name=yamada; value=987' http://localhost:3000/cgi-bin/show-cookie.cgi Cookie: name=yamada; value=987 |
curlコマンドの応用
xmllintコマンドと組み合わせWebスクレイピング
Webスクレイピングとは、ウェブサイトから欲しい情報を抽出することを言います。curlコマンドとxmllintコマンドを組み合わせることでウェブサイトから欲しい情報を抽出することが出来ます。
xmllintコマンドはxmlやhtmlを解析できるコマンドになります。このxmllintコマンドの--htmlオプションと--xpathオプションを用いてHTMLから任意のタグの要素を抽出することが出来ます。XPathの構文仕様はW3CのXPathの仕様に規定されています。
しかし、XPathの構文について、あまり知らなくても、ブラウザから欲しい要素のXPathをコピーすることも出来ます。
例えば、Google Chromeでは情報が欲しい箇所に右クリックを押し、「検証」をクリックすることで、デベロッパーツールを開くことができ、さらにそのデベロッパーツールの画面で欲しい要素に対して、右クリックを押し、「コピー」から「XPathをコピーする」項目があります。ここから簡単にXPathを取得することできます。
xmllintコマンドで要素を抽出することができたら、抽出した文字列を編集していき、欲しい文字列に整形していきます。欲しい文字列に整形するためにはsedやawk等のコマンドを用いることが出来ます。
以下に簡単なWebスクレイピングのコマンド例と実行結果を示します。コマンド例はこのサイトのトップページから3ページまで記事タイトルとそのURLを表示する例になります。
コマンド例
1 2 3 4 |
curl "https://linuxcommand.net" "https://linuxcommand.net/page/[2-3]/" | xmllint --html --xpath '//div[@id="list"]//a[contains(@class,"entry-title")]' - 2>/dev/null | sed -n -e's!<a[^>]*href="\([^"]*\)"[^>]*>!\1 !g' -e 's!</a>!</a>\n!g' -e 's!<[^>]*>!!g' -e 'p' | nl |
実行結果例
1 https://linuxcommand.net/python-cgi-web-server/ PythonワンライナーでWebサーバを構築しCGIプログラムを動かしてみる 2 https://linuxcommand.net/help/ help - Bashの組み込みコマンドの使い方を確認する 3 https://linuxcommand.net/tar/ tar - tar形式のアーカイブファイルを作成する 4 https://linuxcommand.net/alias/ alias - コマンドを別の文字列に置き換える 5 https://linuxcommand.net/gzip/ gzip - ファイルをgz形式に圧縮する 6 https://linuxcommand.net/xargs/ xargs - 標準入力と引数を組み合わせコマンドを実行する 7 https://linuxcommand.net/find/ find - ファイルを検索しファイルリストを出力する 8 https://linuxcommand.net/edit-exec-command/ bashでエディタを呼び出しコマンドを一気に実行する方法 9 https://linuxcommand.net/seq/ seq - 数列を出力する 10 https://linuxcommand.net/split/ split - ファイルを分割する 11 https://linuxcommand.net/select/ select - 選択肢から変数に代入してコマンドを実行する 12 https://linuxcommand.net/read/ read - 標準入力から変数に代入する 13 https://linuxcommand.net/null-command/ :(ヌルコマンド) - 何もしないコマンド 14 https://linuxcommand.net/md5sum/ md5sum - 128ビットのメッセージダイジェストを計算する 15 https://linuxcommand.net/tr/ tr - 文字を変換または削除する 16 https://linuxcommand.net/rmdir/ rmdir - 空のディレクトリを削除する 17 https://linuxcommand.net/uniq/ uniq - 同じ行を繰り返さずに表示する 18 https://linuxcommand.net/mkdir/ mkdir - ディレクトリを作成する 19 https://linuxcommand.net/nice/ nice - プロセスの優先度を変更するためのナイス値を変更する 20 https://linuxcommand.net/env/ env - 環境変数を一時的に修正してコマンドを実行する 21 https://linuxcommand.net/chroot/ chroot - ルートディレクトリを変更しコマンドを実行する 22 https://linuxcommand.net/mknod/ mknod - 特殊ファイルを作成する 23 https://linuxcommand.net/chmod/ chmod - アクセス権限を変更する 24 https://linuxcommand.net/bash-history-fc/ コマンド履歴を編集してコマンドをまとめて実行する 25 https://linuxcommand.net/chown/ chown - ファイルの所有者やグループを変更する 26 https://linuxcommand.net/id/ id - ユーザIDやグループIDを表示する 27 https://linuxcommand.net/whatis/ whatis - マニュアルページの名前から検索し概要を表示する 28 https://linuxcommand.net/mktemp/ mktemp - 一時的なファイルやディレクトリを作成する 29 https://linuxcommand.net/rm/ rm - ファイルを削除する 30 https://linuxcommand.net/mv/ mv - ファイルの移動や名前変更をする 31 https://linuxcommand.net/cp/ cp - ファイルをコピーする 32 https://linuxcommand.net/install/ install - ファイルの権限などを設定してコピーする 33 https://linuxcommand.net/ptx/ ptx - 文章から索引を作成する 34 https://linuxcommand.net/expr/ expr - 式を評価する 35 https://linuxcommand.net/fold/ fold - 長い行を折り返す 36 https://linuxcommand.net/nl/ nl - 行番号を追加する 37 https://linuxcommand.net/sleep/ sleep - 指定した時間だけ停止する 38 https://linuxcommand.net/uname/ uname - システムの情報を表示する 39 https://linuxcommand.net/head/ head - ファイルの最初の部分を表示する 40 https://linuxcommand.net/nproc/ nproc - プロセッサの数を表示する 41 https://linuxcommand.net/fmt/ fmt - 文章を整形する 42 https://linuxcommand.net/ruby-oneliner/ Rubyワンライナーを使ってみる 43 https://linuxcommand.net/od/ od - ファイルをバイナリで表示する 44 https://linuxcommand.net/comm/ comm - ソート済みファイルの行を比較する 45 https://linuxcommand.net/test/ test - ファイルチェック・値の比較をする
コマンド例の簡単な解説を行います。まず初めにcurlコマンドでWebページの取得を行います。
1 |
curl "https://linuxcommand.net" "https://linuxcommand.net/page/[2-3]/" |
その後に、パイプ(|)で繋いでxmllintで取得したい要素を抽出します。これはHTMLのソースと比較しながら行っていきます。
xmllintは引数にファイルを指定する必要がありますが、標準入力を指定する場合は、'-'を指定します。
また、xmllintでHTMLタグとして解釈されないタグがある場合、エラーメッセージが出力されるため、2>/dev/nullでエラーメッセージを捨てています。
そして、抽出した要素を編集するために次のsedコマンドにパイプで渡します。
1 |
xmllint --html --xpath '//div[@id="list"]//a[contains(@class,"entry-title")]' - 2>/dev/null |
sedコマンドでは、-nオプションで明示的な出力のみを出力するようにし、
(1)aタグのhref属性からリンクの出力
(2)HTMLのソースが圧縮されているため、aタグの閉じタグを用いて改行の追加
(3)全てのタグを削除
(4)出力
を行っています。
また、sedコマンドのスクリプトの区切り文字として、's/pattern/replacement/'の形式をよく見ますが、HTMLタグにある'/'と混同しやすいため、's!pattern!replacement!'のように別の区切り文字を使用しています。
1 2 3 4 5 |
sed -n -e 's!<a[^>]*href="\([^"]*\)"[^>]*>!\1 !g' -e 's!</a>!</a>\n!g' -e 's!<[^>]*>!!g' -e 'p' |
最後に出力に行番号をつけるためにnlコマンドを用いています。
ウェブサイトから欲しい情報を抽出するためにcurlコマンドとxmllintコマンドを利用することはとても有用です。しかし、JavaScriptでWebページを動的に作成していくようなサイトの情報を抽出するにはまた別のテクニックが必要になります。具体的にはSeleniumというブラウザの自動操作ツールを用いることで実現できます(Selenium - Web Browser Automation)。
参考
curlの公式サイト
マニュアルとしてはcurl - Manual等が参考に、curlに関わるRFC等の仕様はcurl - Specificationsが参考になります