【実務】広報で使う画像が、自社サイトのどのページに使われているかを自動抽出

タイトルの通りですが、広報で使っている画像が

  • 自社サイトのどこで使われているか(ページのURL)

をExcelで管理しようとなりました。
サイトを一つ一つ見て、どの画像を使っているかなんて記録するなんてめんどくさすぎるので、webスクレイピングで解決しようと思いました。

仕組みの概要ですが、以下の通りです。

  • (事前準備)自社サイトで掲載している”画像の”名前(aaaa.png とかそういう感じ)
  • (事前準備)その画像に誰が載っている
  1. 調べたいサイトの一番上流のページを指定(例えばhttps://webscraper.io/test-sites/e-commerce/allinone だったら、https://webscraper.io)
  2. そのページから辿る事が出来る、そのサイトの内部のページを全て検索し、シートのA列にある画像がページに含まれているか確認(https://webscraper.io 以外のサイトは見ない※Twitterとか)
  3. 含まれていたページのURLをC列に、”, “区切りで出力

この事前準備のところにある「誰が載っているか」の部分も、表記ゆれや入力の手間を考えて、VBAを使って名前を入力してもらおうと考えました。なので、本ページは以下の構成です。

  1. Excel VBAで「名前一覧」から複数選択できるユーザーフォームを作り
  2. PythonでWebスクレイピングを行い、結果を同じExcelに自動書き戻す

1. Excel VBAで「名前一覧」から複数選択できるユーザーフォームを作る

前提・準備

  1. Windows版Excel(マクロ有効ブック:.xlsm
  2. リボンに「開発」タブが見えていること(なければファイル→オプション→リボンのユーザー設定で有効化)
  3. VBAエディタを開く(Alt+F11

この辺の設定の詳細はこちら

フォルダ構成イメージ

ステップ1:標準モジュールのModule1に「ShowNamePicker」を作成

まず、VBAエディタで挿入 → 標準モジュールを選び、Module1に以下を貼り付けます。

' Module1.bas
Option Explicit

' ダブルクリックされたセルをフォームに渡すグローバル変数
Public TargetCell As Range

' シートのBeforeDoubleClickイベントから呼び出すサブルーチン
Sub ShowNamePicker()
    Dim uf As UserForm1
    ' フォームのインスタンスを作成
    Set uf = New UserForm1
    ' 操作対象のセル(ActiveCell)をフォームに渡す
    Set uf.TargetCell = ActiveCell
    ' フォームを表示(Initialize → Activate が自動実行)
    uf.Show
End Sub

ステップ2:UserForm1を作成し、コントロールを配置

  1. VBAエディタの挿入 → UserFormでフォームを追加
  2. プロパティウィンドウでフォーム名をUserForm1に設定
  3. Toolboxから以下をドラッグ&ドロップし、プロパティを設定。(左下のところにプロパティを編集できるところがあります)
コントロールNameCaption主な設定
ListBoxlstNames(空欄)MultiSelect =
fmMultiSelectMulti
LabellblPreview(空欄)(そのまま)
CommandButtonbtnOKOK(そのまま)
CommandButtonbtnCancelキャンセル(そのまま)

各ブロックのサイズは、見やすいように大きくしちゃいました

UserForm1 のコードを貼り付け

左メニューにあるUserForm1を右クリック→「コードの表示」をして、以下を丸ごと貼り付けてください。

' UserForm1
Option Explicit

' Module1 から渡された「編集対象セル」
Public TargetCell As Range

' 初回ロード:tableシートの T_name から名前を読み込む
Private Sub UserForm_Initialize()
    Dim ws  As Worksheet
    Dim tbl As ListObject
    Dim cel As Range

    ' 名前一覧を置いた「table」シートを指定
    Set ws  = ThisWorkbook.Worksheets("table")
    Set tbl = ws.ListObjects("T_name")

    ' ListBox をクリアしてから項目を追加
    Me.lstNames.Clear
    For Each cel In tbl.DataBodyRange.Columns(1).Cells
        Me.lstNames.AddItem cel.Value
    Next cel
End Sub

' フォーム表示時:既存セル値をプレビュー&選択状態に反映
Private Sub UserForm_Activate()
    Dim existing As String
    Dim arr      As Variant
    Dim v        As Variant
    Dim i        As Long

    If Not TargetCell Is Nothing Then
        existing = CStr(TargetCell.Value)
        Me.lblPreview.Caption = existing

        If existing <> "" Then
            arr = Split(existing, ",")
            For Each v In arr
                v = Trim(v)
                For i = 0 To Me.lstNames.ListCount - 1
                    If Me.lstNames.List(i) = v Then
                        Me.lstNames.Selected(i) = True
                        Exit For
                    End If
                Next i
            Next v
        End If
    End If
End Sub

' ListBoxが変わるたびプレビューを更新
Private Sub lstNames_Change()
    Dim sel As String
    Dim i   As Long
    sel = ""
    For i = 0 To Me.lstNames.ListCount - 1
        If Me.lstNames.Selected(i) Then
            If sel = "" Then sel = Me.lstNames.List(i) _
              Else sel = sel & ", " & Me.lstNames.List(i)
        End If
    Next i
    Me.lblPreview.Caption = sel
End Sub

' OKボタン:プレビュー内容をセルに書き戻す
Private Sub btnOK_Click()
    If Not TargetCell Is Nothing Then
        TargetCell.Value = Me.lblPreview.Caption
    End If
    Unload Me
End Sub

' キャンセル:何もせず閉じる
Private Sub btnCancel_Click()
    Unload Me
End Sub

ステップ3:scrapingシートにダブルクリックイベントを設定

最後に、左メニューからscrapingシートをダブルクリックし、右のエディター上に下記を貼り付けます。

Option Explicit

Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
    ' B列(列番号2)限定、1セルだけを対象
    If Target.Column = 2 And Target.Cells.Count = 1 Then
        Cancel = True         ' 通常編集をキャンセル
        ShowNamePicker        ' Module1 のフォーム表示サブルーチン呼び出し
    End If
End Sub

動作イメージ

  1. scrapingシートの任意のB列セルをダブルクリック
  2. UserForm1が開き、左側に
    tableシートの名前一覧(T_name)が縦並びで表示
  3. 複数選択すると、下のプレビュー領域(lblPreview)に即時反映
  4. 「OK」を押すと、B列セルに「山本, 田中, 佐藤」のように入力

以上の設定で、ExcelのVBAに不慣れな方でもドラッグ&ドロップで簡単に

  • 名前の表記ゆれを防ぎ
  • セルへの直接入力の手間を減らし
  • リアルタイムプレビューで入力ミスを防ぐ

――という便利なユーザーフォームを実現で来たと思っています!!
次から、画像がどこで使われているか検索するプログラムを書きます。


ステップ4:PythonでWebスクレイピング&Excel自動更新

続いて、Pythonを使ってWebサイトをクロールし、指定画像が使われているページURLをExcelに書き戻す方法をご紹介します。初心者向けに手順とコードを解説します。

前提

  • Pythonがインストール済み(Windowsならpython --versionで確認)
    pythonインストール方法はこちら
  • Excelファイル(.xlsmでもOK)をスクリプトと同じフォルダに置く(上記のVBAを実装したなら.xlsmになっているはずです)
  • 必要なライブラリはターミナルでインストール:
pip install requests beautifulsoup4 pandas openpyxl tqdm openpyxl

スクリプトの動作イメージ

  1. Excelのtest.xlsmから画像パスリストを読み込む(名前は皆さんの好きに変えてね!)
  2. https://webscraper.io/以下をBFSクロール(←このリンクはご自身の調べたいところのTOPページを指定してください~)
  3. 各ページのHTMLに画像パスが含まれていればURLを記録
  4. マクロ情報を保持したままExcelのC列に書き戻す

Pythonスクリプト全文

スクリプトを実行するときは、Excel閉じてください!

#!/usr/bin/env python3
# coding: utf-8
"""
Excel (.xlsm) の scraping シートから画像パスを読み込み、
指定サイトをクロールして「どのページで使われているか」を調べ、
C列に自動書き戻すスクリプト
"""

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
from collections import deque
import pandas as pd
from tqdm import tqdm
from openpyxl import load_workbook

# ── 設定セクション ──
INPUT_PATH  = r"C:\Users\xxxx\Documents\test.xlsm"                     # そのExcelの絶対パスを指定
SHEET_NAME  = "scraping"                          # 読み書きするシート名
START_URL    = "https://webscraper.io/"           # 調べたいサイトのURL

# ドメイン&パスを分割して定数化
PARSED_URL   = urlparse(START_URL)
BASE_DOMAIN  = PARSED_URL.netloc      # "webscraper.io"
PATH_PREFIX  = PARSED_URL.path        # "/test-sites"

def normalize(url):
    """相対URLを絶対URLに変換"""
    return urljoin(START_URL, url)

def is_within_site(url: str) -> bool:
    """同一ドメインかつ /test-sites 以下かどうかを判定"""
    p = urlparse(url)
    return (p.netloc == BASE_DOMAIN) and p.path.startswith(PATH_PREFIX)

def crawl_all(start):
    """
    BFSでサイト内をクロールし、
    (ページURL, HTMLテキスト) のリストを返す
    """
    visited = set([start])
    queue   = deque([start])
    pages   = []

    while queue:
        url = queue.popleft()
        try:
            r = requests.get(url, timeout=10)
            r.raise_for_status()
            html = r.text
        except:
            continue
        pages.append((url, html))

        soup = BeautifulSoup(html, "html.parser")
        for a in soup.find_all("a", href=True):
            full = normalize(a["href"])
            if full not in visited and is_internal(full):
                visited.add(full)
                queue.append(full)
    return pages

def main():
    # 1) Excelをpandasで読み込み
    df = pd.read_excel(INPUT_PATH, sheet_name=SHEET_NAME, engine="openpyxl")
    image_list = df.iloc[:, 0].dropna().astype(str).tolist()

    if not image_list:
        print("A列に画像パスが見つかりません。")
        return

    # 2) サイトクロール
    print("サイトクロール開始…")
    pages = crawl_all(START_URL)
    print(f"取得ページ数: {len(pages)}")

    # 3) マッピング:画像ごとに使われるページURLを収集
    mapping = {img: [] for img in image_list}
    for url, html in tqdm(pages, desc="ページ調査"):
        for img in image_list:
            if img in html:
                mapping[img].append(url)

    # 4) C列用リストを生成
    results = [", ".join(mapping.get(img, [])) for img in image_list]

    # 5) openpyxlでマクロ保持しつつC列を上書き
    wb = load_workbook(INPUT_PATH, keep_vba=True)
    ws = wb[SHEET_NAME]

    # ヘッダー(1行目)に「used_pages」を設定
    ws.cell(row=1, column=3, value="used_pages")
    # 2行目以降に結果を書き込む
    for i, val in enumerate(results, start=2):
        ws.cell(row=i, column=3, value=val)

    wb.save(INPUT_PATH)
    print("完了:Excelに書き戻しました。")

if __name__ == "__main__":
    main()

(しつこいですが、Excelが閉じていないとエラーになります….)

結果は、C列に出力されます。
A2セルにある画像データを検索し、A.htmlとB.htmlとC.htmlの3つで使っていたと判定された場合、
C2セルに

A.html, B.html, C.html

という感じで、C2セル内に”, “区切りで出力されます。

使い方まとめ

  • 上記Pythonコードを scrape_and_write.py として保存
  • 同じフォルダに test.xlsm を配置
  • ターミナルで python scrape_and_write.py を実行
  • 終了後、test.xlsm のscrapingシートのC列に自動で結果が入る

これで実現できること

  • Excel上で名前を簡単に複数選択できるフォーム
  • 指定Webサイトのどこに、探したい画像があるかを自動調査してExcelに出力

プログラミング初心者でも上記をコピペ&実行すれば動作します。ぜひお試しください!

このスクレイピングしたデータをもとに、管理簿や集計表などに加工しちゃってください!

コメント

タイトルとURLをコピーしました