링크 유효성 검사

링크 유효성 검사

스크립트

  • check_link.py
import os
import re
import urllib.parse
# 파일이 존재하는지 확인하는 함수
def file_exists(source_dir, 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(source_dir, 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 = set()  # 중복 제거를 위한 set 사용
    links.update(md_pattern.findall(content_without_code_blocks))
    links.update(obsidian_pattern.findall(content_without_code_blocks))
    links.update(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, image_directory):
    broken_links = {
        "Anchor": [],
        "Image": [],
        "Internal": [],
    }
    
    for md_root, dirs, md_files in os.walk(base_path):
        # _나 .으로 시작하는 디렉토리 제외
        dirs[:] = [d for d in dirs if not d.startswith('.') and not d.startswith('_')]
        
        for md_file in md_files:
            if md_file.endswith(".md") and md_file != "_index.md":  # _index.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(md_root, 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
# 디렉토리 경로 설정
source_dir = os.environ.get('SOURCE_DIR', r"D:\obsidian")
source_static_dir = os.environ.get('SOURCE_STATIC_DIR', os.path.join(source_dir, "resources"))
# 마크다운 파일 처리 및 깨진 링크 찾기
broken_links = process_markdown_files(source_dir, source_static_dir)
# 깨진 링크 출력
print("\nBroken links check start")
for link_type, links in broken_links.items():
    if links:
        print(f"\t{link_type} Links:")
        for md_file, link, actual_path in links:
            print(f"\t\tFile: {md_file}, Link: {link}, Path: {actual_path}")
print("\nBroken links check end")
print()

기능

파일 존재 확인

  • file_exists 함수는 주어진 경로에 파일이 존재하는지 확인합니다.
  • 링크를 URL 디코딩하고, 경로 구분자를 운영체제에 맞게 변환하여 파일의 실제 존재 여부를 체크합니다.

링크 추출

  • extract_links 함수는 Markdown 파일에서 코드 블록을 제외한 이미지 링크, Obsidian 링크, 및 일반 링크를 추출합니다.
  • 코드 블록 내의 링크는 무시하여 실제 문서 내에서 유효성을 검증할 수 있습니다.

Markdown 파일 처리

  • process_markdown_files 함수는 모든 Markdown 파일을 순회하면서 각 파일에서 추출한 링크의 유효성을 확인합니다.
  • 링크의 종류에 따라 다음과 같이 처리합니다:
    • 앵커 링크: 파일 내에 해당 앵커가 존재하는지 확인합니다.
    • 이미지 링크: 이미지 파일이 resources 디렉토리에 존재하는지 확인합니다.
    • 내부 링크: 해당 파일이 존재하는지 확인하며, 외부 URL은 무시합니다.

필터

  • . 이나 _ 로 시작하는 폴더는 제외됩니다.
  • _index.md 파일은 제외됩니다.

결과 출력

  • 깨진 링크가 발견되면, 파일 경로와 링크 경로, 실제 경로를 콘솔에 출력하며, 그 외 조치는 취하지 않습니다.