🥅

画像スクレイピングしながら画像+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 クラス
    icrawlerImageDownloader をカスタマイズしたクラスです。
    画像を保存するだけでなく、以下の情報を 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