以前の記事のコードを一部書き直しました。
-
【日向坂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秒待つみたいな指示を出します。
スポンサーリンク
辞書型で書く
以前の記事では、番号、漢字名、ローマ字名を別々のリストに格納していました。
member_No = [] name_kanji = [] name_rome = [] for i in range(len(all_member)): member_No.append(all_member[i]['data-member']) name_kanji.append(all_member[i].find('div', {'class': 'c-member__name'}).get_text().strip()) path = os.path.join('data', name_kanji[i]) if not os.path.isdir(path): os.mkdir(path) name_kana = all_member[i].find('div', {'class': 'c-member__kana'}).get_text().strip() last = name_kana.split()[0] first = name_kana.split()[1] first = conv.do(first) last = conv.do(last) name_rome.append(first + '.' + last)
これだと見づらかったので辞書型で記述しました。
member = [] for i in range(len(all_member)): number = all_member[i]['data-member'] kanji = all_member[i].find('div', {'class': 'c-member__name'}).get_text().strip() path = os.path.join('data', kanji) if not os.path.isdir(path): os.mkdir(path) name_kana = all_member[i].find('div', {'class': 'c-member__kana'}).get_text().strip() last = name_kana.split()[0] first = name_kana.split()[1] first = conv.do(first) last = conv.do(last) rome = first + '.' + last a = {'number': number, 'kanji': kanji, 'rome': rome} member.append(a)
実行結果
{'number': '2', 'kanji': '潮 紗理菜', 'rome': 'sarina.ushio'} {'number': '4', 'kanji': '影山 優佳', 'rome': 'yuuka.kageyama'} {'number': '5', 'kanji': '加藤 史帆', 'rome': 'shiho.katou'} #省略 {'number': '21', 'kanji': '上村 ひなの', 'rome': 'hinano.kamimura'} {'number': '23', 'kanji': '森本 茉莉', 'rome': 'marii.morimoto'} {'number': '24', 'kanji': '山口 陽世', 'rome': 'haruyo.yamaguchi'}
各メンバーの要素を辞書型で記述できました。
スポンサーリンク
完成形
ここまで長く付き合って頂きありがとうございました。
辞書型に合わせて書き直しました。
おまけでループの書き方も直しました。
import requests import bs4 import os from pykakasi import kakasi import time #ここから① kakasi = kakasi() kakasi.setMode('H', 'a') conv = kakasi.getConverter() search_url = 'https://www.hinatazaka46.com/s/official/search/artist?ima=0000' responce = requests.get(search_url) responce.raise_for_status() bs4_blog = bs4.BeautifulSoup(responce.text, 'html.parser') all_member = bs4_blog.select('.sort-default li') member = [] if not os.path.isdir('data'): os.mkdir('data') for i in range(len(all_member)): number = all_member[i]['data-member'] kanji = all_member[i].find('div', {'class': 'c-member__name'}).get_text().strip() path = os.path.join('data', kanji) if not os.path.isdir(path): os.mkdir(path) name_kana = all_member[i].find('div', {'class': 'c-member__kana'}).get_text().strip() last = name_kana.split()[0] first = name_kana.split()[1] first = conv.do(first) last = conv.do(last) rome = first + '.' + last a = {'number': number, 'kanji': kanji, 'rome': rome} member.append(a) #①はここまで for m in member: now_page = 0 next_exist = True while next_exist: # ここから② url = 'https://www.hinatazaka46.com/s/official/diary/member/list?ima=0000&page=' + str(now_page) + '&cd=member&ct=' + str(m['number']) 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 a in article: date = a.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 = m['rome'] + '_' + year + '_' + month.zfill(2) + '_' + day.zfill(2) + '_' img = a.select('img') if day == last_day: pass else: No = 0 last_day = day for i in img: src = 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', m['kanji'], 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) # ②はここまで next_page = bs4_blog.select('svg') link = 0 for i in range(len(next_page)): next_page[i] = next_page[i].find_parent() next_page[i] = next_page[i].find_parent() if next_page[i].get('href') != None: link = link + 1 if now_page == 0: now_page = now_page + 1 elif now_page >= 1 and link >= 2: now_page = now_page + 1 else: next_exist = False print(m['kanji'], now_page, 'end')
まとめ
辞書型の記述が便利だなって最近気がついたので、使っていきたいですね。