ライブラリアン・コラム
コマンドプロンプトを使ってみよう(3)――大量のファイルダウンロード
今満 亨崇
2024年7月
はじめに
コマンドプロンプトを使用すると、1行のコマンドでweb上のファイルをダウンロードすることができる。少し工夫すれば、大量のファイルダウンロードも容易である。これができるメリットは何か、筆者の経験を述べると、まず、「システムアップデート前のwebサイトのバックアップが作成できる」点が挙げられる。実例を示すと、2023年に行われたJAIRO Cloud(アジア経済研究所の機関リポジトリARRIDEを構築するために使用しているクラウドサービス)のメジャーアップデート前に全ページのバックアップを取得しておいた。これにより、昔の画面・データはどのようなものだったのかをいつでも確認でき、新環境において「昔はどうだったかな?」という細かい疑問がすぐに解消できた。
また、「分析用のデータ取得ができる」点も挙げられる。多くのwebサービスではAPIを公開してデータを機械的に取得できるようにしており、例えば木下のように蔵書評価1に活用することができる。木下は自館と他館の重複所蔵資料を調査しているが、他館の所蔵情報はCiNii Books APIで取得している。一般的にはプログラムを書くことが多いが、情報収集のために大量のファイルをダウンロードする処理だけをみればコマンドプロンプトでも可能であるし、学習コストも低い。
本記事ではCiNiiを例に、検索にヒットした書誌全件の詳細情報XMLをダウンロードする方法を解説する。大まかな流れは、(1)CiNii Booksの検索結果をXMLで取得する、(2)そのXMLから書誌詳細情報のURLを抽出する、(3)書誌詳細情報のXMLを取得する、という手順で進めていく。なお、紹介する手順の実施は自己責任で行うこと。筆者および日本貿易振興機構はその結果に対していかなる責任も負わない。
事前準備
本記事ではExcelでコマンドの生成を行う箇所がある。サンプルのExcelファイル(以下、「サンプルファイル」という)を公開しているので、併せてご確認いただきたい。
また、CiNiiのAPIはデベロッパー登録が必要である。https://support.nii.ac.jp/ja/cinii/api/developerを参照し、利用規程や細則を確認したうえで登録をしておこう。本記事中では登録時に発効されるappidを"XXXXXXXXXX"と表現する。
CiNii Booksの検索結果をXMLで取得する
URLの確認
まずはCiNii Books(https://ci.nii.ac.jp/books/)を開いて、どのような検索結果が必要かを考えよう。今回は、「フリーワード=アジア経済、図書館ID=FA011033、出版年=2020年から2023年まで」でCiNii Booksを検索したものとして話を進める。
検索結果は96件で、webブラウザのアドレスバーを見ると、次のURLが表示されている2。
https://ci.nii.ac.jp/books/search?advanced=true&count=20&sortorder=3&q=%E3%82%A2%E3%82%B8%E3%82%A2%E7%B5%8C%E6%B8%88&type=0&year_from=2020&year_to=2023&fano=FA011033&update_keep=true
URLを前から見ていくと"?"以降は、"パラメータ名=値"の形式になっており、例えば"year_from=2020"は「出版年=2020年から」に対応している。そのためアドレスバーに表示されているURLのこの部分を"year_from=2021"に書き換えてEnterを押下すると、画面内の詳細検索ボックスを編集しなくても、検索条件を変えることができる。複数の条件は"&"で区切られていることも確認しておこう。
このURLと、CiNii Booksの図書・雑誌検索のOpenSearchの説明(https://support.nii.ac.jp/ja/cib/api/b_opensearch)を見比べながら、検索結果をXMLで取得できるよう修正していく。先に結果を示すと次のようになる(A)3。
https://ci.nii.ac.jp/books/opensearch/search?advanced=true&count=20&sortorder=3&q=%E3%82%A2%E3%82%B8%E3%82%A2%E7%B5%8C%E6%B8%88&type=0&year_from=2020&year_to=2023&fano=FA011033&update_keep=true&appid=XXXXXXXXXX&p=1
まずはURLの"/"区切りの中に、"opensearch/"の階層を追加している。これにより通常の検索結果画面ではなくXMLでデータを取得できるようになる。"appid"はAPI利用登録の際に発効されたものであり、API利用時には必ず追加する必要がある。最後の"p"は検索結果のページ番号である。今回のURLでは1ページあたり20件の結果が取得できるので、"p=1"は1~20件目、"p=2"は21~40件目のデータを取得できる。
(A)のURLをコピーし、XXXXXXXXXXを自身が取得したappidに修正して、webブラウザでアクセスしてみよう。実際の動作が確認できるはずだ。もし画面に書籍のタイトルや出版社名が全く表示されないようであれば、何か間違っているので確認してほしい。また、"p="の最大値は何にすべきかを確認しておこう。今回の検索結果は96件なので、96件 / 20件 = 5(余りは切り上げ)と計算できるが、仮に"p=6"としてアクセスしたときの表示も確認しておくとよい。
検索結果取得コマンドの生成(1回分)
すべての検索結果を取得するために、ここではコマンドプロンプトを使用する。処理は(A)で確認したURLにアクセスしてXMLを取得し、名前を付けて保存し、次の処理のためのインターバルを設け、これら処理を"p="の値を変えながら繰り返し実施することになる。最初に1回分の処理コマンド(B)と説明を下に記す。
curl -sS -o s1.xml "https://ci.nii.ac.jp/books/opensearch/search?advanced=true&count=20&sortorder=3&q=%E3%82%A2%E3%82%B8%E3%82%A2%E7%B5%8C%E6%B8%88&type=0&year_from=2020&year_to=2023&fano=FA011033&update_keep=true&appid=XXXXXXXXXX&p=1" & ping 127.0.0.1 -n 6 > nul
- curl:指定したURLのデータを取得するコマンド。通常は取得したデータをそのままコマンドライン上に表示するが、"-o"のオプションを付与することにより名前を付けて保存できる(ここではs1.xmlはファイル名)。"-sS"はデータ取得の進捗等を非表示にするオプションであり、無くても動く。
- &:"&"の左側の処理が終わったら、右側の処理を実施させる記号。
- ping:本来は指定したドメインやIPアドレス宛に通信できるかを確認するためのコマンド。処理内容をそのまま言葉にすると、「自分のPC(127.0.0.1)と通信できるか6回試行("-n"のオプション)する」となるが、今回はpingの試行間隔が約1秒であることを利用し、「次の処理との間にインターバル(5秒)を設ける」ために使用している。
- > nul:処理内容をそのまま言葉にすると「コマンドの実行結果を空(カラ)に書き込む」となるが、ここで利用する意図は、上記pingの結果をコマンドプロンプトの画面に表示させないためであり、この記述は無くても問題なく動作する。なお">"と"nul"は独立した命令であり、別の文脈ではそれぞれ別の用途でも用いられる。
ここまで読んだら、コマンドプロンプトを起動4して(B)のコマンドを実行してみるとよいだろう(もちろん、"appid"は各自で取得したものに変更する必要がある)。"-sS"や" -o s1.xml"や"-n 6 > nul"を削除したり、" -n"の値を変更して実際の動作を確認してみると理解がより深まる。
なおインターバルを5秒としているが、この時間は短いとwebサービスへの攻撃とみなされ、長いと処理時間が膨大になってしまう。筆者は通常は5秒、早く処理したいときは3秒、動作が遅いwebサービスだと7秒を目安にしている。適切な時間設定の参考となれば幸いである5。
検索結果取得コマンドの生成(全件分)
(B)を元に検索結果全件を取得できるコマンド群をExcelで生成しよう。URLで変動させるべき個所は、赤字にした"1"(保存する際のファイル名、URLパラメータ"p="の値)である。サンプルファイルの[1.全検索結果取得]シートを参照してほしい。まずは3行目に固定値とすべきものを入力している。具体的には下記の通りである。[B3]セルにURLを入力しているが、末尾の数字(p=の値)を削除している点に注意が必要である。また、各命令の区切りは半角スペースとなっているが、Excelのセル内で文頭、文末に半角スペースを入力しても分かりづらい。そのため固定値の項目にこれを入力するのは避け、後の工程で対応している。
- [A3]1. データ取得コマンド:curl -sS -o
- [B3]2. URL(除くp値):https://ci.nii.ac.jp/books/opensearch/search?(中略)appid=XXXXXXXXXX&p=
- [C3]3. 処理待ちコマンド:ping 127.0.0.1 -n 6 > nul
続いて、[A7]セル以下に数字を入力している。[A7]セルに1を入力して、Excelのオートフィル機能を用いれば、下方向への連番が簡単に入力できる。この連番は"p="の取り得る最大値(今回の例では"5")まで入力しておこう。
最後に、これら固定値と変数を結合してコマンドを仕上げていこう。[B7]セルでは=CONCAT($A$3, " s", A7, ".xml ", """", $B$3, A7,"""", " & ", $C$3)という関数を入力している。CONCATは( )内で指定した文字列を結合する関数であり、入力にあたっての補足・注意点は次の通りである。[B7]セルに(B)と同じコマンドが表示されたら、[B8]セル以下にExcelのオートフィル機能やコピー&ペーストで関数を適用しよう。
- $A$3:固定値に入力したものである。関数を[B8]セル以下に適用させたときに参照するセルがずれないように、"$"で参照先を固定している。この"$"は数式入力中にF4キーを押すことでも入力できる。
- " s", A7, ".xml ":この3つの値を結合してファイル名にしている。sの前、xmlの後ろにそれぞれ半角スペースが入力されている点に注意が必要である。ここでは、"p="の値をファイル名に流用することにしたので、[A7]セルを参照している。
- """", $B$3, A7,"""":この4つの値を結合してURLを完成させている。$B$3の説明は$A$3に同じ。Excelの関数の中でダブルクオーテーション1文字を示すには、ダブルクオーテーションを4回並べて記述する。なお、この並びの中での各文字の意味は次のようになる。
- ここから文字列が始まることを示す記号。
- 次に来る文字は特殊な文字(例えば、文字列の終わりであることを示す記号)【ではない】ことを示す記号。
- 実際に表示されるダブルクオーテーション。
- ここで文字列が終わることを示す記号。
コマンドの実行
Excelでコマンド群を生成したら、コマンドプロンプトでの操作に移ろう。今回は「C:\cinii\1_search」というフォルダを作成しておいたので、cdコマンドを利用して移動しよう(図3の1行目)。
その後、前ステップで作成したコマンド([B7~B11]セル)をコピーする。[B7]セルをクリックした後、「Shift+Ctrl+↓」キーを押下すると、コマンドが入力されたすべてのセルを選択できるので活用してほしい。コピーしたらその後、コマンドプロンプトの画面上で「右クリック」する。これで貼り付けの動作となり、即時コマンドが実行される。すべての処理が実行完了したら、コマンドプロンプトは入力待ち(図3の最終行参照。ディレクトリパスと">"が表示される)の状態となる。また、実際に作業用ディレクトリをエクスプローラーで開いてみると、XMLファイルが生成されている。なお、コマンドプロンプトの画面上で「左クリック」するとコマンドプロンプト上の文字をコピーしてしまうので、Excelからの貼り付けは「右クリックを2回(1回目はコマンドプロンプトをアクティブにする、2回目はコマンドの貼り付け&実行)」と覚えておくのがよい。
詳細情報URLを抽出する
XMLファイルの確認
取得したXMLファイルをwebブラウザやメモ帳で開いてみよう。冒頭でヒット件数などデータの概要が示された後、<entry></entry>タグに囲われた部分を1件分として、簡単な書誌情報などが表示される。この中でも特に、 <link rel="alternate" type="application/rdf+xml" hrefから始まる行に着目すると、CiNiiの詳細ページURLに".rdf"が付加されたURLが表示されている。このURLにアクセスすると各図書・雑誌の詳細情報をXMLで取得できる。詳細はCiNii Booksの、図書・雑誌情報のRDFの説明(https://support.nii.ac.jp/ja/cib/api/b_rdf)をご参照いただきたい。
URLを含む行の抽出
では複数存在する検索結果XMLファイルから、詳細ページURLを一気に抽出しよう。まずは事前準備として"CHCP 65001"コマンドを実行することを推奨する。これはコマンドプロンプトの文字コードをUTF-8に変更するコマンドだ。取得したXMLファイルはUTF-8だが、コマンドプロンプトの文字コードはShift-JISであるため、ファイルの内容を表示すると文字化けしてしまう(ただし本記事中にこの過程は記述していないので、ただ手順を真似するだけなら困ることはない)。
次に、URLの抽出を実行する。使用するコマンドは次のとおりである。
findstr /c:"<link rel=\"alternate\" type=\"application/rdf+xml\" href" *.xml >> urllist.txt
- findstr:指定したファイルに、指定した文字列が含まれていればその行を出力するコマンド。/c:オプションの後にダブルクオーテーションで囲った検索対象文字列を記述する。注意が必要なのは、検索対象文字列にダブルクオーテーションを含む場合である。コマンド用の記号なのか検索対象文字なのかを明確に区別するため、検索対象のダブルクオーテーション前には\を入力する必要がある。また本コマンドでは、UTF-8ファイルの全角文字列は検索できない(Shift-JISならできる)ので、別の例に応用する場合には注意が必要である。
検索対象とするファイルは*.xmlと記述することで、同フォルダ内のすべてのXMLファイルを対象にできる(*はその場所にどのような文字が何文字あってもよいことを示す)。 - >>:">>"の左側の処理結果を、右側で指定するファイルに追記させる記号。ここではurllist.txtというファイルに書出しさせている。追記なので、一度行った処理をやり直す際は事前にファイルを削除しておかないと、前回の結果が残ることに注意。この記号以降の記述を省略すると、ファイルではなく画面に処理結果が表示される。
URLの抽出
前ステップで抽出したURLの行は、不要な文字列も多く含まれている。そこでここでは、Excelの機能を用いて純粋なURLだけを抽出する。まずはurllist.txtをメモ帳で開いて、すべて選択(Ctrl+A)して、コピーし、Excelの適当なシートへ貼り付ける。サンプルファイルでは、[2.url抽出]シートを参照してほしい。このシートでは同じデータを、A列に比較用の作業前データとして、B列に実作業用のデータとして、貼り付けしている。A列は無視して問題ない。
さて、「B列を選択 > データ > 区切り位置 > コンマやタブなどの区切り文字によってフィールドごとに区切られたデータ > 次へ > ☑その他:" > 次へ > 完了」とクリックしていこう。これでG列がURLだけの行となったはずだ。
詳細情報XMLを取得する
本セクションの説明は「CiNii Booksの検索結果をXMLで取得する」のセクションとほぼ変わらないので、解説や注意点はそちらを参照してほしい。さて、詳細情報1件をCiNiiから取得するには下記のコマンドを用いる。赤字にした部分(ファイル名及びURL)は別の詳細情報を取得する際に変更すべき個所である。
curl -sS -o d1.xml https://ci.nii.ac.jp/ncid/BD05099543.rdf?appid=XXXXXXXXXX & ping 127.0.0.1 -n 6 > nul
サンプルファイルの[3.詳細取得]シートが、本セクションに対応するものである。3行目に固定値とすべきもの(「1. データ取得コマンド」「2. appid」「3. 処理待ちコマンド」)を入力している。[A3][C3]セルに変更は無いが、[B3]セルは、"?appid="を冒頭に追記したappidを入力している。これは前ステップで作成した詳細ページURLと結合してリクエストURLとするために用いる。続いて[A7]セル以下に連番を入力しており、これは取得した詳細情報XMLを保存するファイル名のためだけに使用する。[B7]セル以下には前ステップで作成した詳細ページURLをコピー&ペーストしている。
最後に、これら固定値と変数を結合してコマンドを仕上げていこう。[C7]セルでは=CONCAT($A$3, " d", A7, ".xml ", B7, $B$3, " & ", $C$3)という関数を入力している。結果に問題なければ、[C8]セル以下にも同関数を適用しよう。
Excelでコマンドが生成できたら、コマンドプロンプトでの操作に移ろう。詳細情報の取得には「C:\cinii\2_detail」というフォルダを作成しておいたので、cdコマンドを利用して移動しよう(図8の1行目)。 その後、前ステップで作成したコマンド([C7]セル以下)をコピーし、コマンドプロンプト上で右クリックして実行しよう。1行ずつ処理が実行され、大量のXMLファイルが生成される。処理が完了したら適当なファイルを開いて、詳細情報が取得できていることを確認しよう。
この処理は、URL1件につき5秒+αの時間がかかり、件数によっては長時間かかってしまう。もし処理を中断しなければならなくなったら、コマンドプロンプトを×で終了するしかないので、時間に余裕をもって実施するか、一度にExcelからコピーするのを100件ずつにするなどして調整しよう。
おわりに
本記事ではCiNii APIを例に、web上から大量のデータをダウンロードする方法を示した。これらXMLファイルから必要な情報を抽出する方法は次の記事で紹介できればと考えている。
記事中では例示していないが、curlコマンドはHTMLでもPDFでも、URLでアクセスできるページ、ファイルであればダウンロードできるので、機会があれば試してみて欲しい。一方で今回の方法は、ダウンロードの失敗を簡便にキャッチアップする方法がない。必要な情報が全件取得できているかは、保存されたファイルを確認する、コマンドプロンプトの画面を遡ってエラーの出力を確認するなど、ひと手間かけて確認する必要がある。この点においてはプログラムを書いて処理する方法に軍配が上がる。また、これはプログラムを書いても同様であるが、要ログインページへの対応や、保存するファイルによるPCの容量圧迫が生じれば追加の工夫を要する。
決して万能な方法ではないが、仕事の幅を広げる方策として記憶の片隅にでも留めておいていただけると幸いである。
著者プロフィール
今満亨崇(いまみつみちたか) アジア経済研究所学術情報センター図書館情報課。担当は図書館の管理するシステム全般。最近の活動に日本図書館協会開催の研修講師や「特集:インフォプロのためのプログラミング事例集」(情報の科学と技術 70巻4号)の編集担当主査などがある。
注
- 木下雅弘「アジア情報室所蔵中国語図書の蔵書評価 ― CiNii Booksとの比較を通じて ―」『アジア情報室通報』vol. 21, no.3, 2023. p.13-16, https://dl.ndl.go.jp/pid/13013043(参照 2024-04-26).
- 人によっては次のように表示されている("アジア経済"の赤字部分が差分)。URLは全角文字を使用することができないため、半角英数字に置き換えられる(これをURLエンコードという)が、最近のwebブラウザは人間が理解しやすいように全角文字に再変換して表示している。
https://ci.nii.ac.jp/books/search?advanced=true&count=20&sortorder=3&q=アジア経済&type=0&year_from=2020&year_to=2023&fano=FA011033&update_keep=true - 今回は検索結果全件を取得すること、およびブラウザで検索した結果を用いてOpenSearch用のURLを作成したことから、無駄なパラメータも含まれている。下記で赤字にしたパラメータ(advanced, count, sortorder, type, update_keep)は削除しても動作するし、コマンドもすっきりする。 https://ci.nii.ac.jp/books/opensearch/search?advanced=true&count=20&sortorder=3&q=%E3%82%A2%E3%82%B8%E3%82%A2%E7%B5%8C%E6%B8%88&type=0&year_from=2020&year_to=2023&fano=FA011033&update_keep=true&appid=XXXXXXXXXX&p=1
- 起動方法などは拙稿「コマンドプロンプトを使ってみよう」の冒頭をご参照頂きたい. https://www.ide.go.jp/Japanese/Library/Column/2022/0804.html
- Librahack事件では1秒1アクセスが攻撃とみなされた。この判断には専門家からも疑義が挙げられているが、無用なトラブルを避ける観点からも余裕を持ったアクセス頻度とするべきである。上原哲太郎「JANOG的にLibrahackを解説する」https://www.janog.gr.jp/meeting/janog27.5/doc/janog27_5-librahack-pub-uehara.pdf(参照 2024-4-30).