現時点の理解
- Pythonの標準ライブラリ
urllib.request.urlopen
を使ってHTTP通信すると、403 Forbiddenが返るurllib.error.HTTPError: HTTP Error 403: Forbidden
- 同じURLにrequestsを使うと、403は発生しない(正常に扱える)
- CloudflareがurllibのデフォルトのUser-Agentヘッダのリクエストを弾いているらしい
- urllibでヘッダのUser-Agentを指定すると、403は発生せず正常に扱えた
目次
- 現時点の理解
- 目次
- Webhookを使ってDiscordにPythonで投稿する裏話
- urllibのデフォルトのUser-Agentヘッダ
- Discord以外でも再現。共通点はCloudflare
- 終わりに
Webhookを使ってDiscordにPythonで投稿する裏話
Pythonの標準ライブラリurllibを使って、DiscordのWebhookにリクエストを送り、Discordに投稿する実装が手元にあります1。
からあげさんがまとめてくださっています2。
この実装では、HTTPリクエストヘッダを以下のように書いています。
headers = { "Content-Type": "application/json", "User-Agent": "DiscordBot (private use) Python-urllib/3.10", }
User-Agentがポイントです3。
これを指定しないと、403 Forbiddenが返ります。
ただしurllibを使わなければ403は送出されません。
urllibのデフォルトのUser-Agentヘッダ
HOW TOドキュメント「urllib パッケージを使ってインターネット上のリソースを取得するには」より。
https://docs.python.org/ja/3/howto/urllib2.html#headers
デフォルトでは urllib は自身の情報を Python-urllib/x.y として扱います( x と y は Python のリリースバージョンのメジャーバージョンとマイナーバージョンです、例えば Python-urllib/2.5 など)。
このドキュメントには、Internet Explorerとして扱う例が記載されています(例がだいぶ古いですね)。
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64)'}
Discord以外でも再現。共通点はCloudflare
urllibで403 Forbiddenという事象は、DiscordのWebhookだけではなく、arXiv vanityへのリクエストでも経験しました。
urllibの代わりにrequestsを使ったときは再現しません。
検索したところ、原因はCloudflareのようです4。
The specific site in question, wallpapercrafter.com, is protected by cloudflare which will block web scrapers.
たしかにScrape Shieldの中にUser-Agentヘッダでブロックする機能があります。
おそらくこれでurllibのデフォルトのUser-Agentヘッダが弾かれているのだと理解しました。
終わりに
過去に経験した、urllibでだけHTTPリクエストが403になる事象を共有しました。
私の経験ではCloudflareが共通していて、urllibのデフォルトのUser-Agentヘッダがブロックされています。
User-Agentを指定する、もしくはurllib以外を使うことで解決しました。
- https://github.com/ftnext/meetup-host-ops/blob/ffc50f8f8a97868d8beddf0c9a6cda556cdcef33/discord.py↩
-
手元の実装を参考例としてお伝えしました。
↩おお、素晴らしいですね!そして「Webhookを使うもっと簡単な方法」、私も経験あったのでわかる〜となってました。
— nikkie / にっきー (@ftnext) 2023年4月16日
DiscordもSlackもWebhookでいけると思うので、urllibとかrequests使えば共通化できるんじゃないかな〜と考えています(未検証)
urllibでDiscord Webhookの例https://t.co/xiqRQKmAy5 - この指定も当時検索して見つけたのだと思うのですが、ソースを見つけられませんでした。↩
- DiscordはCloudflareを使っている認識です。↩