링크 유효성 검사

스크립트

import os
import re
import urllib.parse

# 파일이 존재하는지 확인하는 함수
def file_exists(base_path, file_path):
    file_path = urllib.parse.unquote(file_path)  # URL 디코딩
    file_path = file_path.replace('/', os.sep).replace('\\', os.sep)
    return os.path.exists(os.path.join(base_path, file_path))

# 마크다운 파일에서 링크를 추출하는 함수
def extract_links(content):
    # 코드 블럭 패턴
    code_block_pattern = re.compile(r'(```[\s\S]+?```|`[^`]+`)')
    
    # 코드 블럭 안의 내용을 제거
    content_without_code_blocks = code_block_pattern.sub('', content)
    
    # 이미지 링크, 옵시디언 링크, 일반 링크 추출
    md_pattern = re.compile(r'!\[([^\]]*)\]\(([^)]+)\)')  # 이미지 링크 패턴
    obsidian_pattern = re.compile(r'!\[\[([^\]]+)\]\]')  # 옵시디언 링크 패턴
    link_pattern = re.compile(r'\[([^\]]+)\]\(([^)]+)\)')  # 일반 링크 패턴
    
    links = md_pattern.findall(content_without_code_blocks)
    links += obsidian_pattern.findall(content_without_code_blocks)
    links += link_pattern.findall(content_without_code_blocks)
    
    return [link[1] if isinstance(link, tuple) else link for link in links]

# 마크다운 파일을 처리하는 함수
def process_markdown_files(base_path, md_directory, image_directory):
    broken_links = {
        "Anchor": [],
        "Image": [],
        "Internal": [],
    }
    
    for md_root, _, md_files in os.walk(md_directory):
        for md_file in md_files:
            if md_file.endswith(".md"):
                md_file_path = os.path.join(md_root, md_file)
                with open(md_file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                links = extract_links(content)
                for link in links:
                    actual_path = os.path.join(base_path, urllib.parse.unquote(link.replace('/', os.sep)))
                    if link.startswith('#'):
                        # 앵커 링크는 현재 파일 내에 해당 앵커가 존재하는지 확인
                        anchor = link[1:]
                        if not re.search(r'^#+\s+' + re.escape(anchor), content, re.MULTILINE):
                            broken_links["Anchor"].append((md_file_path, link, actual_path))
                    elif link.endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp')):
                        # 이미지 링크
                        image_path = os.path.join(image_directory, urllib.parse.unquote(link.replace('/', os.sep)))
                        if not os.path.exists(image_path):
                            broken_links["Image"].append((md_file_path, link, image_path))
                    elif not re.match(r'http[s]?://', link):
                        # 내부 링크
                        if not file_exists(base_path, link):
                            broken_links["Internal"].append((md_file_path, link, actual_path))
    
    return broken_links

# 디렉토리 경로 설정
base_path = r'D:\Obsidian\DocFlow'
md_directory_path = r'D:\Obsidian\DocFlow'
image_directory_path = os.path.join(base_path, 'Resources')

# 마크다운 파일 처리 및 깨진 링크 찾기
broken_links = process_markdown_files(base_path, md_directory_path, image_directory_path)

# 깨진 링크 출력
print("\nBroken links:")
for link_type, links in broken_links.items():
    if links:
        print(f"\n{link_type} Links:")
        for md_file, link, actual_path in links:
            print(f"File: {md_file}, Link: {link}, Path: {actual_path}")

스크립트 설명

  • 마크다운 파일 내의 앵커, 이미지, 내부 링크를 검사하여 깨진 링크를 식별하고 출력합니다.