Amazon Linux2 で運用しているMinecraft Serverのバージョンアップ
久しぶりにMinecraftを遊ぼうと思ったらバージョンが上がってたので、サーバー側のバージョンも上げることにしました。
環境はAmazon Linux 2で systemctlでサーバープロセスを管理しています。
まずはサーバーを停止。
$ sudo systemctl stop minecraft_server.service $ sudo systemctl status minecraft_server.service
無事に停止できたら、新バージョンを公式からダウンロードします。(現時点では、1.17.1が最新でした。)
$ wget https://launcher.mojang.com/v1/objects/a16d67e5807f57fc4e550299cf20226194497dc2/server.jar
実際に動かしているファイル名はminecraft_server.jar
なので、ファイル名を変更。
(本来なら元のファイル名を別のファイルに退避などをしても良いです。この環境はバックアップを取得していたので省略)
$ mv server.jar minecraft_server.jar
startしてみましたが、起動しませんでした。
$ systemctl start minecraft_server.service $ systemctl status minecraft_server.service ● minecraft_server.service - Minecraft Server Loaded: loaded (/etc/systemd/system/minecraft_server.service; enabled; vendor preset: disabled) Active: failed (Result: start-limit) since Fri 2021-07-16 15:06:17 UTC; 439ms ago Process: 3519 ExecStart=/bin/bash /home/ec2-user/minecraft/boot (code=exited, status=1/FAILURE) Main PID: 3519 (code=exited, status=1/FAILURE) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: minecraft_server.service: main process exited, code=exited, status=1/FAILURE Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: Unit minecraft_server.service entered failed state. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: minecraft_server.service failed. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: minecraft_server.service holdoff time over, scheduling restart. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: start request repeated too quickly for minecraft_server.service Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: Failed to start Minecraft Server. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: Unit minecraft_server.service entered failed state. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: minecraft_server.service failed.
ログを確認すると、Error: A JNI error has occurred, please check your installation and try again
というエラーが見つかりました。
これは、javaのバージョンが古いというエラーです。どうやらMinecraftは1.17系からjava16が必要になったようです。
$ journalctl -ex -u minecraft_server.service (中略) -- Unit minecraft_server.service has begun starting up. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: Error: A JNI error has occurred, please check your installation and try again Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: Exception in thread "main" java.lang.UnsupportedClassVersionError: net/minecraft/server/Main has been compiled by a more recent version Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.lang.ClassLoader.defineClass1(Native Method) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.lang.ClassLoader.defineClass(ClassLoader.java:763) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.net.URLClassLoader.defineClass(URLClassLoader.java:468) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.net.URLClassLoader.access$100(URLClassLoader.java:74) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.net.URLClassLoader$1.run(URLClassLoader.java:369) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.net.URLClassLoader$1.run(URLClassLoader.java:363) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.security.AccessController.doPrivileged(Native Method) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.net.URLClassLoader.findClass(URLClassLoader.java:362) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.lang.ClassLoader.loadClass(ClassLoader.java:424) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at java.lang.ClassLoader.loadClass(ClassLoader.java:357) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal bash[3519]: at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495) Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: minecraft_server.service: main process exited, code=exited, status=1/FAILURE Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: Unit minecraft_server.service entered failed state. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: minecraft_server.service failed. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: minecraft_server.service holdoff time over, scheduling restart. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: start request repeated too quickly for minecraft_server.service Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: Failed to start Minecraft Server. -- Subject: Unit minecraft_server.service has failed -- Defined-By: systemd -- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel -- -- Unit minecraft_server.service has failed. -- -- The result is failed. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: Unit minecraft_server.service entered failed state. Jul 16 15:06:17 ip-172-31-41-86.ap-northeast-1.compute.internal systemd[1]: minecraft_server.service failed.
元々の環境では java8でした。
$ java -version openjdk version "1.8.0_201" OpenJDK Runtime Environment (build 1.8.0_201-b09) OpenJDK 64-Bit Server VM (build 25.201-b09, mixed mode)
そこで、javaのバージョンを上げることにします。
そういえばAmazonが管理しているOpenJDKがあったことを思い出したので、ドキュメント通りにインストールします。
$ sudo rpm --import https://yum.corretto.aws/corretto.key $ sudo curl -L -o /etc/yum.repos.d/corretto.repo https://yum.corretto.aws/corretto.repo $ sudo yum install -y java-16-amazon-corretto-devel
これだけで簡単にバージョンが上がりました。
$ java --version openjdk 16.0.1 2021-04-20 OpenJDK Runtime Environment Corretto-16.0.1.9.1 (build 16.0.1+9) OpenJDK 64-Bit Server VM Corretto-16.0.1.9.1 (build 16.0.1+9, mixed mode, sharing)
これで無事に起動でき、バージョンアップできました!
$ sudo systemctl start minecraft_server.service $ systemctl status minecraft_server.service ● minecraft_server.service - Minecraft Server Loaded: loaded (/etc/systemd/system/minecraft_server.service; enabled; vendor preset: disabled) Active: active (running) since Fri 2021-07-16 15:35:13 UTC; 7s ago Main PID: 23650 (bash) CGroup: /system.slice/minecraft_server.service ├─23650 /bin/bash /home/ec2-user/minecraft/boot └─23651 java -Xms3072M -Xmx3072M -jar /home/ec2-user/minecraft/minecraft_server.jar nogui (略)
『Pythonトリック』を読んだ
『Pythonトリック』が良書だったので紹介します。
翔泳社のページからPDFでの購入もできます。
私は普段の業務ではRubyを使うことの方が多いのですが、Pythonにも興味があり趣味で書くこともあります。 pythonの言語の仕組みやより良いとされている書き方を勉強したかったのですがFluent Python ―Pythonicな思考とコーディング手法やEffective Python 第2版 ―Pythonプログラムを改良する90項目は分厚すぎて尻込みしていたところ こちらの本はコンパクトで立ち読みしたところちょうどよく知りたいことが書かれていた感じだったので購入してみました。
目次
CHAPTER 1 はじめに 1.1 Pythonトリックとは何か 1.2 本書はどのように役立つか 1.3 本書の読み方 CHAPTER 2 よりクリーンなPythonのためのパターン 2.1 アサーションによる安全対策 2.2 無頓着なコンマの配置 2.3 コンテキストマネージャーとwith文 2.4 アンダースコアとダンダー 2.5 文字列のフォーマットに関する衝撃の事実 2.6 「The Zen of Python」の隠しコマンド CHAPTER 3 効果的な関数 3.1 Pythonの関数はファーストクラスオブジェクト 3.2 ラムダは単一式の関数 3.3 デコレータの威力 3.4 *argsと**kwargs 3.5 引数のアンパック 3.6 ここから返すものは何もない CHAPTER 4 クラスとオブジェクト指向プログラミング 4.1 オブジェクトの比較:”is”と”==” 4.2 文字列変換:すべてのクラスに_repr_が必要 4.3 カスタム例外クラスを定義する 4.4 趣味と実益を兼ねたクローンオブジェクトの作成 4.5 抽象基底クラスは継承に待ったをかける 4.6 名前付きタプルは何に役立つか 4.7 クラス変数とインスタンス変数の落とし穴 4.8 インスタンスメソッド、クラスメソッド、静的メソッドの謎を解く CHAPTER 5 Pythonの一般的なデータ構造 5.1 ディクショナリ、マップ、ハッシュテーブル 5.2 配列 5.3 レコード、構造体、DTO 5.4 セットとマルチセット 5.5 スタック(LIFO) 5.6 キュー(FIFO) 5.7 優先度付きキュー CHAPTER 6 ループとイテレーション 6.1 パイソニックなループの書き方 6.2 内包を理解する 6.3 リストのスライスとすし演算子 6.4 美しいイテレータ 6.5 ジェネレータは単純化されたイテレータ 6.6 ジェネレータ式 6.7 イテレータチェーン CHAPTER 7 ディクショナリのトリック 7.1 ディクショナリのデフォルト値 7.2 趣味と実益を兼ねたディクショナリのソート 7.3 ディクショナリを使ってswitch/case文をエミュレートする 7.4 型破りなディクショナリ式 7.5 ディクショナリのいろいろなマージ法 7.6 ディクショナリの出力を整える CHAPTER 8 パイソニックな生産性向上テクニック 8.1 Pythonのモジュールとオブジェクトを調べる 8.2 仮想環境を使ってプロジェクトの依存関係を分離する 8.3 バイトコードの裏側を覗く CHAPTER 9 最後に 9.1 Python開発者のためのメールマガジン 9.2 PythonistaCafe:Python開発者のコミュニティ
2章は手取り早く役にたつTips集といった感じで、この章だけでも読むとこの本がどんな本かわかると思います。
3章以降は関数、クラス、イテレータ、データ構造などそれぞれのトピックを取り上げて色々なテクニックを紹介する内容になっています。 各章の内容は独立していて、どこから読んでも良いような内容になっているので、とりあえず興味のある章だけ読むという読み方をしても良いと思います。
プログラミング言語初心者でオブジェクト志向はなんとなくわかるというレベルなら4章から、すでに他の言語をある程度理解していてPythonならではの書き方や特徴を知りたいというかたは5~6章、関数型の書き方に興味があるという方は3章がおすすめです。
さらに、各節の最後には内容の簡単な要約もついているので、上級者の場合は最初に要約から読んで知らなった項目だけ読むということもできます。 (私の場合は70%くらいが初めて知る内容でした。)
with文に対応したObjectの作り方
ここからは私が特に面白いと感じた内容について簡単に紹介していきます。
with文はファイル操作などでよく見る書き方です。
with open('hello.txt', 'w') as f: f.write('hello, world')
この場合はwithブロックから抜けたらすぐに自動でf.close()
を行うことで、安全にリソースを管理することができるようになっています。
with文に対応するオブジェクト(=コンテキストマネージャと呼ばれるらしい)は、このように必ず最初と最後に行いたい処理がある場合に便利な機能です。
実はこのようなコンテキストマネージャは自作クラスや関数でも簡単に作れるようです。
私が試しに考えてみた、環境変数を一時的に変更できる関数を紹介します。
import contextlib import copy from os import environ @contextlib.contextmanager def env(env: str, value): original = copy.copy(environ[env]) # 元の環境変数を保存 environ[env] = value # 環境変数を上書き yield environ[env] = original # 元あった値に戻す # 使い方 print(environ["USER"]) # tarou with env("USER", "honako"): print(environ["USER"]) # hanako print(environ["USER"]) # tarou
詳しい仕組みはぜひ書籍を読んでいただきたいですが、ざっくりと解説すると、@contextlib.contextmanager
というデコレータを使うことで、
自作のenv関数をwth文に対応させています。
関数の中のyield
までがwith文の最初に処理される部分、yield
以降がwithブロックから抜けた後に実行される処理です。
ここでは最初に既存の環境変数を別の変数に保存し、最後に再び元の環境変数にセットし直すことで一時的に環境変数を変更するということができました。
これ以外にも最初と最後になんらかの処理を実行したいという時にはwith文に対応した関数を使えば書くほうも読む方も楽になりそうなので、便利に使えそうです。
また、この本では関数ではなくオブジェクト(クラス)をwith文に対応させる方法も書かれています。 本の中で宿題として「time.time関数を使ってコードブロックの実行時間を計測するコンテキストマネージャーを実装してみてください」と書かれていたので、 自分なりに考えて実装してみました。
こちらはクラスベースの実装になっていますが、ネタバレになってしまう可能性もあるので心配な方はぜひ書籍を読んでから見てみてください。
Cloud9から既存EC2(tokyo)に初回設定のSSHができない問題を解決
Cloud9から既存EC2(tokyo)に初回設定のSSHができない
Tokyoリージョンに立てた開発用のEC2にCloud9(シンガポールリージョン)からの初期設定のSSHができなくて困りました。
以下のことを行うことで設定できました。
- nodejsがEC2に入っていることを確認する
node -v
/home/user/.ssh/authorized_keys
にコンソールからコピーした公開鍵を貼る- セキュリティーグループのTCP,PORT 22を一旦0.0.0.0/0に解放する
- Environment pathはデフォルトで/home/ec2-userを見に行くため、ubuntuやcentosの場合やユーザー名を変更している場合は個別で設定する。
/home/ubuntu
セキュリティグループ をよりセキュアに設定したい
セキュリティグループ が0.0.0.0/0に解放したままでは危険のため、セキュリティグループ の設定をAWSのシンガポールリージョンのCloud9のIPのみに設定します。
AWSのIP範囲はこちらから確認できます。
AWS IP アドレスの範囲 - アマゾン ウェブ サービス
このドキュメントの通りに、jsonからIPを抜き出します。
# 一旦jsonをファイルに保存 curl https://ip-ranges.amazonaws.com/ip-ranges.json > ip-ranges.json # Linux等、jqが使える場合 jq -r '.prefixes[] | select(.region=="ap-southeast-1") | select(.service=="CLOUD9") | .ip_prefix' < ip-ranges.json >>> 13.250.186.128/27 >>> 13.250.186.160/27
これで、13.250.186.128/27と13.250.186.160/27に対してセキュリティグループ のTCP, Port 22を解放すればOKです。
Cloud9のリージョンがシンガポール以外の場合は.region==""
の部分を適宜変更してください。
AWS クロスアカウント権限管理 「リソースポリシーからrootに許可するパターン」編
クロスアカウントで色々いじっていて、IAMの権限管理についてのパターンがなんとなくわかってきたのでメモします。
クロスアカウントでの権限管理は色々なパターンがあるようですが、今回扱うのは「AアカウントのリソースポリシーでBアカウントのrootを許可→Bアカウントの中でポリシーアタッチ」のパターンです。
具体的に見ていきましょう。まずはよくあるS3のパターンです。
別アカウントのS3バケットにLambdaからアクセスする
ここではアカウントID:111111111111
のアカウント1
からアカウントID:0000000000000000
のアカウント0
のS3バケットにアクセスしたいとします。
アカウント0のコンソールで、S3のバケットポリシーを以下のように設定します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Example permissions", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::1111111111111:root" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::BUCKET_NAME/*" } ] }
ここではアカウント1のrootユーザーに対してS3バケットへのアクセスを許可しています。
次に、許可をもらったアカウント1のコンソールで以下のポリシーを作成し、S3バケットにアクセスするLambdaにアタッチします。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject" ], "Resource": "arn:aws:s3:::BUCKET_NAME/*", "Effect": "Allow" } ] }
これで、ポリシーをアタッチされたLambdaはアカウント0のS3バケットに対してアクセスすることができるようになります。
まずはアカウントのrootユーザーに権限を与える → 権限を得たroot(admin)ユーザーがリソースに対して権限を与えるという流れでわかりやすいと思います。
もちろん、最初にバケットポリシーで許可したActionしかできないようになっているので、例えば今回の場合s3:GetObject以外の操作権限はLambda にもrootユーザーにも与えられていません。
LambdaからObjectを新たにPUTしたりするためには、再びアカウント0の管理者にバケットポリシーからs3:PutObjectを許可してもらう必要があります。
別アカウントのLambdaにLambdaからアクセスする
先ほどと同様にアカウント1のLambdaからアカウント0のLambdaにアクセスしたいとします。
Lambdaの場合はリソースポリシーというものがあるので、ここで、アカウント1のrootユーザーに権限を与えます。
参考: AWS Lambda でリソースベースのポリシーを使用する (Lambda 関数ポリシー) - AWS Lambda
この操作は現在AWS CLIやAPIでしか行えないようです。
aws lambda add-permission \ --function-name FUNC_NAME \ --statement-id Example permissions \ --action lambda:InvokeFunction \ --principal 111111111111 \ --output json
上記のコマンドを実行すると以下のjsonが返ってきます。
{ "Statement": "{\"Action\":[\"lambda:InvokeFunction\"], \"Resource\":\"arn:aws:lambda:YOUR_REGION:000000000000:function:FUNC_NAME\", \"Effect\":\"Allow\", \"Principal\":{\"AWS\":\"111111111111\"}, \"Sid\":\"Example permissions\"}" }
CLIなので、見ためは違いますが、やっていることとしては先ほどのバケットポリシーと対して変わりません。アカウント1のrootユーザーに対してアカウント0のLambdaへのlambda:InvokeFunction
の権限を与えています。
次に、アカウント1のコンソールから、Lambda(アクセスする方)の実行ロールにアカウント0のLambdaへアクセスする権限を与えるポリシーをアタッチします。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "lambda:InvokeFunction" ], "Resource": "arn:aws:lambda:YOUR_REGION:000000000000:function:FUNC_NAME", "Effect": "Allow" } ] }
これで、アカウント1のLambdaからアカウント0のLambdaにアクセスすることができるようになります。
おまけ: kmsのキーポリシーをIAMポリシーで管理する
実はクロスアカウントの権限管理以外にも、似たような権限の与え方をするパターンがあります。
それが、KMSのキーポリシーです。
AWSでは若干複雑なことに、KMSで作ったCMKに対する権限をKMSのキーポリシーで管理するということになっています。
ただ、特にLambdaに関しては実行ロールに付与されているIAMポリシーでLambdaの権限を管理できた方がわかりやすいため、IAMポリシーで管理できた方が便利だと思います。
そういった場合に、キーポリシーをIAMポリシーで管理できるようにするキーポリシーの書き方が以下です。
{ "Sid": "Enable IAM User Permissions", "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::111111111111:root"}, "Action": "kms:*", "Resource": "*" }
これで、IAMポリシーでKMSのCMKの権限を管理できるようになります。 Lambdaの実行ロールには以下の権限を付与します。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "kms:*" ], "Resource": "arn:aws:kms:YOUR_REGION:111111111111:key/xxxxxxxxxx", "Effect": "Allow" } ] }
【Python】responder でHello, World ! 0.0.0.0をhostに指定する方法
responder でHello World! 0.0.0.0をhostに指定する方法
人間のためのイケてるPython WebFramework「responder」、そして作者のKenneth Reitzについて - フリーランチ食べたい
こちらの記事で話題になっているresponderを早速試して見ました。
Hello World ! するまでに2つひっかかったポイントがあったので、共有します。
ちなみにresponderどころかDjangoもFlaskも使ったことのない初心者なので、おかしな内容になっているかもしれません。ご容赦ください。
※2018年12月2日時点での情報です。
環境 * AWS EC2 Ubuntu Server 18.04 LTS (HVM), SSD Volume Type * Python 3.6.5
ModuleNotFoundError: No module named 'starlette.lifespan'
とりあえずこちらのクイックスタート通りにHello World ! を書いてみました。 Quick Start! — responder 1.1.2 documentation
import responder api = responder.API() @api.route("/") def hello_world(req, resp): resp.text = "hello, world!" api.run()
実行したところModuleNotFoundError: No module named 'starlette.lifespan’
というエラーが出てしまいました。
これはgithubのissueにもあげられていました。
ModuleNotFoundError: No module named ‘starlette.lifespan’ · Issue #255 · kennethreitz/responder · GitHub.
problem solved by using starlette 0.8 version
とりあえず今のところはstarletteのバージョン0.8を使えば解決するようです。
pip install starlette==0.8
hostに0.0.0.0を指定したい
ubuntu@ip-172-31-43-88:~/dev/test$ python hello.py INFO: Started server process [17400] INFO: Waiting for application startup. INFO: Uvicorn running on http://127.0.0.1:5042 (Press CTRL+C to quit)
これはEC2を開発環境にしていると毎回困ることなのですが、localhostや127.0.0.1にアクセスするのが困難です。
0.0.0.0を開くようにしないとローカルのブラウザから確認できません。
Responderのコードを読むと、どうやら環境変数として”PORT”が設定されている場合は0.0.0.0:PORTが開かれ、それでない場合は127.0.0.1:5042になるようです。
if "PORT" in os.environ: if address is None: address = "0.0.0.0" port = int(os.environ["PORT"]) if address is None: address = "127.0.0.1" if port is None: port = 5042
ということで適用にわかりやすいPORTを設定して、
ubuntu@ip-172-31-43-88:~/dev/test$ export "PORT"=10001
EC2のセキュリティグループも変更します。
そしてブラウザからアクセスすると、
Hello World ! が無事表示されました 。
ubuntu@ip-172-31-43-88:~/dev/test$ python hello.py INFO: Started server process [17405] INFO: Waiting for application startup. INFO: Uvicorn running on http://0.0.0.0:10001 (Press CTRL+C to quit) INFO: ('m.y.i.p', 53094) - "GET / HTTP/1.1" 200
追記: api.run()の引数にaddressとportを与えても同様のことができるようです。
api.run(address='0.0.0.0',port=10001)
【Python】requestsでボタンを擬似クリック
requestsでボタンを擬似クリック
Beautiful Soupでhrefが取得できないボタン等のクリック動作をrequestsで再現する方法を調べたので、まとめます。
hrefが取得できないボタン
例えばこのページの「CSVダウンロード」ボタン部分などはBeatiful Soupでもhrefが取得しにくいためにrequestsでのスクレイピングが困難です。
CSVダウンロード
「csvダウンロード」ボタンのソースコードはこのようになっています。
<input class="btn btn-primary" type="submit" value="CSVダウンロード">
おそらくこのボタンを押すとなんらかのscriptが動いてダウンロードが始まるものと思います。
requestsでこのボタンをクリックするという動作を再現することはできないので、seleniumを使うといった選択肢をとることが多いかもしれませんが、場合によってはrequestsだけでボタンをクリックしたのと同等の通信を再現することができます。
以下がその方法です。
crhomeデベロッパーツールでnetworkを見てみる
デベロッパーツールを表示した状態でボタンをクリックすると、networkタブでどのような通信が行われているか確認することができます。
今回のダウンロードもバッチリURLを確認することができました。
URLはhttps://www.itdashboard.go.jp/Csv/download
で、ここにPOSTでdataを送れば良さそうです。
cookieなどはrequestsのsession()を使えば処理してもらえます。
POSTで送るdataもデベレッパーツールから確認できます。
最終的なコードはこのようになりました。
import requests # ページにアクセス session = requests.Session() res = session.get('https://www.itdashboard.go.jp/DataFeeds/csv') res.raise_for_status() # エラーならここで例外を発生させる # dataに項目をセット data = { "_method": "POST", "year": "", "modelname": "BasicInformationAll" } # クリック時の通信 res = session.post('https://www.itdashboard.go.jp/Csv/download', data=data) res.raise_for_status() # エラーならここで例外を発生させる # レスポンスで返ってきたコンテンツをcsvとして保存 contentType = res.headers['Content-Type'] contentDisposition = res.headers['Content-Disposition'] ATTRIBUTE = 'filename=' fileName = contentDisposition[contentDisposition.find(ATTRIBUTE) + len(ATTRIBUTE):] with open(fileName, 'wb') as saveFile: saveFile.write(res.content)
ファイルを保存する部分はこちらの記事を参考にしました。
Pythonのrequestsを利用してファイルダウンロードする方法 - Qiita