Python と Selenium を使って、ギャラリーサイトなどから画像を一括をダウンロードしてみましょう。
スポンサーリンク
メインの画像のみダウンロードしたい
一般的にスクレイピングで画像を一括ダウンロードする場合は、HTML 中の <img>
タグから src
属性を取り出し、その URL の画像を一括でダウンロードする方法が考えられれる。
しかし、基本的に Web ページ上には、メインの画像以外にも宣伝や、その他の画像が掲載されていることが多く、すべての画像をダウンロードしてしまうと、関係のない画像までダウンロードしてしまうことになる。
そこで、極力メイン画像のみをスクレイピングするように、この記事で紹介するサンプルコードは、次のような事を考える。
メインの画像のみダウンロードする方式
通常、ギャラリーサイトに掲載されている画像は、次のイメージのように画像サイズが類似していることが多い。
そこで、今回紹介するサンプルコードは、極力メイン画像のみをスクレイピングするように、画像サイズが誤差 30px で同じ画像の数をカウントして、もっとも数が多いサイズの画像をダウンロードするようにしている。
サンプルコード
それでは、上のことを考慮したサンプルコードを紹介する。少し長いサンプルコードになるが、途中のコメントを見ていけば問題なく理解できるであろう。
from selenium import webdriver
import requests
import os
import datetime
#ダウンロードする画像の最低サイズ
MIN_WIDTH = 200
MIN_HEIGHT = 300
#画像の一括ダウンロードを行うサイト
url = "https://xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# 画像のサイズと画像のurlを管理するクラス
class ImageSet:
def __init__(self, w, h):
self.w = w
self.h = h
self._url_list = []
def is_within_range(self, w, h):
#print("w=%d h=%d rw=%d %d " % (w, h, self.range_w.start, self.range_w.stop))
return self.range_w.start <= w <= self.range_w.stop \
and self.range_h.start <= h <= self.range_h.stop
@property
def range_w(self):
return range(self.w - 30, self.w + 30)
@property
def range_h(self):
return range(self.h - 30, self.h + 30)
def add_url(self, url):
self._url_list.append(url)
@property
def url_list(self):
return self._url_list
def display(self):
#print("w=%d h=%d count=%d" % (self.w, self.h, len(self.url_list)))
for n in self.url_list:
print(n)
img_set_list = []
# Chrome の Web ドライバーを読み込み(パスを環境に合わせて変更すること)
driver = webdriver.Chrome("/path/to/chromedriver")
driver.get(url)
driver.implicitly_wait(10)
#imgタグのリストを取得
elements = driver.find_elements_by_tag_name("img")
img_items = [
{ 'w': e.size["width"],
'h': e.size["height"],
'url': e.get_attribute("src")
}
for e in elements if e.size["width"] >= MIN_WIDTH and e.size["height"] >= MIN_HEIGHT
]
#画像はカレントディレクトリの「img/yyyymmddhhmmss」に格納するためフォルダを作成しておく
if not os.path.isdir("img"):
os.makedirs("img")
now = datetime.datetime.now()
path = "{}/{}".format("img", now.strftime("%Y%m%d%H%M%S"))
if not os.path.isdir(path):
os.makedirs(path)
#画像サイズごとにURLのリストを作成
for i,el in enumerate(img_items, start=1):
#表示上の幅と高さを取得
w, h = el["w"], el["h"]
print("w=%d h=%d" % (w, h))
#似たサイズの画像リスト検索
find_list = list(filter(lambda x: x.is_within_range(w, h), img_set_list))
if len(find_list) == 0:
#似たサイズがなければ画像リスト作成
img_set = ImageSet(w, h)
img_set.add_url(el["url"])
img_set_list.append(img_set)
else:
#似たサイズのリストに追加
for x in find_list:
x.add_url(el["url"])
max_img_set = max(img_set_list, key=lambda x: len(x.url_list))
if max_img_set is not None:
#max_img_set.display()
for i, url in enumerate(max_img_set.url_list, 1):
#ファイルのダウンロード
print("download start %s" % url)
responce = requests.get(url)
content_type = responce.headers['content-type']
#print("download end content-type=%s" % content_type)
#URLまたは content-type ヘッダからファイルの拡張子を設定
_, ext = os.path.splitext(url)
ext = ext if len(ext) > 0 else ".jpg"
for k, v in {"image/jpeg" : ".jpg", "image/png" : ".png", "image/gif" : ".gif"}.items():
if k in content_type:
ext = v
#ファイルの保存
with open(path + "/" + "{:04}{}".format(i, ext), "wb") as f:
f.write(responce.content)
#webdriverを閉じる
driver.quit()
注意
画像を一括でダウンロードする方法を解説してきました。インターネット上の画像は著作権で保護されてい可能性もあるため、ダウンロードした画像を二次利用する時は十分に気をつけましょう。
また、Cloudflare などのスクレイピング対策された Web サイトの場合は、requests
モジュールで画像をダウンロードすると、bot 扱いとなりダウンロードができません。cloudscraper
を使えばそれも回避できるようだが、今回はここまで。
0 件のコメント:
コメントを投稿