WEBディレクション / 思考
URLリストを作成する

サイトリストの詳細設計やTDK取得、見積もり調査のためにはURLのリストが必要になってきます。
このスクリプトはList.txtに記載されたURLまたはローカルHTMLファイルを順次読み込み各ページ内のリンクを抽出し重複排除・正規化の上でソートしてExcelに出力し、そのExcelファイルを自動で開くツールです。
使い方としては最初にドメインのルートだけをList.txtに記載
出力したEXCELからリンクをList.txtに追記する
これを数回繰り返すと全体のURLが取得出来ます。
機能
- 実行前に確認
- 必要モジュールの有無を確認し、インストール許可を得て実施
List.txt
からURLを読み込み- URL/ローカルファイルを取得し、リンク抽出
- 正規化・重複除去
- 優先順位つけてソート
- Excelに出力
- 出力したExcelファイルを自動で開く
# _linklist.py
import os
import sys
import re
import subprocess
import datetime
from urllib.parse import urljoin, urlparse, urlunparse
# 最初に実行の意思確認を行う
def initial_confirmation():
response = input("このスクリプトを実行しますか? (y/n): ").strip().lower()
if response != 'y':
print("キャンセルされました。")
sys.exit(0)
# 必要な外部モジュールのインストールを確認・実施
def ensure_modules():
import importlib.util
# 使用するモジュールとpipパッケージ名の対応
required = {
"requests": "requests",
"bs4": "beautifulsoup4",
"openpyxl": "openpyxl"
}
for module, package in required.items():
if importlib.util.find_spec(module) is None:
response = input(f"モジュール「{package}」が見つかりません。インストールしますか? (y/n): ").strip().lower()
if response == 'y':
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
else:
print(f"モジュール「{package}」が必要です。終了します。")
sys.exit(1)
# URLの正規化処理(末尾スラッシュ処理、不要パラメータ除去は別途)
def normalize_url(url):
parsed = urlparse(url)
# パスの末尾のスラッシュを除去(ルートは残す)
path = parsed.path.rstrip('/')
if not path:
path = '/'
return urlunparse((parsed.scheme, parsed.netloc, path, '', '', ''))
# HTMLをURLまたはローカルファイルから取得
def fetch_html(path):
if re.match(r'^https?://', path):
import requests
try:
response = requests.get(path, timeout=10)
response.raise_for_status()
return response.text, path
except Exception as e:
print(f"取得失敗: {path} → {e}")
return None, path
else:
try:
with open(path, 'r', encoding='utf-8') as f:
# ローカルファイルの場合はfile://形式でbase_urlを返す
return f.read(), f"file://{os.path.abspath(path)}"
except Exception as e:
print(f"ファイル読み込み失敗: {path} → {e}")
return None, path
# BeautifulSoupでHTMLからリンクを抽出
def extract_links(html, base_url=""):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')
links = []
for tag in soup.find_all('a', href=True):
href = tag['href'].strip()
# 無効リンクやjavascript系は無視。ただし誤記や変なバックスラッシュなどは
# 正規表現でhttps://以降を拾う(怪しいものを多少拾う)
if not href or href.startswith('#') or href.lower().startswith('javascript:') or re.match(r'^https?:\\', href):
href_match = re.search(r"(https?://[^\s\"'>]+)", href)
if href_match:
links.append(href_match.group(1))
else:
# onclick属性内にURLがあれば拾う(必要に応じて拡張可能)
onclick = tag.get("onclick", "")
onclick_match = re.search(r"(https?://[^\s\"'>]+)", onclick)
if onclick_match:
links.append(onclick_match.group(1))
continue
# 相対パスはbase_urlから絶対URLに変換
if not urlparse(href).scheme:
if base_url:
full_url = urljoin(base_url, href)
else:
continue
else:
full_url = href
links.append(full_url)
return links
# Excelに出力する処理(罫線・ゼブラ行・列幅調整含む)
def write_to_excel(links):
from openpyxl import Workbook
from openpyxl.styles import PatternFill, Border, Side
wb = Workbook()
ws = wb.active
ws.title = "Link List"
thin_border = Border(left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin'))
zebra_fill = PatternFill(start_color='DDDDDD', end_color='DDDDDD', fill_type='solid')
# ヘッダー
ws.cell(row=1, column=1, value="No.")
ws.cell(row=1, column=2, value="URL")
# データ書き込み+罫線+ゼブラ塗りつぶし
for i, link in enumerate(links, start=1):
no_cell = ws.cell(row=i+1, column=1, value=i)
url_cell = ws.cell(row=i+1, column=2, value=link)
no_cell.border = thin_border
url_cell.border = thin_border
if i % 2 == 0:
no_cell.fill = zebra_fill
url_cell.fill = zebra_fill
# 列幅調整(URL列はかなり広めに)
ws.column_dimensions['B'].width = 80
ws.column_dimensions['A'].width = 6
# ファイル名生成(実行時刻_リンクリスト.xlsx)
filename = datetime.datetime.now().strftime("%Y%m%d%H%M") + "_linklist.xlsx"
wb.save(filename)
print(f"\n✅ Excel出力完了: {filename}")
# ExcelファイルをOSに応じて自動で開く
if sys.platform.startswith("win"):
os.startfile(filename)
elif sys.platform == "darwin":
subprocess.call(["open", filename])
else:
subprocess.call(["xdg-open", filename])
# リンクの優先順位付け用キー(netloc+pathを小文字で取得)
def get_key_parts(url):
parsed = urlparse(url)
return (parsed.netloc + parsed.path).rstrip('/').lower()
def main():
# 最初に実行確認
initial_confirmation()
# 必要モジュールの確認とインストール(必要あれば)
ensure_modules()
# List.txtはスクリプトと同じフォルダにある想定
list_path = os.path.join(os.path.dirname(__file__), "List.txt")
if not os.path.exists(list_path):
print("List.txt が見つかりません。")
sys.exit(1)
all_links = []
# List.txtからURL/ファイルパスを読み込み
with open(list_path, 'r', encoding='utf-8') as f:
targets = [line.strip() for line in f if line.strip()]
total = len(targets)
if total == 0:
print("List.txt に処理対象がありません。")
sys.exit(1)
# List.txtのURLのnetloc+pathを優先キーとして先にExcelに出したいURLの判定に使用
priority_keys = set()
for t in targets:
parsed = urlparse(t)
key = (parsed.netloc + parsed.path).rstrip('/').lower()
priority_keys.add(key)
# 順次URLまたはローカルHTMLを読み込みリンクを抽出
for index, target in enumerate(targets):
print(f"[{index + 1}/{total}] 対象: {target}")
html, base_url = fetch_html(target)
if html:
links = extract_links(html, base_url)
print(f" → 抽出リンク数: {len(links)}")
all_links.extend(links)
else:
print(f" → スキップ")
# リンクを正規化しつつ重複除去
print("\nリンクを正規化して重複を除外中...")
normalized_set = {}
for link in all_links:
norm = normalize_url(link)
if norm not in normalized_set:
normalized_set[norm] = link
unique_links = list(normalized_set.values())
# 優先順位付け関数
# List.txtのURLに近いものを先に並べる(netloc+pathが先頭一致すれば優先)
def priority(link):
link_key = get_key_parts(link)
for pkey in priority_keys:
if link_key.startswith(pkey) or pkey.startswith(link_key):
return 0
return 1
# 優先度→ドメイン→パスの順でソート
unique_links.sort(key=lambda x: (priority(x), urlparse(x).netloc, urlparse(x).path))
print(f"重複除去後リンク数: {len(unique_links)}")
# Excelに出力して自動で開く
write_to_excel(unique_links)
if __name__ == "__main__":
main()