【Rust】Pythonのミラーリストからインストール可能なバージョンをスクレイピングする
この記事は Rust Advent Calendar 2021 の21日目の記事です。
はじめに
本来はTaKO8Ki/frumに触発されPythonのパッケージマネージャCLIアプリを作成し紹介しようかと思ったのですが、開発が間に合わなかったため副産物のPythonのミラーリングリストスクレイピングの紹介をします。
スクレイピングする理由
プログラミング言語のバージョンマネージャを作成する際に必須の機能、それはインストール可能なリストの取得です。自分でリストを用意してもいいのですが公式を逐一覗いて更新するのは面倒なのでミラーリングリストを参照しようと思います。そこでRubyやNode.jsはミラーリングリストに(https://cache.ruby-lang.org/pub/ruby/index.txt)や(https://nodejs.org/dist/index.json)のようにtxtやjsonファイルでインストール可能なバージョンの一覧やそのURLがまとまっています。しかしPythonのミラーリングリストである(https://www.python.org/ftp/python/)を覗いてみると一つのファイルにまとまっていません。そのため強引にこのURLからバージョン一覧及びそのバージョンのtar.xzのURLを取得します。
使うクレート
- seanmonstar/reqwest: HTTPクライアント
- causal-agent/scraper: HTMLパース
- dtolnay/semver: Semantic Versioning用のパーサ
実装
ミラーリングリストがどうなっているのか
Pythonミラーリングリストにアクセスしてみると以下の画像のようになっています。
Pythonのバージョンも掲載されていますが余計な文字も多く含まれています。これから余計なものを除いてバージョンリストをベクタに格納することが目標です。
コード
基本的に上で紹介した各クレートのsampleを元にしています。
use semver::Version;
fn main() {
let value = reqwest::blocking::get(format!("https://www.python.org/ftp/python/").as_str())
.unwrap()
.text()
.unwrap();
let mut versions = vec![];
let doc = scraper::Html::parse_document(&value); // 取得したHTMLをパース
let sel = scraper::Selector::parse("a").unwrap(); // さらにaタグについてパース
for (index, node) in doc.select(&sel).enumerate() {
if node.inner_html().is_empty() || index == 0 { // 行頭は必ず../なのでcontinue
continue;
}
let mut version = node.inner_html(); // aタグのうちtextに該当するものを取得
version.retain(|c| c != '/'); // バージョンの後ろにある/を除去
match Version::parse(&version.to_string()) {
Ok(v) => versions.push(v), // semverで定義されたVersionならばversionsベクタに格納
_ => continue, // それ以外はcontinue
}
}
for ver in &versions {
let tar_url = format!(
"https://www.python.org/ftp/python/{}/Python-{}.tar.xz",
ver, ver
); // 各バージョンのtar.xzファイルのURlは2箇所にバージョンを入れるだけ!!
println!("{} - {}", ver, tar_url)
}
}
まとめ
今回はPythonのミラーリングリストからスクレイピングしてインストール可能なPythonのバージョンを取得することができました。かなり強引な手法ではあると思いますが、エラーハンドリングはrust-analyzerに従って修正していれば基本的なものは問題ないはず。まあキチンと取得できているためヨシとします。Pyenvではcondasシリーズやpypyシリーズもインストール可能ですが個人的に両者はあまり使わないので対応させるつもりは有りません。また年内にはPythonのバージョンマネージャCLIアプリを形にしようと思っているためそのt機にはまた投稿します。
今回のコードのリポジトリ
Discussion