一日1回押していただけると大変応援になります↓
はじめに
こんにちは、まっきーです。
たまにはKNIMEではないTopicも書いてみようと思います。ウェブスクレイピングについての備忘録です。ウェブサイトにいくつもリンクがあり、その中から欲しいPDFだけを探してダウンロードしてみたいと思います。
Pythonについてはまだまだ勉強中なので、かなり不器用な書き方をしていることがあると思います。ぜひアドバイスいただければともいます。
github.com
実行環境
よく環境が違うことで動かなくなるということが起こるので、実行環境を記載しておこうと思います。
OS:Windows 10
Python:Python 3.7.3
開発環境:Jupyter Notebook
モジュール
beautifulsoup4:4.7.1
やりたいこと
IT系の資格で有名なものに、基本情報技術者試験(FE)、応用情報技術者試験(AP)などがあると思います。私は今度、IT系の資格の一つであるデータベーススペシャリスト試験(DB)に挑戦しようとしています。
情報処理技術者試験を受ける際、いつも過去問題をダウンロードするのですが、1回の試験で問題・回答・講評があり、なおかつ午前と午後で分かれているので何個ダウンロードさせる気だよ!となります。
そんな面倒な作業はPythonに自動でやってもらおうと思います。
対象のURL・ファイル
下記のサイトからダウンロードします。
IPA 独立行政法人 情報処理推進機構:問題冊子・配点割合・解答例・採点講評(2018、平成30年)
このサイトにはダウンロードしたいデータベーススペシャリスト試験の過去問題だけでなく、他の試験も同様に含まれています。その中で、データベーススペシャリスト試験(DB)だけを抜き出してダウンロードします。
全体フロー
全体のフローはこんな感じです。
- 必要モジュールのImport
- サイトのHTMLを取得・aタグを抜き出す
- aタグからリンクだけを抽出
- 「.pdf」で終わるものについてのみ取得
- データベーススペシャリスト試験のPDFのみを取得
- 相対URLから絶対URLに変換
- URLからファイル名を取得
- 保存先のディレクトリを作成
- ダウンロード実行
Step1 - 必要モジュールのImport
今回使うのは下記のモジュールです。
ウェブスクレイピング用にBeautiful Soup
パスの処理にOS、urljoin
ポーズ用にtime
という形です。
from bs4 import BeautifulSoup
import urllib.request as req
import urllib
import os
import time
from urllib.parse import urljoin
Step2 - サイトのHTMLを取得・aタグを抜き出す
コード
まずはサイトのHTMLからaタグを抜き出してきます。urlの欄に今回のターゲットのリンクを貼り付けます。
url = "https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2018h30.html"
res = req.urlopen(url)
soup = BeautifulSoup(res, "html.parser")
result = soup.select("a[href]")
print(result)
実行結果
現在までの結果は"result"という変数に格納しています。結果を出力すると、PDF以外にも画像用のリンクなど色々含まれていますね。
[<a href="http://www.ipa.go.jp/index.html"><img alt="IPA 独立行政法人 情報処理推進機構" height="35" src="/common/images/logo.gif" width="273"/></a>, <a href="javascript:void(0);" id="fontM">標準</a>, <a href="javascript:void(0);" id="fontL">拡大</a>, <a href="http://www.ipa.go.jp/about/index.html"><img alt="IPAについて" height="28" src="/common/images/head_link01.gif" width="104"/></a>, .....]
Step3 - aタグからリンクだけを抽出
コード
次に、リンクだけを抜き出します。link_listというリンクに格納していきます。
link_list =[]
for link in result:
href = link.get("href")
link_list.append(href)
print(link_list)
実行結果
URLだけ取り除かれましたね。
['http://www.ipa.go.jp/index.html', 'javascript:void(0);', 'javascript:void(0);', 'http://www.ipa.go.jp/about/index.html', 'http://www.ipa.go.jp/about/news/index.html', 'http://www.ipa.go.jp/about/sitemap/index.html', 'http://www.ipa.go.jp/about/inquiry_index_0.html', 'http://www.ipa.go.jp/index-e.html', 'http://www.ipa.go.jp/index.html', '/', '/1_04hanni_sukiru/_index_mondai.html',....]
Step4 - 「.pdf」で終わるものについてのみ取得
コード
続いて、pdfのリンクを抜き出していきます。endswitchという関数を用いてPDFで終わる文字列を抜き出していきます。
pdf_list = [temp for temp in link_list if temp.endswith('pdf')]
print(pdf_list)
実行結果
先ほどのURLリストから、.pdfを抜き出せています。また、このサイトは相対パスで書いていることが分かるので、絶対パスに後程直さないといけませんね。
['mondai_kaitou_2018h30_2/2018h30a_sg_am_qs.pdf', 'mondai_kaitou_2018h30_2/2018h30a_sg_am_ans.pdf', 'mondai_kaitou_2018h30_2/2018h30a_sg_pm_qs.pdf', 'mondai_kaitou_2018h30_2/2018h30a_sg_pm_ans.pdf', 'mondai_kaitou_2018h30_2/2018h30a_sg_pm_cmnt.pdf', 'mondai_kaitou_2018h30_2/2018h30a_fe_am_qs.pdf', 'mondai_kaitou_2018h30_2/2018h30a_fe_am_ans.pdf', 'mondai_kaitou_2018h30_2/2018h30a_fe_pm_qs.pdf', 'mondai_kaitou_2018h30_2/2018h30a_fe_pm_ans.pdf', 'mondai_kaitou_2018h30_2/2018h30a_fe_pm_cmnt.pdf', 'mondai_kaitou_2018h30_2/2018h30a_ap_am_qs.pdf', ...]
コード
先ほど取得したのはすべてのPDFなので、基本情報処理技術者試験などのPDFも含んでいます。私はデータベーススペシャリストのみで十分なので、絞っていきたいと思います。
どう絞るかは、URLによく注目して規則性を見つけましょう。すると、データベーススペシャリスト試験のものには必ず「"_db_"」という文字が入っていることが分かります。他の試験、例えば基本情報処理技術者試験では、「"_fe_"」という文字が入っていますね。
そこで、「"_db_"」を含む文字列だけを取り出していきます。
dbpdf_list = [temp for temp in pdf_list if '_db_' in temp]
print(dbpdf_list)
実行結果
このように抜き出せました。
['mondai_kaitou_2018h30_1/2018h30h_db_am2_qs.pdf', 'mondai_kaitou_2018h30_1/2018h30h_db_am2_ans.pdf', 'mondai_kaitou_2018h30_1/2018h30h_db_pm1_qs.pdf', 'mondai_kaitou_2018h30_1/2018h30h_db_pm1_ans.pdf',...]
Step6 - 相対URLから絶対URLに変換
コード
先ほど指摘した通り、今の段階では相対URLになっているので、絶対URLに書き換えなければいけません。
urljoinという関数を使うと、元URLと相対URLを使って、絶対URLに書き換えてくれます。
abs_dbpdf_list = []
for relative in dbpdf_list:
temp_url = urljoin(url, relative)
abs_dbpdf_list.append(temp_url)
print(abs_dbpdf_list)
実行結果
前に共通してベースURLがくっつきました。
['https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2018h30_1/2018h30h_db_am2_qs.pdf', 'https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2018h30_1/2018h30h_db_am2_ans.pdf', 'https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2018h30_1/2018h30h_db_pm1_qs.pdf', ...]
Step7 - URLからファイル名を取得
コード
続いて保存するためにファイル名をURLから決めていきたいと思います。まず、split関数を使って、仮のリストにURLを階層ごとに分割していきます。ファイル名は一番最後の要素のはずなので、「len()-1」で場所を指定します。
filename_list = []
for target in abs_dbpdf_list:
temp_list = target.split("/")
filename_list.append(temp_list[len(temp_list)-1])
print(filename_list)
実行結果
ファイル名一覧が得られました。
['2018h30h_db_am2_qs.pdf', '2018h30h_db_am2_ans.pdf', '2018h30h_db_pm1_qs.pdf', '2018h30h_db_pm1_ans.pdf', '2018h30h_db_pm1_cmnt.pdf', '2018h30h_db_pm2_qs.pdf', '2018h30h_db_pm2_ans.pdf', '2018h30h_db_pm2_cmnt.pdf']
Step8 - 保存先のディレクトリを作成
コード
さて、続いて保存先を決めていきたいと思います。保存先のフォルダと、先ほど取得したファイル名をくっ付けて絶対パスに変換します。絶対パスにするには、「os.path.join」を使用します。
target_dir = "C:\\Users\\makky\\Downloads\\DB"
savepath_list = []
for filename in filename_list:
savepath_list.append(os.path.join(target_dir, filename))
print(savepath_list)
実行結果
保存先を絶対パスで作ることができました。
['C:\\Users\\makky\\Downloads\\DB\\2018h30h_db_am2_qs.pdf', 'C:\\Users\\makky\\Downloads\\DB\\2018h30h_db_am2_ans.pdf', 'C:\\Users\\makky\\Downloads\\DB\\2018h30h_db_pm1_qs.pdf',
Step9 - ダウンロード実行
コード
さて最後にダウンロードです。ダウンロードする絶対URLと、保存先の絶対パスを指定して、「urllib.request.urlretrieve」で保存を実行します。
ウェブスクレイピングでダウンロード先のサイトに負荷をかけすぎてしまうと違法になる可能性があるので、1個ダウンロードを実行するごとに2秒間の間隔をあけます。
for (pdflink, savepath) in zip(abs_dbpdf_list, savepath_list):
urllib.request.urlretrieve(pdflink, savepath)
time.sleep(2)
実行結果
はい、このように一括でダウンロードできていますね!
ちょっと一言
複数年度の同時ダウンロード
さて、せっかく平成31年分を一括でダウンロードできたわけですから、どうせなら他の年度のものもダウンロードしたいですよね。
最初にリストを作ってしまって、一気にfor文で実行すればできるでしょう!
やりたいこと
わー、いっぱいあるー。これマニュアルダウンロードはやりたくないです。。。
平成16年から平成31年まで、1年に1回がおよそ8ファイル。合計およそ120ファイル分です。
コード
先ほど貼ったので、今回は一括でコード貼ります。エクセルで元URLのリストを作成してPythonにコピペしていきましょう。
実行経過が分かるように、最後のSave部分でPrintを入れています。
from bs4 import BeautifulSoup
import urllib.request as req
import urllib
import os
import time
from urllib.parse import urljoin
baseurl_list = ["https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2019h31.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2018h30.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2017h29.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2016h28.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2015h27.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2014h26.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2013h25.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2012h24.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2011h23.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2010h22.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2009h21.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2008h20.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2007h19.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2006h18.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2005h17.html",
"https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2004h16.html",
]
for url in baseurl_list:
res = req.urlopen(url)
soup = BeautifulSoup(res, "html.parser")
result = soup.select("a[href]")
link_list =[]
for link in result:
href = link.get("href")
link_list.append(href)
pdf_list = [temp for temp in link_list if temp.endswith('pdf')]
dbpdf_list = [temp for temp in pdf_list if '_db_' in temp]
abs_dbpdf_list = []
for relative in dbpdf_list:
temp_url = urljoin(url, relative)
abs_dbpdf_list.append(temp_url)
filename_list = []
for target in abs_dbpdf_list:
temp_list = target.split("/")
filename_list.append(temp_list[len(temp_list)-1])
target_dir = "C:\\Users\\makky\\Downloads\\DB"
savepath_list = []
for filename in filename_list:
savepath_list.append(os.path.join(target_dir, filename))
for (pdflink, savepath) in zip(abs_dbpdf_list, savepath_list):
print(pdflink)
urllib.request.urlretrieve(pdflink, savepath)
time.sleep(2)
GitHub - makkynm/AutoPDFDownload
実行結果
はい、合計123ファイルが2分ほどでダウンロードされました!
おわりに
あるあるだと思う?のですが、データベーススペシャリスト試験の勉強を始めようとして、Pythonのウェブスクレイピングを書き始めてしまいました。
素直にダウンロード:20分
Pythonのウェブスクレイピングを学ぶ+コード&備忘録書く:4時間
何やってんだって感じですね笑
なお、Pythonはデータベーススペシャリスト試験には出ません。一向に進まない試験勉強ですが応用情報処理試験受かってからの午前Ⅰ試験免除が残っている間に受かりたいところです。
今後もこんなTopicも織り交ぜながら書いていきたいと思いますー
こんな記事ももしよければ、読んでみてください~
note.com
普段はKNIMEというNoCodeツールについて記事を書いてます。
degitalization.hatenablog.jp
参考リンク