Linuxのcurlとwget|コマンドでWeb APIを叩く・ファイルを落とす【初心者向け】

前の記事では、cron(=決まった日時に、コマンドをくり返し自動で実行してくれるしくみ)を使って、Python のスクリプトを毎朝決まった時刻に動かす方法を見ました。自動化でよくあるのが、「毎朝、Web からデータを取ってくる」という作業です。

私が最初に Web API(=プログラム同士がデータをやりとりするための窓口)を使ったとき、いきなり Python でプログラムを書いて、うまく動かずに悩みました。原因が「API の使い方がまちがっている」のか「プログラムがまちがっている」のか、切り分けられなかったんです。そこで先輩に教わったのが、まずはコマンド1行で API を叩いて、返ってくるものを見るというやり方でした。

この記事では、そのための道具 curl と wget を使って、Web ページや API の中身を見たり、ファイルをダウンロードしたりする方法を、練習用の安全なサイトで確かめていきます。

💡
この記事でわかること
  • curl と wget の違いと使い分け
  • Web ページの中身と、ヘッダー(-I)の見方
  • ファイルとして保存する方法(curl -o・wget)
  • Web API から JSON を受け取って、見やすく表示する方法
  • データを送る(POST)方法と、ステータスコードの確かめ方
  • API キーを安全に扱うための注意点
🔰
この記事の前提
  • パイプ(|)と head・grep を使える
  • 環境変数(=シェルが覚えていて、そこから動かすプログラムにも渡される「名前つきの設定値」)と export のしくみがわかる
  • sudo apt install でソフトを入れられる

まだの方は、前の記事「毎朝の作業をPCを触らず動かしたい|cron入門(Pythonを定時実行)」から読むのがおすすめです。

💡
用語メモ
  • URL:Web ページなどの場所を表すアドレス(https:// で始まる文字列)
  • SSH:ネットワークの向こうのコンピューターに、暗号化して安全にログインし、操作するしくみ
  • .env ファイル:APIキーなどの秘密の設定を、名前=値 の形で書いておくファイル。人に渡したり、Git に入れたりしません
  • トークン:ログインした状態や、使ってよい範囲を表す文字列。APIキーと同じく、パスワードと同じように人に見せてはいけません

用語で迷ったら、連載のまとめ記事の「よく出てくる用語ミニ辞典」でも確かめられます。


結論:見る・試すなら curl、落とすだけなら wget

やりたいこと コマンド
ページの中身を表示する curl https://example.com
ヘッダーだけ見る curl -I https://example.com
名前をつけて保存する curl -o ファイル名 URL
とにかくファイルを落とす wget URL
リダイレクト先までついていく curl -L URL
JSON を送る(POST) curl -X POST -H "Content-Type: application/json" -d '…' URL
ステータスコードだけ見る curl -s -o /dev/null -w "%{http_code}\n" URL

curl と wget のしくみ

準備:curl と wget を確かめる

練習は ~/linux-practice/web-test の中で行います。

  • 1
  • 2
  • 3
mkdir -p ~/linux-practice/web-test
cd ~/linux-practice/web-test
which curl wget

🖥️
この記事の出力例について

ここから先の出力例は、Debian 13での実行例です(ユーザー名 kaito・パソコン名 demo のダミー環境)。UbuntuやWSL(=Windows の中で Linux を動かせる、Microsoft のしくみ)では、表示が少し違うことがあります。

(例)

  • 1
  • 2
  • 3
kaito@demo:~/linux-practice/python-test$ mkdir -p ~/linux-practice/web-test
kaito@demo:~/linux-practice/python-test$ cd ~/linux-practice/web-test
kaito@demo:~/linux-practice/web-test$ which curl wget

Debian 13 の検証環境では、curl も wget も入っておらず、which は何も表示しませんでした。

どちらかが表示されなかったら、apt で入れます。

  • 1
  • 2
  • 3
  • 4
sudo apt update
sudo apt install curl wget
curl --version | head -1
wget --version | head -1

(例)

  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
kaito@demo:~/linux-practice/web-test$ sudo apt update
Get:1 http://deb.debian.org/debian trixie InRelease [140 kB]
Get:2 http://deb.debian.org/debian trixie-updates InRelease [47.3 kB]
Get:3 http://deb.debian.org/debian-security trixie-security InRelease [43.4 kB]
Fetched 231 kB in 0s (4157 kB/s)
All packages are up to date.
kaito@demo:~/linux-practice/web-test$ sudo apt install curl wget
Installing:
  curl  wget

Installing dependencies:
  libbrotli1      libldap2       libpsl5t64  libsasl2-modules     publicsuffix
  libcurl4t64     libnghttp2-14  librtmp1    libsasl2-modules-db
  libldap-common  libnghttp3-9   libsasl2-2  libssh2-1t64

Suggested packages:
  libsasl2-modules-gssapi-mit        libsasl2-modules-ldap  libsasl2-modules-sql
  | libsasl2-modules-gssapi-heimdal  libsasl2-modules-otp

Summary:
  Upgrading: 0, Installing: 15, Removing: 0, Not Upgrading: 0
  Download size: 3124 kB
  Space needed: 9242 kB / 108 GB available

Continue? [Y/n] Y
…(途中省略)…
Processing triggers for man-db (2.13.1-1) ...
Processing triggers for libc-bin (2.41-12+deb13u4) ...
kaito@demo:~/linux-practice/web-test$ curl --version | head -1
curl 8.14.1 (x86_64-pc-linux-gnu) libcurl/8.14.1 OpenSSL/3.5.7 zlib/1.3.1 brotli/1.1.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.2 libssh2/1.11.1 nghttp2/1.64.0 nghttp3/1.8.0 librtmp/2.3 OpenLDAP/2.6.10
kaito@demo:~/linux-practice/web-test$ wget --version | head -1
GNU Wget 1.25.0 built on linux-gnu.

apt update の出力は、最後の2行だけをのせています。検証では、少し前に sudo を使っていたため、パスワードは聞かれませんでした(sudo は、一度パスワードを打つとしばらくのあいだ聞かずに動きます)。久しぶりに使うときは [sudo] password for kaito: と聞かれるので、自分のパスワードを打ってください。

💡
この記事で使う練習用のサイト

この記事では、練習用に用意されている example.com(説明用のサンプルページ)と、httpbin.org(送ったものをそのまま返してくれる、API の練習用サービス)を使います。どちらも、だれでも使ってよい公開のサイトです。たくさん連続で叩くと迷惑になるので、1回ずつ、ゆっくりためしましょう。


curl でページの中身を見る

curl に URL を渡すと、返ってきた中身をそのまま画面に表示します。

  • 1
curl https://example.com

(例)

  • 1
  • 2
kaito@demo:~/linux-practice/web-test$ curl https://example.com
<!doctype html><html lang=en><head><meta charset=utf-8><link rel=icon href=data:,><meta name=viewport content="width=device-width,initial-scale=1"><title>Example Domain</title><style>html{color-scheme:light dark;background:light-dark(#eee,#222)}body{font:16px/1.6 system-ui,sans-serif;max-width:26em;margin:auto;padding:25vh 2em 2em;text-align:center}</style></head><body><p>This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.</p><script src=/s.js></script></body></html>

ブラウザで見ると文字と色がついたページですが、中身はこのような HTML というテキストです。何行もある長いページでは、head で先頭だけ見るのが便利です。-s は、進み具合の表示を消すオプションです。

  • 1
  • 2
curl -s https://example.com | head -5
curl -s https://example.com | grep "<title>"

(例)

  • 1
  • 2
  • 3
  • 4
kaito@demo:~/linux-practice/web-test$ curl -s https://example.com | head -5
<!doctype html><html lang=en><head><meta charset=utf-8><link rel=icon href=data:,><meta name=viewport content="width=device-width,initial-scale=1"><title>Example Domain</title><style>html{color-scheme:light dark;background:light-dark(#eee,#222)}body{font:16px/1.6 system-ui,sans-serif;max-width:26em;margin:auto;padding:25vh 2em 2em;text-align:center}</style></head><body><p>This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.</p><script src=/s.js></script></body></html>
kaito@demo:~/linux-practice/web-test$ curl -s https://example.com | grep "<title>"
<!doctype html><html lang=en><head><meta charset=utf-8><link rel=icon href=data:,><meta name=viewport content="width=device-width,initial-scale=1"><title>Example Domain</title><style>html{color-scheme:light dark;background:light-dark(#eee,#222)}body{font:16px/1.6 system-ui,sans-serif;max-width:26em;margin:auto;padding:25vh 2em 2em;text-align:center}</style></head><body><p>This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.</p><script src=/s.js></script></body></html>

今の example.com のページは、HTML 全体が1行に書かれた小さなページ(577 バイト)です。そのため、head -5 でも grep でも、同じ1行がまるごと表示されました。何行もあるふつうのページなら、head -5 では最初の5行だけ、grep では <title> を含む行だけが表示されます。


curl -I でヘッダーを見る

-I をつけると、中身ではなくヘッダー(返事についてくる情報)だけを表示します。

  • 1
curl -I https://example.com

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
kaito@demo:~/linux-practice/web-test$ curl -I https://example.com
HTTP/2 200
date: Sat, 31 Oct 2026 18:03:13 GMT
content-type: text/html; charset=utf-8
server: cloudflare
last-modified: Thu, 29 Oct 2026 13:15:03 GMT
allow: GET, HEAD
accept-ranges: bytes
age: 9
cf-cache-status: HIT
cf-ray: a53493404ad83bd1-IAD
alt-svc: h3=":443"; ma=86400

1行目の HTTP/2 200 が「うまくいった」という返事です。HTTP/2 では、ヘッダーの名前が content-type のように小文字で表示されます。date の時刻は世界標準時(GMT)なので、日本の時刻より9時間前になっています。

Debian 13での実行画面

1行目の数字がステータスコードです。よく見るものだけ覚えておけば大丈夫です。

ステータスコード 意味
200 うまくいった
301・302 別の場所に移動した(リダイレクト)
401・403 見る権限がない(キーがない・まちがっている)
404 見つからない(URL がまちがっている)
429 叩きすぎ(少し待ってから)
500 番台 相手のサーバーの問題

ファイルとして保存する

curl -o で名前をつけて保存
  • 1
  • 2
  • 3
curl -s -o example.html https://example.com
ls -l example.html
head -3 example.html

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
kaito@demo:~/linux-practice/web-test$ curl -s -o example.html https://example.com
kaito@demo:~/linux-practice/web-test$ ls -l example.html
-rw-rw-r-- 1 kaito kaito 577 Nov  1 03:03 example.html
kaito@demo:~/linux-practice/web-test$ head -3 example.html
<!doctype html><html lang=en><head><meta charset=utf-8><link rel=icon href=data:,><meta name=viewport content="width=device-width,initial-scale=1"><title>Example Domain</title><style>html{color-scheme:light dark;background:light-dark(#eee,#222)}body{font:16px/1.6 system-ui,sans-serif;max-width:26em;margin:auto;padding:25vh 2em 2em;text-align:center}</style></head><body><p>This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.</p><script src=/s.js></script></body></html>

このページは1行だけなので、head -3 でも1行が表示されました。

wget でダウンロード

wget は、URL を渡すだけでファイルとして保存してくれます。名前を決めたいときは -O(大文字のオー)を使います。

  • 1
  • 2
  • 3
wget https://example.com
wget -O example-wget.html https://example.com
ls -l

(例)

  • 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
  • 26
  • 27
kaito@demo:~/linux-practice/web-test$ wget https://example.com
--2026-11-01 03:03:24--  https://example.com/
Resolving example.com (example.com)... xxx.xxx.xxx.xxx
Connecting to example.com (example.com)|xxx.xxx.xxx.xxx|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘index.html’

index.html                   [ <=>                               ]     577  --.-KB/s    in 0s

2026-11-01 03:03:24 (20.1 MB/s) - ‘index.html’ saved [577]
kaito@demo:~/linux-practice/web-test$ wget -O example-wget.html https://example.com
--2026-11-01 03:03:27--  https://example.com/
Resolving example.com (example.com)... xxx.xxx.xxx.xxx
Connecting to example.com (example.com)|xxx.xxx.xxx.xxx|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘example-wget.html’

example-wget.html            [ <=>                               ]     577  --.-KB/s    in 0s

2026-11-01 03:03:28 (10.6 MB/s) - ‘example-wget.html’ saved [577]
kaito@demo:~/linux-practice/web-test$ ls -l
total 12
-rw-rw-r-- 1 kaito kaito 577 Oct 29 22:15 example-wget.html
-rw-rw-r-- 1 kaito kaito 577 Nov  1 03:03 example.html
-rw-rw-r-- 1 kaito kaito 577 Oct 29 22:15 index.html

Resolving・Connecting の行に出る接続先の IP アドレスは、ここでは xxx.xxx.xxx.xxx に伏せています。このページは大きさ(Length)を前もって知らせてこないため、進み具合は 100% ではなく [ <=> ] と表示されました。最後の saved [577] の行が出れば、保存できています。ls -l を見ると、wget で保存した2つのファイルの日時は、サーバーが知らせてきたページの更新日時(last-modified)になっていて、curl -o で保存した example.html は保存した時刻になっています。

wget で URL の最後にファイル名がないときは、index.html という名前で保存されます。同じ名前のファイルがあると、index.html.1 のように番号がついたファイルが作られます。


Web API から JSON を受け取る

Web API の多くは、JSON という形でデータを返します。httpbin.org/json は、練習用の JSON を返してくれる場所です。

  • 1
curl -s https://httpbin.org/json

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
kaito@demo:~/linux-practice/web-test$ curl -s https://httpbin.org/json
{
  "slideshow": {
    "author": "Yours Truly",
    "date": "date of publication",
    "slides": [
      {
        "title": "Wake up to WonderWidgets!",
        "type": "all"
      },
      {
        "items": [
          "Why <em>WonderWidgets</em> are great",
          "Who <em>buys</em> WonderWidgets"
        ],
        "title": "Overview",
        "type": "all"
      }
    ],
    "title": "Sample Slide Show"
  }
}

そのままだと読みにくいので、Python に入っている道具で見やすく整えます。

  • 1
curl -s https://httpbin.org/json | python3 -m json.tool

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
kaito@demo:~/linux-practice/web-test$ curl -s https://httpbin.org/json | python3 -m json.tool
{
    "slideshow": {
        "author": "Yours Truly",
        "date": "date of publication",
        "slides": [
            {
                "title": "Wake up to WonderWidgets!",
                "type": "all"
            },
            {
                "items": [
                    "Why <em>WonderWidgets</em> are great",
                    "Who <em>buys</em> WonderWidgets"
                ],
                "title": "Overview",
                "type": "all"
            }
        ],
        "title": "Sample Slide Show"
    }
}

Debian 13での実行画面

jq という JSON 専用の道具を apt で入れると、必要な項目だけを取り出すこともできます。慣れてきたら使ってみてください。


データを送る(POST)

API にデータを送るときは、-X POST と -d を使います。httpbin.org/post は、送ったものをそのまま返してくれるので、「ちゃんと送れているか」を確かめる練習に使えます。

  • 1
  • 2
  • 3
curl -s -X POST https://httpbin.org/post \
  -H "Content-Type: application/json" \
  -d '{"name": "kaito", "message": "hello"}'

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
kaito@demo:~/linux-practice/web-test$ curl -s -X POST https://httpbin.org/post -H "Content-Type: application/json" -d '{"name": "kaito", "message": "hello"}'
{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Content-Length": "37",
    "Content-Type": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "curl/8.14.1"
  },
  "method": "POST",
  "origin": "xxx.xxx.xxx.xxx",
  "url": "https://httpbin.org/post",
  "data": "{\"name\": \"kaito\", \"message\": \"hello\"}",
  "files": {},
  "form": {},
  "json": {
    "message": "hello",
    "name": "kaito"
  }
}

"origin" の IP アドレスは、xxx.xxx.xxx.xxx に伏せています。検証では、コマンドを1行にまとめて入力しました(\ で行を分けて打っても、同じ結果になります)。

  • -H:ヘッダーをつける(ここでは「JSON を送ります」という印)
  • -d:送る中身
  • 行の最後の \:長いコマンドを、次の行に続けて書くための印

返事の "origin" には、みなさんのインターネット側の IP アドレスが表示されます。スクリーンショットや質問に貼るときは、伏せておきましょう。


ステータスコードだけを確かめる

スクリプトで「うまくいったか」を確かめたいときは、ステータスコードだけを取り出すと便利です。

  • 1
  • 2
curl -s -o /dev/null -w "%{http_code}\n" https://example.com
curl -s -o /dev/null -w "%{http_code}\n" https://httpbin.org/status/404

(例)

  • 1
  • 2
  • 3
  • 4
kaito@demo:~/linux-practice/web-test$ curl -s -o /dev/null -w "%{http_code}\n" https://example.com
200
kaito@demo:~/linux-practice/web-test$ curl -s -o /dev/null -w "%{http_code}\n" https://httpbin.org/status/404
404

-o /dev/null は「中身は捨てる」、-w は「最後にこの形で表示して」という意味です。

ここで気をつけたいのは、404 のように失敗の番号が返ってきても、curl 自身は「成功」として終わることです。curl から見ると「サーバーと話して、答えを受け取れた」ので、終了コード(echo $? で見られる番号)は 0 になります。

シェルスクリプトや cron で curl を使うときは、-f をつけておくと安心です。-f をつけると、404 や 500 のような失敗の答えのときに curl も失敗(終了コード 22)として終わるので、シェルスクリプトの記事の set -e で止められます。-s と一緒に使うなら、-fsS と書くのがよくある形です(-S は、-s で静かにしていても、エラーのときだけは知らせてくれる、という意味です)。

手元で、わざと 404 を返すページを作って確かめてみます。ターミナルを2つ使います。まず、ターミナル2で、自分のパソコンの中だけ(127.0.0.1)で見られる、小さな Web サーバーを動かします(python3 -m http.server は、Python に入っているお試し用のサーバーです)。

  • 1
  • 2
cd ~/linux-practice/web-test
python3 -m http.server 8000 --bind 127.0.0.1

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
kaito@demo:~$ cd ~/linux-practice/web-test
kaito@demo:~/linux-practice/web-test$ python3 -m http.server 8000 --bind 127.0.0.1
Serving HTTP on 127.0.0.1 port 8000 (http://127.0.0.1:8000/) ...
127.0.0.1 - - [01/Nov/2026 05:55:47] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [01/Nov/2026 05:55:50] code 404, message File not found
127.0.0.1 - - [01/Nov/2026 05:55:50] "GET /no-such-page HTTP/1.1" 404 -
127.0.0.1 - - [01/Nov/2026 05:55:53] code 404, message File not found
127.0.0.1 - - [01/Nov/2026 05:55:53] "GET /no-such-page HTTP/1.1" 404 -
127.0.0.1 - - [01/Nov/2026 05:55:55] code 404, message File not found
127.0.0.1 - - [01/Nov/2026 05:55:55] "GET /no-such-page HTTP/1.1" 404 -
127.0.0.1 - - [01/Nov/2026 05:55:58] code 404, message File not found
127.0.0.1 - - [01/Nov/2026 05:55:58] "GET /no-such-page HTTP/1.1" 404 -
127.0.0.1 - - [01/Nov/2026 05:56:01] "GET / HTTP/1.1" 200 -

Serving HTTP on 127.0.0.1 port 8000 と出たら、待ち受け(=プログラムが、決まったポートで通信が来るのを待っている状態)の状態です。その下の行は、ターミナル1から curl で読みに来るたびに1行ずつ増えていきます(上の(例)は、ターミナル1の操作がすべて終わったときの画面です)。

次に、ターミナル1で、ふつうの curl・-fs・-fsS の3つで、ないページ(/no-such-page)を読みに行き、そのたびに echo $? で終了コードを見ます。最初の echo の行は、確かめ用の index.html を作り直しています(上で wget で保存した index.html は、この1行で書きかわります)。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
echo "hello from kaito" > index.html
curl http://127.0.0.1:8000/
echo $?
curl http://127.0.0.1:8000/no-such-page
echo $?
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/no-such-page
echo $?
curl -fs http://127.0.0.1:8000/no-such-page
echo $?
curl -fsS http://127.0.0.1:8000/no-such-page
echo $?
curl -fsS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/
echo $?

(例)

  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
kaito@demo:~/linux-practice/web-test$ echo "hello from kaito" > index.html
kaito@demo:~/linux-practice/web-test$ curl http://127.0.0.1:8000/
hello from kaito
kaito@demo:~/linux-practice/web-test$ echo $?
0
kaito@demo:~/linux-practice/web-test$ curl http://127.0.0.1:8000/no-such-page
<!DOCTYPE HTML>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Error response</title>
    </head>
    <body>
        <h1>Error response</h1>
        <p>Error code: 404</p>
        <p>Message: File not found.</p>
        <p>Error code explanation: 404 - Nothing matches the given URI.</p>
    </body>
</html>
kaito@demo:~/linux-practice/web-test$ echo $?
0
kaito@demo:~/linux-practice/web-test$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/no-such-page
404
kaito@demo:~/linux-practice/web-test$ echo $?
0
kaito@demo:~/linux-practice/web-test$ curl -fs http://127.0.0.1:8000/no-such-page
kaito@demo:~/linux-practice/web-test$ echo $?
22
kaito@demo:~/linux-practice/web-test$ curl -fsS http://127.0.0.1:8000/no-such-page
curl: (22) The requested URL returned error: 404
kaito@demo:~/linux-practice/web-test$ echo $?
22
kaito@demo:~/linux-practice/web-test$ curl -fsS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/
200
kaito@demo:~/linux-practice/web-test$ echo $?
0

Debian 13での実行画面

ふつうの curl は、404 のエラーのページ(Error code: 404)をそのまま表示して、終了コードは 0(成功)でした。-w "%{http_code}\n" で 404 と出たときも 0 です。-fs にすると、画面には何も出ずに、終了コードが 22 になりました。-fsS なら、終了コードは同じ 22 で、curl: (22) The requested URL returned error: 404 とエラーの文だけが表示されます。ページがある / なら、-fsS でも 200 で 0 です。

試し終わったら、ターミナル2で Ctrl+C を押して、サーバーを止めておきましょう(Keyboard interrupt received, exiting. と出て止まります)。

リダイレクトについていく(-L)

URL が別の場所に移動しているときは、-L をつけないと移動先の中身を取ってきません。

  • 1
  • 2
curl -s -o /dev/null -w "%{http_code}\n" https://httpbin.org/redirect/1
curl -s -L -o /dev/null -w "%{http_code}\n" https://httpbin.org/redirect/1

(例)

  • 1
  • 2
  • 3
  • 4
kaito@demo:~/linux-practice/web-test$ curl -s -o /dev/null -w "%{http_code}\n" https://httpbin.org/redirect/1
302
kaito@demo:~/linux-practice/web-test$ curl -s -L -o /dev/null -w "%{http_code}\n" https://httpbin.org/redirect/1
200

「ダウンロードしたファイルが小さすぎる」「中身がちがう」ときは、-L のつけ忘れを疑ってみてください。


API キーを使うときの注意

本物の Web API では、多くの場合、API キー(使うための合い言葉)が必要です。よくある書き方は、ヘッダーにキーをつける形です。ただし、コマンドにキーを直接書くと、history にそのまま残ってしまいます。

💡
キーは直接書かず、環境変数から読む

curl -H "Authorization: Bearer <キーの文字列>" … のようにキーを直接書くのではなく、curl -H "Authorization: Bearer $API_TOKEN" … のように環境変数から読む形にしておくと、コマンドの履歴やスクリプトにキーが残りにくくなります。環境変数を入れるファイル(.env など)は、Git(=ファイルの変更の履歴を記録しておく道具)に入れないようにしましょう。この記事では、本物のキーは使いません。

ただし、環境変数から読んでも、curl に渡すときには本物のキーに置きかわっています。そのため、curl が動いているあいだは、プロセス(=今動いているプログラムのこと)の記事の ps aux で、同じパソコンのほかのユーザーからコマンドの中身が見えることがあります。共用のサーバーで使うときは、ヘッダーをファイルに書いておき、-H @ファイル名 で読ませる方法もあります。

🚫
ダウンロードしたものを、そのまま実行しない(文章だけで説明します)

ソフトの入れ方の説明で、curl -fsSL https://… | sudo bash のように、ダウンロードしたスクリプトをそのまま管理者の権限で動かす書き方を見かけることがあります。中身を確かめずに動かすと、何をされるかわかりません。また、wget -r(サイトをまるごとダウンロードする書き方)や、短い間隔でくり返し API を叩くスクリプトは、相手のサーバーに大きな負担をかけ、利用を止められることもあります。この記事では試しません。どうしても使うときは、まず curl -o でファイルに保存し、less で中身を読んでから判断しましょう。

🤖 詰まったらAIに聞こう:API がうまく叩けない

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
Linuxの初心者です。curl で Web API を使おうとしていますが、うまくいきません。
原因として考えられることと、確かめる順番を教えてください。
ステータスコードの意味と、curl のオプションの意味もやさしく説明してください。

【使っている環境】WSLのUbuntu など
【使いたい API とやりたいこと】例:天気の API から今日の天気を JSON で取りたい
【打ったコマンド(APIキーは***に置き換え済み)】ここに貼る
【curl -I またはステータスコードの結果】ここに貼る
【返ってきた中身やエラー(個人情報は置き換え済み)】ここに貼る

🔒
AIに貼る前に:秘密情報やパスワードは貼らない

curl のコマンドには、APIキー、トークン、パスワード、Cookie がそのまま入っていることがよくあります。-H "Authorization: …" や -u ユーザー名:パスワード の部分は、必ず「***」に置き換えてから貼ってください。返事に含まれる IP アドレスやメールアドレスも、伏せておきましょう。


後片づけ

  • 1
  • 2
  • 3
  • 4
cd ~/linux-practice
ls web-test
rm -r web-test
ls

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
kaito@demo:~/linux-practice/web-test$ cd ~/linux-practice
kaito@demo:~/linux-practice$ ls web-test
example-wget.html  example.html  index.html
kaito@demo:~/linux-practice$ rm -r web-test
kaito@demo:~/linux-practice$ ls
archive-test  chmod-test  find-test  link-test   perm-test     python-test    script-test
bin           edit-test   grep-test  nohup-test  process-test  redirect-test

ls に並ぶフォルダは、これまでの記事で作った練習用のものです。人によってちがいます。


よくある質問

Q. -X POST は、書かないといけませんか?

-d をつけると、curl は自動で POST で送ります。そのため、-X POST を書かなくても同じように送れます。この記事では、何をしているかがひと目でわかるように、あえて -X POST も書いています。

Q. -o と -O は、何が違いますか?

小文字の -o は、-o 名前 のように自分で名前を決めて保存します。大文字の -O は、URL の最後の部分(/ より後ろ)を、そのままファイル名にして保存します。同じ名前のファイルがあると、どちらも上書きするので気をつけましょう。

Q. JSON が1行でつながっていて、読みにくいです。

jq という道具を入れると、curl -s URL | jq のようにつなぐだけで、字下げされて読みやすくなります。Debian や Ubuntu では sudo apt install jq で入れられます。この記事で使った python3 -m json.tool でも、同じように整えられます。

Q. curl: (6) Could not resolve host と出ました。

URL の名前(ドメイン)から、サーバーの場所を調べられなかった、という意味です。まず URL のつづりを確かめ、次にインターネットにつながっているかを、ほかのサイト(curl -I https://example.com など)で確かめましょう。会社のネットワークなどでは、プロキシの設定が必要なこともあります。


まとめ

  • curl は中身を画面に表示する「見る・試す」道具、wget はファイルとして保存する「落とす」道具
  • curl -I でヘッダーとステータスコードを見る。200 なら成功、404 は URL ちがい
  • curl -o や wget -O で、名前をつけて保存する
  • JSON は python3 -m json.tool で見やすく整えられる
  • POST は -X POST -H "Content-Type: application/json" -d '…'
  • 移動したページは -L でついていく
  • API キーはコマンドに直接書かず、環境変数から読む。| sudo bash はしない

次の記事では、Lv3 の最後として、Linux で Git を使い、SSH 鍵を作って GitHub につなぐところまで進めます。


次に読む記事


関連する記事