画像スクレイピングしながら画像+URLリンクファイル作成
📌はじめに
仕事で「記者発表はされているけど、公式サイトにはまだ情報が載っていない」他社製品の情報を調べることになりました。
そこで考えたのが、画像を使った調査です。
「画像をスクレイピングして、その画像が掲載されている元のページを調べれば情報がわかるのでは?」と思い、このツールを作ってみました。
📌この記事でできること
- 特定の画像をWeb上から探す
- その画像が掲載されているページURLを自動取得
- 画像の類似検索結果をまとめて収集
情報調査の効率化や新しい発見に役立ちます。
📌環境
python3.x
📌コード解説
#============================================
# 0. ライブラリ検索ワード設定
#============================================
import datetime
import os
import os.path as osp
from icrawler.builtin import BingImageCrawler
from icrawler import ImageDownloader
from selenium.webdriver.remote.remote_connection import LOGGER as selenium_logger
import csv
from openpyxl import Workbook
from openpyxl.drawing.image import Image as ExcelImage
from openpyxl.styles import Alignment
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.hyperlink import Hyperlink
from PIL import Image as PILImage
# 検索ワード
a='ペンギン 親子 かわいい'
b='子猫 へそ天 いやし'
c='かき氷 フルーツ エモイ'
keywords = [a, b, c]
# 取得する画像枚数
kazu=5
#============================================
# 1. 現在時刻を取得、保存
#============================================
# JSTタイムゾーンで現在時刻を取得
t_delta = datetime.timedelta(hours=9)
JST=datetime.timezone(t_delta, 'JST')
now = datetime.datetime.now(JST)
day_foi = format(now, '%Y%m%d9%H%M%S')
# アプトプットフォルダ
foi = './image_' + str(day_foi)
os.makedirs(foi, exist_ok=True)
# URLリストのファイル名
save_name = 'リスト.csv'
# URLリストに画像を張り付けたエクセルファイル名
save_Exname = 'output.xlsx'
0. ライブラリ検索ワード設定
-
検索ワードの設定
調べたいキーワードをリスト化します。
例として「ペンギン 親子 かわいい」「子猫 へそ天 いやし」「かき氷 フルーツ エモイ」を設定しています。 -
取得枚数の設定
取得する画像の枚数を指定します。
多すぎると同じ画像が混ざることがあるため、必要に応じて調整します。
💡 ポイント:
キーワードや枚数は、調査目的に合わせて柔軟に変更できる設計にしておくと便利です。
1. 現在時刻の取得と保存先ディレクトリ作成
-
現在時刻の取得(JST)
処理開始時刻を取得し、ファイル名やフォルダ名に活用できます。 -
保存ディレクトリの作成
ディレクトリ名に処理開始時刻を含めることで、同じ日付・時間に複数回実行しても上書きせずに保存可能です。
取得した画像ファイルやURLリストは、このディレクトリ内に保存されます。
💡ポイント
ディレクトリ名やファイル名にタイムスタンプを含めておくと、後からデータを整理・参照する際に便利です。
#============================================
# 3. カスタムダウンローダーの定義と画像取得処理
#============================================
class URLDownloader(ImageDownloader):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
import logging
self.logger.setLevel(logging.CRITICAL)
def save_column(self, folname, filepath, file_url, output_csv_path=None):
if output_csv_path is None:
output_csv_path = os.path.join(foi, save_name)
with open(output_csv_path, 'a') as f:
output_str = f'{folname}, {filepath}, {file_url}\n'
f.write(output_str)
def download(self, task, default_ext, timeout=5, max_retry=3, overwrite=False, **kwargs):
file_url = task['file_url']
task['success'] = False
task['filename'] = None
retry = max_retry
while retry > 0 and not self.signal.get('reach_max_num'):
try:
if not overwrite:
with self.lock:
self.fetched_num += 1
filename = self.get_filename(task, default_ext)
if self.storage.exists(filename):
self.logger.info('skip downloading file %s', filename)
return
self.fetched_num -= 1
response = self.session.get(file_url, timeout=timeout)
if self.reach_max_num():
self.signal['reach_max_num'] = True
break
if response.status_code != 200:
self.logger.error('Response status code %d, file %s',
response.status_code, file_url)
break
if not self.keep_file(task, response, **kwargs):
break
with self.lock:
self.fetched_num += 1
filename = self.get_filename(task, default_ext)
self.logger.info('image #%s\t%s', self.fetched_num, file_url)
self.storage.write(filename, response.content)
task['success'] = True
task['filename'] = filename
#folname = task.get('folname', 'unknown')
self.save_column(folname, filename, file_url)
break
except Exception as e:
self.logger.error('Exception caught when downloading file %s, error: %s, remaining retry times: %d',
file_url, e, retry - 1)
finally:
retry -= 1
def get_filename(self, task, default_ext):
filename = f"{self.fetched_num}.{default_ext}"
return filename
crawler = BingImageCrawler(storage ={'root_dir' : foi})
#============================================
# 4. 出力フォルダ名指定
#============================================
for keyword in keywords:
if keyword == a:
moji = '1_'
elif keyword == b:
moji = '2_'
elif keyword == c:
moji = '3_'
# 検索ワードの頭に連番。
folname = moji + keyword
crawler = BingImageCrawler(downloader_cls=URLDownloader, storage={'root_dir': foi + '/'+ folname})
crawler.crawl(keyword = keyword, max_num = kazu)
3. カスタムダウンローダーの定義と画像取得処理
-
URLDownloader クラス
icrawlerのImageDownloaderをカスタマイズしたクラスです。
画像を保存するだけでなく、以下の情報を CSV に記録します:- 画像の保存先フォルダ名
- 保存したファイル名
- 画像の元 URL
💡ポイント
どの画像がどの URL から取得されたかを後から簡単に確認できるので、調査の透明性と再現性が向上します。
-
BingImageCrawler の利用
検索キーワードを順番に検索し、各キーワードごとに専用フォルダに画像を保存します。
フォルダ名には番号を付けて整理することで、後から見てもどのキーワードに対応する画像か分かりやすくなります。
4. 出力フォルダ名指定
- 検索するキーワードごとに保存フォルダ名を指定
- 頭に番号を付けることで整理しやすくなる
- 各フォルダに対して
BingImageCrawlerを実行し、指定枚数の画像を取得 - これにより、検索ワードごとに整理された画像フォルダと対応する CSV が生成されます
💡ポイント
フォルダ名に番号やキーワードを含めると、後で画像と検索条件の対応関係を簡単に追跡できます。
#============================================
# 4. CSVファイルを読み込みながら画像をExcelに貼り付け
#============================================
# 画像保存トップフォルダ
base_dir = foi # ダウンロード時のフォルダ変数と同じにする
# CSVファイルパス
csv_path = os.path.join(foi, 'リスト.csv')
wb = Workbook()
ws = wb.active
# セルの高さ・幅設定
row_height = 84
ws.column_dimensions['A'].width = 30
ws.column_dimensions['B'].width = 24
ws.column_dimensions['C'].width = 11
ws.column_dimensions['D'].width = 50
# 画像リサイズの高さ
resize_height = row_height
for i, row in enumerate(csv.reader(open(csv_path, newline='', encoding='utf-8-sig')), start=1):
folname = row[0].strip()
img_filename = row[1].strip()
img_url = row[2].strip()
img_path = os.path.join(base_dir, folname, img_filename)
if not os.path.exists(img_path):
print(f"画像ファイルが見つかりません: {img_path}")
continue
# 行の高さ設定(画像サイズに合わせて)
ws.row_dimensions[i].height = resize_height * 0.75 # Excelの高さはポイントなので調整
# 画像をPillowで開いてリサイズ(縦resize_heightピクセルに)
pil_img = PILImage.open(img_path)
w, h = pil_img.size
new_h = resize_height
new_w = int(w * (new_h / h))
pil_img = pil_img.resize((new_w, new_h))
# 一時ファイルに保存
tmp_path = os.path.join(base_dir, f'tmp_resized_{i}.png')
pil_img.save(tmp_path)
# Excelに画像挿入
img = ExcelImage(tmp_path)
img.anchor = f'A{i}'
ws.add_image(img)
# B〜D列にテキスト挿入
ws[f'B{i}'] = folname
ws[f'C{i}'] = img_filename
# URLはハイパーリンク付きテキストで中央揃えに
cell = ws[f'D{i}']
cell.value = img_url
cell.hyperlink = img_url
cell.style = "Hyperlink"
cell.alignment = Alignment(horizontal='center', vertical='center')
# B, C列も中央揃え
for col_letter in ['B', 'C']:
ws[f'{col_letter}{i}'].alignment = Alignment(horizontal='center', vertical='center')
# 保存して一時ファイルは任意で削除
output_xlsx_path = os.path.join(foi, save_Exname)
wb.save(output_xlsx_path)
print(f"Excelファイルを保存しました: {output_xlsx_path}")
# 一時画像削除(必要なら)
for i in range(1, ws.max_row + 1):
tmp_file = os.path.join(base_dir, f'tmp_resized_{i}.png')
if os.path.exists(tmp_file):
os.remove(tmp_file)
os.startfile(os.path.realpath(foi))
os.startfile(os.path.join(os.path.realpath(foi), save_Exname))
4. CSVファイルを読み込みながら画像をExcelに貼り付け
-
CSV読み込み
事前に作成した CSV(取得した画像のフォルダ名・ファイル名・元URL)を読み込みます。
これにより、どの画像がどのURLから取得されたかを簡単に参照可能です。 -
Excelファイル作成
- 画像を挿入するための行高さや列幅を設定
- 画像は Pillow でリサイズして Excel に貼り付け
- B〜D列にはフォルダ名・ファイル名・URLを挿入
- URLはハイパーリンク付きにして、クリックすると元ページに飛べるようにする
-
整理と可視化のメリット
- 画像と関連情報を Excel で一元管理できる
- 画像とURLをセットで確認できるため、情報調査の効率化に最適
- 一時画像は自動で削除でき、Excelファイルだけ残して管理可能
💡ポイント
Excel 上で画像とURLを同時に確認できるので、
あとから調査結果をレビューしたり共有したりする際に便利です。
出力結果

📌まとめ
今回紹介した方法は、特定の画像から元ページを探すスクレイピング手法ですが、
別の方法で情報を取得できたため、使用しませんでした。
💡 ポイント
-
利用規約を守る
スクレイピングを行うときは、対象サイトのルールを確認し、サーバーに負担をかけすぎないようにしましょう。 -
個人情報や著作権に配慮
収集する情報が個人情報や著作権に触れないか、軽く確認しておくと安心です。 -
違法や迷惑行為を避ける
法律やサイトの規約に反しない範囲で、安全に行うことが大切です。
参考リンク・素材について
GitHubリポジトリ
本記事で紹介したコードやサンプルデータはこちらのリポジトリで公開しています。
https://github.com/iwakazusuwa/crawler-with-url-log-mit-license
画像素材
掲載している画像素材は「いらすとや」さんのものを加工して使っています。
Discussion