前回の記事の続きになります。
-
【日向坂46】ブログから画像を全自動ダウンロードしてみた①【Python】ウェブスクレイピング練習
Pythonの練習も兼ねて何か作ろうと思い立ち、日向坂46の公式ブログをウェブスクレイピングしてみました。 はるいち本当は最近、個人的におすし(金村 美玖)が来ているだけ ...
Pythonを使ったウェブスクレイピングの実践的な内容です。
日向坂46の公式ブログからブログ内の画像を全自動ダウンロードすることが目標です。
この記事を読んでもらいたい人
- 日向坂46(特におすし推し)が好きな人
- 画像のダウンロードを手作業でしたくない人
- Pythonを勉強した(したい)けど活用の仕方が分からない人
Pythonの環境構築(準備)や基礎については以下の記事を参照してください
-
Pythonの始め方~準備から基礎~
私は、Pythonを習ったことがありません。 いわゆる、独学ってやつです。 独学でも これぐらいのものは作れるようにはなります。 私の場合のPython勉強方 ...
おことわり
- Python歴が浅い&独学のためお見苦しいところはあると思います
- こっちの書き方がいいと指摘があれば、コメントかTwitterで教えてくださると助かります
長くなるのでいくつかの記事に分けたいと思います。
最後の記事にはまとめたコードを載せるのでそちらをコピーしてもらえば利用できます。
①では、メンバーごとの情報を集めてリストにしていきました。
②では、ブログから画像をダウンロードしていきます。
目次
注意点
ウェブスクレイピングは強力な手法ですが、いくつか注意点があるので確認しておきましょう。
これは①と同じなので、既に読んでいる人は本編へどうぞ。
攻撃とみなされる
コンピューターが高速でリクエスト(ページを表示したり、画像をダウンロードしたり)するので、それこそ人ではあり得ないスピードで行うので、相手のサーバーには負荷がかかります。
遅延やサーバーダウンが起こったりします。
一時停止をはさみながらリクエストの頻度を下げる必要が有ります。
利用規約違反
Webサイトによっては、明確にスクレイピングを禁止しているものも存在します。
事前に確認しましょう。
日向坂の公式サイト内には見つからなかったので、節度有るリクエスト頻度なら大丈夫(なはず)。
著作権法違反
Webスクレイピングの対象となる情報に著作権があると、活用方法を誤ってしまうと著作権法違反となるので注意が必要です。
個人的に保存する分にはOKだけど、「自分の物ですよ」って使うのはOUTぐらいの感覚。
私は全画像ダウンロードしたけどこのサイトで公開するとBANですね。
(各自のパソコンでやってね)
スポンサーリンク
ライブラリ
今回、使用する主なライブラリを先に紹介します。
インストールしていないライブラリがあったら各自「pip install」
ここでは、「へーそんなのあるだ~」程度の理解で大丈夫です。
Requests
サイトの HTMLソースを取得します。
ブラウザ上に文字を表示したりするコード。
ブラウザを開いた状態で「F12」を押すと確認できます。
Beautiful Soup 4
Requestsで取得したHTMLコードを解析します。
全体から必要な情報だけ探す時に使います。
pykakasi
ひらがなからローマ字に変換するために使います。
ファイル名をローマ字で付けたいから。
os
フォルダを作ったり、そのフォルダに保存したりするときに使います。
time
高速リクエストをすると怒られるので、1秒待つみたいな指示を出します。
スポンサーリンク
ブログから画像をダウンロードする
メンバー個別のブログにいって画像をダウンロードしていきます。
今回はおすし(金村 美玖)のブログの1ページ目を例に進めていきます。
サイトのHTMLを確認
とりあえずHTMLを確認してみますか。
https://www.hinatazaka46.com/s/official/diary/member/list?ima=0000&page=0&cd=member&ct=12
<div class="p-blog-article">
が1つ1つの記事になっているらしい。
①と同じ手順で記事のHTMLを取得します。
url = 'https://www.hinatazaka46.com/s/official/diary/member/list?ima=0000&page=0&cd=member&ct=12' responce = requests.get(url) responce.raise_for_status() bs4_blog = bs4.BeautifulSoup(responce.text, 'html.parser') article = bs4_blog.select('.p-blog-article')
基本的には同じ流れなので説明は割愛します。
articleに1記事ごとのHTMLを格納できたので、必要な情報を探していきましょう。
日付を取得する
画像ファイル名を「メンバー名_日付_番号」みたいにつけたいので、まずは日付の情報を探しましょう。
<div class="c-blog-article__date">
が日付情報みたいですね。
for j in range(len(article)): date = article[j].find('div', {'class': 'c-blog-article__date'}).get_text().strip()
実行結果はこんな感じ
2020.12.31 18:21 2020.12.31 18:21 #省略 2020.8.23 18:08
流石に投稿時間は要らないので取りましょう
date = date.split()
実行結果
['2020.12.31', '18:21'] ['2020.12.31', '18:21'] #省略 ['2020.8.23', '18:08']
split()でスペースで分ける(スプリット)
0番目が日付、1番目が投稿時間になってますね。
つまり、0番目だけをつかえばいいので
date = date.split()[0]
「年.月.日」となっているので、今度は「.」で分ければいいですね。
year = date.split('.')[0] month = date.split('.')[1] day = date.split('.')[2]
実行結果
2020 12 31 2020 12 31 #省略 2020 8 23
オッケーですね。
ファイル名を作りましょう。
「メンバー名_日付_」まで作ります。
おすしのローマ字名は9番目に格納されています。
label = name_rome[9] + '_' + year + '_' + month.zfill(2) + '_' + day.zfill(2) + '_'
zfill(数字)で足りない桁を「0」で埋める。
今回は二桁です。
実行結果
miku.kanemura_2020_12_31_ miku.kanemura_2020_12_31_ #省略 miku.kanemura_2020_08_23_
良い感じ
一連の流れをまとめると
for j in range(len(article)): date = article[j].find('div', {'class': 'c-blog-article__date'}).get_text().strip() date = date.split()[0] year = date.split('.')[0] month = date.split('.')[1] day = date.split('.')[2] label = name_rome[9] + '_' + year + '_' + month.zfill(2) + '_' + day.zfill(2) + '_'
スポンサーリンク
画像をダウンロードする
埋め込まれている画像のHTMLを確認すると
&lt;img src=""&gt;
ここが画像の保存先のようなので、まずimgタグを取得しましょう。
とりあえず、0番目の記事で進めていきます。
img = article[0].select('img')
imgの中からsrcを取りましょう
for i in range(len(img)): src = img[i].get('src') print(src)
実行結果
https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/202012/mobhyLvkr.jpg https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/202012/mobPQ6y6C.jpg https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/202012/mobXoYItQ.jpg https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/202012/mobN6kxq5.jpg https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/202012/mobtmvzum.jpg https://cdn.hinatazaka46.com/files/14/diary/official/member/moblog/202012/mobnaP0PF.jpg
ファイル名に番号(No)を振っていきますが、同じ日に2つ記事が投稿されることを加味する必要があります。
last_dayに前の画像の日を記録し、違うならNoを0にリセットしていきます。
if day == last_day: pass else: No = 0 last_day = day for i in range(len(img)): file_name = label + str(No).zfill(2) No = No + 1
実行結果
miku.kanemura_2020_12_31_00 miku.kanemura_2020_12_31_01 miku.kanemura_2020_12_31_02 miku.kanemura_2020_12_31_03 miku.kanemura_2020_12_31_04 miku.kanemura_2020_12_31_05
ファイル名には拡張子が必要なので付けていきます。
srcに.jpgや.pngがあるのでそれを参考に付けていきます。
(jpgやpngは画像ファイルですよって意味の拡張子)
fg = True if '.jpg' in src or '.jpeg' in src: file_name = file_name + '.jpeg' elif '.png' in src: file_name = file_name + '.png' else: print(src) fg = False
inは指定した文字列を含んでいるか
実行結果
miku.kanemura_2020_12_31_00.jpeg miku.kanemura_2020_12_31_01.jpeg miku.kanemura_2020_12_31_02.jpeg miku.kanemura_2020_12_31_03.jpeg miku.kanemura_2020_12_31_04.jpeg miku.kanemura_2020_12_31_05.jpeg
拡張子が付いていますね。
拡張子がjpgやpngじゃなかった時のためにチェックできるようにしています。
保存先のpathを指定します。
path = os.path.join('data', name_kanji[9], file_name)
実行結果
data\金村 美玖\miku.kanemura_2020_12_31_00.jpeg data\金村 美玖\miku.kanemura_2020_12_31_01.jpeg data\金村 美玖\miku.kanemura_2020_12_31_02.jpeg data\金村 美玖\miku.kanemura_2020_12_31_03.jpeg data\金村 美玖\miku.kanemura_2020_12_31_04.jpeg data\金村 美玖\miku.kanemura_2020_12_31_05.jpeg
srcを元にリクエストを送り画像を保存していきます。
import time if not os.path.isfile(path) and fg: responce = requests.get(src) time.sleep(1) if responce.status_code == 200: print(file_name) with open(path, "wb") as f: f.write(responce.content) else: print('False', responce.status_code, src)
3行目でまだダウンロードしていないか確認
4行目でリクエストを送る
5行目で1秒待つ(サーバーに負担をかけないように)
7行目でステータスコードを確認、200だと正常に接続された
9~10行目で画像の保存
11~12行目で接続できなかったsrcを表示、原因の解明に活かすため
保存できてる
スポンサーリンク
コードをまとめる
この記事のコードをまとめると
import time url = 'https://www.hinatazaka46.com/s/official/diary/member/list?ima=0000&page=0&cd=member&ct=12' responce = requests.get(url) responce.raise_for_status() bs4_blog = bs4.BeautifulSoup(responce.text, 'html.parser') article = bs4_blog.select('.p-blog-article') last_day = 0 No = 0 for j in range(len(article)): date = article[j].find('div', {'class': 'c-blog-article__date'}).get_text().strip() date = date.split()[0] year = date.split('.')[0] month = date.split('.')[1] day = date.split('.')[2] label = name_rome[9] + '_' + year + '_' + month.zfill(2) + '_' + day.zfill(2) + '_' img = article[j].select('img') if day == last_day: pass else: No = 0 last_day = day for i in range(len(img)): src = img[i].get('src') file_name = label + str(No).zfill(2) No = No + 1 fg = True if '.jpg' in src or '.jpeg' in src: file_name = file_name + '.jpeg' elif '.png' in src: file_name = file_name + '.png' else: print(src) fg = False path = os.path.join('data', name_kanji[9], file_name) if not os.path.isfile(path) and fg: responce = requests.get(src) time.sleep(1) if responce.status_code == 200: print(file_name) with open(path, "wb") as f: f.write(responce.content) else: print('False', responce.status_code, src)
13~14行目でlast_dayとNoに初期値を入れています。
まとめ
本格的にウェブスクレイピングみたいになってきましたね。
次の記事ではループの構造を作っていきます。
2ページ目、3ページ目と遡っていったり、全メンバーのブログを巡回していきます。
個人的に、ループとif文はプログラミングの基本だと思うのでいい復習になると思います。
-
【日向坂46】ブログから画像を全自動ダウンロードしてみた③【Python】ウェブスクレイピング練習
前回の記事の続きになります。 Pythonを使ったウェブスクレイピングの実践的な内容です。 日向坂46の公式ブログからブログ内の画像を全自動ダウンロードすることが目標です。 ...