率直に言えば、私たちの立場はこうです。私たちは Geonode であり、プロキシを販売しています。ユーザーエージェントに関する質問は、たいてい「なぜブロックされているのか」という問い合わせとセットで寄せられます。 率直に言えば、ユーザーエージェントは利用可能なシグナルの中でも信頼性の低いものの一つであり、TLSフィンガープリントがそれとは異なることを示しているリクエストにおけるブラウザ文字列は、偽装を全く行わない場合よりもさらに悪い — あなたは、実際のブラウザでは決して発生しないような不整合を生み出しているのです。 これについては後述します。人々の予想以上に頻繁に効果を発揮する戦略は、その正反対のものです。つまり、自分が誰であるかを明かし、連絡先URLを提供し、合理的な振る舞いをすることです。匿名の自動化は、身元が判明している自動化よりもはるかに容易にブロックされますし、身元を明かすことには何のコストもかかりません。
構文
curl -A "MyBot/1.0" https://example.com
curl マニュアル には、-A, --user-agent <name>
について次のように記載されています。「HTTP サーバーに送信する User-Agent 文字列を指定します。文字列内の空白をエンコードするには、文字列を単一引用符または二重引用符で囲んでください。」
デフォルト設定についても次のように記載されています。「デフォルトでは、curlはcurl/VERSION
を使用します。例:User-Agent: curl/8.22.0
。」
したがって、オプションを指定しない限り、送信するすべてのリクエストにおいて、curlであることとバージョンが通知されます。サーバーによってはこれに基づいて異なる応答を返す場合があるため、サイトが機能していないと結論付ける前に、この点を把握しておく価値があります。
汎用ヘッダーを使用した同等の記述は次の通りです:
curl -H "User-Agent: MyBot/1.0" https://example.com
どちらでも結果は同じです。マニュアルには、このヘッダーは「--header
または --proxy-header
オプションで設定することもできる」と記載されています。-A
の方が短いですが、-H
は他のすべてのヘッダーの設定方法と一貫しており、プログラムでコマンドを生成する場合に重要です。
複数回指定した場合、「最後に設定された値が使用されます」。
ヘッダーを完全に削除する
あまり知られていないケースですが、マニュアルではこの違いについて明確に説明されています:
--user-agentに空の引数(「」)を渡すと、リクエストからヘッダーが完全に削除されます。空白のヘッダーを希望する場合は、単一のスペース(「 」)を設定してください。
curl -A "" https://example.com # no User-Agent header at all
curl -A " " https://example.com # User-Agent: (empty value)
これらは本質的に異なるリクエストです。ヘッダーを送信しないことと、空のヘッダーを送信することは同じではなく、サーバーはこれらを区別できます。
どちらも珍しいケースですが、珍しいこと自体がシグナルとなります。ユーザーエージェントが全く含まれていないリクエストは、curlとして識別されるリクエストよりもさらに稀であるため、目立たないようにヘッダーを削除しても、一般的には逆効果になります。
-H でも同様の仕組みが機能し、マニュアルでは両方のケースの構文について次のように説明されています。「コロン(:)の右側にコンテンツのない置換値を指定することで、内部ヘッダーを削除します。例:-H "Host:"」。一方、値が空のヘッダーにはセミコロン(;)が必要です。-H "X-Custom-Header;" は X-Custom-Header: を送信します。
デフォルトが重要な理由 「
curl/8.22.0」はごく普通の文字列ですが、これには影響があります。
一部のサーバーはこれを完全にブロックします。 既知の自動化ツールに対する一律のルールは簡単に設定でき、非常に一般的であるため、実際に遭遇することになるでしょう。
コンテンツが異なるものもあります。 マークアップが簡略化されていたり、JavaScriptに依存するセクションがなかったり、時には全く別のページが表示されたりすることもあります。
ログに記録するだけで何もしないサーバーもあります。 これが圧倒的に最も一般的なケースです。
**一部のCDNは、これを単独の判断基準ではなく、**多くのシグナルの一つとして扱います。
実用上の結果として、「自分のブラウザでは動作するがcurlでは動作しない」という現象にはいくつかの原因が考えられますが、ユーザーエージェントはそのうちの1つに過ぎません。 変更を行う前に、その違いが実際にJavaScriptによるものかどうかを確認してください。curlはJavaScriptを実行しないため、クライアント側で組み立てられたページは、どのようなヘッダーを送信してもcurlからはほぼ空の状態に見えます。これはブロックされるような問題ではなく、どのユーザーエージェントでも修正されるものではありません。
ブラウザを偽装すると逆効果になりがちな理由
Chromeの文字列を貼り付ける前に一読すべきセクション。
ユーザーエージェントは「主張」に過ぎません。最新のボット対策システムは、その主張を証拠と照らし合わせて検証しますが、その証拠は数多く存在します:
TLSフィンガープリント。 クライアントがTLSをネゴシエートする方法――暗号スイートの順序、拡張機能、サポートされているグループ――によって、一般にJA3やJA4として要約される署生が生成されます。curlのものはChromeのものではなく、ヘッダーを変更してもそれは変わりません。 curlのTLSハンドシェイクを用いてChromeを装ったリクエストは、正直にcurlであると主張するリクエストよりもさらに特定されやすい。なぜなら、本物のChromeがそのような組み合わせを生成することは決してないからだ。
ヘッダーのセットと順序。 ブラウザは、Accept、Accept-Language、Accept-Encoding、Sec-Fetch-* など、特徴的な順序で特徴的なヘッダーのセットを送信します。curl は 3 つか 4 つしか送信しません。Chrome を装いながら curl のヘッダーセットを送信することは、明らかな矛盾です。
HTTPのバージョンと動作。 接続の再利用、多重化、ヘッダー圧縮の詳細。
動作。 実際のブラウザは、CSS、画像、スクリプトを取得します。HTMLドキュメントを1つ取得するだけで、それ以外は何も取得しないクライアントは、たとえ何と言おうとブラウザには見えません。
したがって、正直な階層関係はこうなります:正確なユーザーエージェントは一貫性があり、特に目立たないものです。一方、偽造されたものは一貫性がなく、目立つものです。もし本当にブラウザのようなリクエストが必要な場合は、ヘッダーではなく、ブラウザ(Playwrightや類似のツール)が必要です。これに関連するトレードオフについては、Playwrightを使ったスクリーンショットの取得で取り上げました。
ただし、正当な妥協点として、ごく限られたケースがあります。それは、自サイトのユーザーエージェント処理をテストする場合や、モバイルクライアントに対してサイトが異なる方法で配信するコンテンツを取得する場合です。これらは自システムに対する検証や、無害なコンテンツネゴシエーションであり、問題ありません。
代わりに送信すべき内容
自動アクセスを行うクライアントの場合、ブロックされる可能性が最も低い文字列は、自分が何者であるかを明記したものです。
curl -A "AcmePriceBot/1.2 (+https://acme.example.com/bot)" https://example.com
この形式は、名前、バージョン、そして自分が何者かや連絡方法を調べられるURLという3つの部分で構成されています。これが機能するのは、サイト運営者にブロック以外の選択肢を与えるからです。 説明のないリクエストのパターンは阻止すべき問題ですが、連絡先ページを持つ特定されたクローラーについては判断が必要となり、多くの場合、許可するという判断が下されます。
3つの実用的な利点、いずれも現実的なものです:
**robots.txt
を通じて、あなたに個別に連絡できます。** ルールはユーザーエージェントのトークンによって照合されるため、サイト運営者はあなたのクローラーに対して、他のすべての人には与えない特別な許可を与えることができます。匿名の場合、これは不可能です。
運営者はブロックする前にあなたに連絡を取ることができます。 これは人々が予想するよりも頻繁に起こり、3週間後にブロックされたことに気づくよりもはるかに良い結果です。
アクセス許可の申請をサポートします。 「私たちは AcmePriceBot として識別されたクローラーです。私たちの活動内容は以下の通りです」というやり取りは、有意義な進展につながる可能性があります。 「私たちは未識別スクリプトです」という主張では、そのような対話は生まれません。
その主張に見合った行動をとってください:robots.txt
を遵守し、Crawl-delay
および Retry-After
を尊重し、リクエストレートを控えめに保ち、変更のないリソースを二度取得しないようキャッシュを活用してください。
スクリプト内での一貫した設定
コマンドが1つ以上ある場合は、その文字列を1か所にまとめてください。
UA="AcmePriceBot/1.2 (+https://acme.example.com/bot)"
curl -sS --fail --location \
--user-agent "$UA" \
--connect-timeout 5 --max-time 30 \
"$URL"
あるいは、curlの設定ファイルで一度設定しておけば、フラグを繰り返し指定することなく、すべての呼び出しで一貫性を保つことができます:
# bot.conf
--user-agent "AcmePriceBot/1.2 (+https://acme.example.com/bot)"
--location
--show-error
curl -K bot.conf "$URL"
特に ~/.curlrc
に関する注意点:これは、そのユーザーによる すべての curl 実行に適用されます。あなたが記述していないコマンドも含まれます。そこにボットのユーザーエージェントを設定すると、あなたがこれまでに送信したすべてのアドホックなリクエストがあなたのクローラーとして識別され、数ヶ月後に混乱を招く結果となります。ワークロード固有の設定については、-K
を使用して名前付き設定ファイルを利用してください。
ワークロード全体で複数のユーザーエージェントが必要な場合は、それらを配列に入れておき、ランダムではなく意図的に選択してください。セッション内でランダムにローテーションさせると、訪問の途中でブラウザを変更しているように見えるクライアントが生成され、これは偽装というよりは、むしろ一貫性の欠如となります。
実際に送信した内容を確認する
ヘッダーに関する思い込みは驚くほど頻繁に間違っているため、この習慣を身につける価値があります。
curl -v -A "MyBot/1.0" https://example.com 2>&1 | grep -i '^> user-agent'
詳細出力はstderrに出力されるため、2>&1と指定しています。>の行は、curlが送信した内容です。
あるいは、サービスにその内容を返してもらうこともできます:
curl -sS -A "MyBot/1.0" https://httpbin.org/user-agent
これは、マニュアルに記載されている特定の挙動があるため、一見した以上に重要です。「curlが使用する内部ヘッダーと同じ名前のカスタムヘッダーを追加した場合、内部ヘッダーの代わりに外部で設定されたヘッダーが使用されます」。したがって、-Aと-H "User-Agent: ..."を組み合わせると、どちらかが黙って優先されてしまい、どちらが優先されているかを知るには確認する必要があります。 マニュアル自体のアドバイスを改めて強調しておきます。「自分が何をしているかを完全に理解していない限り、内部で設定されたヘッダーを上書きしてはなりません。」
この確認はプロキシ経由の場合にも当てはまります。--proxy-header を実行すると、ターゲットへのリクエストではなく、プロキシ接続に対してヘッダーが設定されます。この違いにより、設定したヘッダーが到達していないように見えてしまうという事態に陥ることがあります。
ユーザーエージェント文字列の読み方
ユーザーエージェント文字列を作成する必要が生じる可能性があること、またその形式を知ることでブラウザの文字列がなぜあんなに奇妙に見えるのかが理解できることから、知っておく価値があります。
最新のChromeの文字列は、おおむね次のような形をしています:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
この文字列の内容は、ほとんどが事実ではありません。MozillaでもSafariでもなく、AppleWebKit/537.36は数年前から変更されていません。 この文字列は、20年にわたるコンテンツネゴシエーションの「化石記録」です。各ブラウザは、競合他社のブラウザを検知したサーバーが適切なバージョンのページを配信できるよう、先行するブラウザのトークンを追加してきました。その結果、信頼できる情報がほとんど含まれていないにもかかわらず、多くのソフトウェアによって解析されている形式が生まれました。
その基盤となる構文は単純です。Product/Versionというトークンの列で、各トークンの後には任意で括弧で囲まれたコメントが続くというものです。これが仕様のすべてであり、AcmePriceBot/1.2 (+https://acme.example.com/bot)のような文字列が文法的に正しい理由でもあります――製品トークン、バージョン、そしてURLを含むコメントです。URLの先頭にある+というプレフィックスは必須要件というよりは慣例ですが、広く認知されています。
モバイルに関する問題。 多くのサイトはモバイルクライアントに対して異なるマークアップを提供しており、その区別となるトークンは通常、文字列内のどこかに含まれる「Mobile」です。 もし、モバイルユーザーに対してページがどのようにレンダリングされるかを正当に確認しているのであれば、モバイルユーザーエージェントを送信することは、なりすましではなく、ごく一般的なコンテンツネゴシエーションです。ただし、いつものように同じ注意点があります。デスクトップ型の接続環境やデスクトップビューポートでモバイル文字列を使用しても、それは主張の半分に過ぎず、実際のモバイルブラウザであれば他にもいくつかの点で異なるはずです。
そして、バージョンの固定化という傾向。 ブラウザは、このヘッダーで公開する詳細情報を徐々に削減し、代わりに機能情報を構造化されたクライアントヒントに移行させています。実用的な意味としては、ユーザーエージェントの解析は、リクエストへの対応を決定するサイトを含め、誰にとっても情報源としての価値が低下しつつあるということです。これが、ユーザーエージェントを戦略の基盤とするのが弱いシグナルであるもう一つの理由です。
ユーザーエージェントが問題ではない場合
ユーザーエージェントを変更しても解決しないケースを、多くの人がまず試す傾向にあるため、ここに挙げておきます。
コンテンツが JavaScript によってレンダリングされている場合。 curl はスクリプトを実行しません。レスポンスがほぼ空であるということは、ページがクライアント側で組み立てられていることを意味し、解決策はヘッダーではなく、ブラウザまたは基盤となる API にあります。
レート制限を受けている場合。 「429」はトラフィック量に関するものであり、身元に関するものではありません。 リクエスト速度を落とすことは有効ですが、新しいユーザーエージェントを指定しても効果はありません。
アドレス自体が問題である場合。 特定の範囲全体がブロックされている場合、ヘッダーの設定に関わらず、その範囲からのリクエストはすべて失敗します。
認証が必要な場合。 401 には認証情報が必要です。
TLSフィンガープリントによって正体がバレてしまう場合。 これは前述の通りであり、ヘッダーのみによるブラウザのなりすましが期待外れになりがちな理由でもあります。
サイトが単に自動トラフィックを望んでいない場合。 一部のサイトは利用規約でこれを明記し、厳格に適用しています。ヘッダーを変更しても利用規約は変わりません。クロールを禁止しているサイトは、謎かけではなく明確な情報を提供しているのです。
これらを素早く見分ける方法:同じ接続環境下で、通常のブラウザから同じURLにリクエストを送ってみてください。ブラウザでは動作するがcurlでは動作しない場合、2つのリクエストのヘッダーを1つずつ比較してみてください。もし、重要な違いがコマンドラインから変更できないものだけであるなら、それが答えです。
よくある質問
curl で User-Agent を設定するにはどうすればよいですか?
curl -A "MyBot/1.0" URL、または同等の curl -H "User-Agent: MyBot/1.0" URL。文字列にスペースが含まれる場合は、引用符で囲んでください。複数回指定された場合、最後の値が優先されます。
curlのデフォルトのUser-Agentは何ですか?
curl/VERSION — 例:curl/8.22.0。上書きまたは削除しない限り、すべてのリクエストで送信され、一部のサーバーはこれに基づいて異なる応答を返します。
curl で User-Agent ヘッダーを削除するにはどうすればよいですか?
curl -A "" URL を実行すると、ヘッダーが完全に削除されます。curl -A " " URL を実行すると、空の値で送信されますが、これは別のリクエストとなります。User-Agent を送信しないことは、curl のデフォルト値を送信することよりも珍しい行為であるため、かえって注目を集めることになる点に注意してください。
curl でブラウザの User-Agent を偽装すべきですか?
通常は避けるべきです。TLS フィンガープリント、ヘッダーセット、および順序がすべて curl のものであるリクエストにブラウザの文字列を含めることは、実際のブラウザでは決して発生しない不整合であり、かえって特定されやすくなります。ブラウザのようなリクエストが必要な場合は、ブラウザを使用してください。
User-Agentを変更すれば、ブロックされなくなるか?
それだけでは、ほとんど効果はありません。既知のツールを拒否する包括的なルールに対しては有効ですが、レート制限、IPアドレスに基づくブロック、TLSフィンガープリント、行動分析に対しては全く効果がありません。連絡先URLを明記して正直に身元を明かす方が、偽装するよりも効果的な場合が多いです。
ボットの User-Agent はどのようにすべきですか?
名前、バージョン、および連絡先 URL:AcmePriceBot/1.2 (+https://acme.example.com/bot)。これにより、robots.txt があなたに個別に連絡できるようになり、運営者があなたをブロックする代わりに連絡を取れるようになり、アクセスを要請する正当な根拠が得られます。
リクエストごとに異なる User-Agent を設定できますか?
はい。-A は呼び出しに適用されるため、毎回異なる値を指定するか、--next を使用して、1つのコマンドで異なるオプションを指定して複数の操作を実行してください。セッション内でランダムにローテーションすることは避けてください。そうすると、訪問中にブラウザを切り替えているように見えるクライアントになってしまいます。
カスタム User-Agent が表示されないのはなぜですか?
おそらく、異なる方法で 2 回設定してしまったためです。curl は内部のヘッダーではなく外部から設定されたヘッダーを使用し、最後に定義されたものが優先されるためです。curl -v ... 2>&1 | grep -i '^> user-agent' で、実際に送信された内容を確認してください。
まとめ
curl でユーザーエージェントを設定するのは 1 つのオプションに過ぎず、構文よりも値の選択の方が重要です。
デフォルトでは curl であることが正直に通知されますが、この正直さは正当な立場です。一貫性があり、目立たず、時にはサイトから合理的な扱いを受ける理由にもなります。ヘッダーを削除することも可能です(引数を空にすると完全に削除され、スペースを1つ指定すると空白になります)が、どちらもデフォルトよりも珍しい設定であり、これは通常、ユーザーが意図することとは正反対です。
ブラウザの文字列をコピーするのは一般的な直感ですが、最も弱い選択肢です。なぜなら、その主張は検証可能だからです。TLSフィンガープリント、ヘッダーの構成や順序、ページのサブリソースを取得しているかどうかといった要素がすべてそれと矛盾しており、矛盾は単純な自白よりも特定されやすいものです。もしブラウザ形式のリクエストが真に必要であるならば、ブラウザこそがそのツールです。
多くの人が予想するよりも効果的なアプローチには、何のコストもかかりません。それは、名前、バージョン、そしてあなたが何者であるかを誰かが確認できるURLです。 これにより、robots.txtがあなたに連絡を取ることができ、オペレーターはあなたをブロックする代わりにメールを送ることができ、「原因不明のトラフィック」が「所有者がいるクローラー」へと変わります。これは、誰かがあなたに対してどう対処すべきかを判断する際、はるかに有利な立場となるのです。
