웹사이트를 S3에 백업

실행 중인 웹사이트를 S3 및 CloudFront에 복제합니다.

C05348A3-9AB8-42C9-A6E0-81DB3AC59FEB
           

이 Python 스크립트의 목적은 실행 중인 웹사이트의 모든 페이지를 CloudFront 배포에서 제공할 수 있도록 S3 버킷으로 백업하거나 마이그레이션하는 것입니다.

제 사용 사례는 간단합니다. 웹 서버에 문제가 생겼을 때를 대비해 웹사이트를 저렴하게 백업하고 싶어요. 요즘 웹사이트를 운영하는 가장 저렴한 방법 중 하나는 웹 지원 S3 버킷을 사용하는 것입니다. 매우 저렴할 뿐만 아니라 확장성도 뛰어납니다.

물론 "웹 지원 S3 버킷"이라고 하면 사람들은 더 이상 그렇게 하지 않고(제발 그러지 마세요), 그 앞에 CloudFront 배포를 넣습니다(역시 확장성이 뛰어나고 저렴합니다).

이 부분을 시작하려면 CloudFormation 템플릿을 사용할 수 있으며, CloudFront 및 S3 버킷 CloudFormation 스택을 참조하세요.

코드

라이브러리

이를 위해 다음 Python 라이브러리를 사용하겠습니다:

pip3 install --upgrade django-dotenv beautifulsoup4 lxml boto3

변수

'.env' 파일에서 AWS 관련 변수를 읽습니다(.gitignore 파일에서 해당 변수를 제외해야 함).

S3 웹사이트가 실제 웹사이트의 장애 조치(DNS 레코드를 다시 가리키기만 하면 "라이브" 상태가 되므로)가 되기를 원하므로 파일을 가져올 원본 웹사이트와 S3 버킷 이름에 대해 bucket_name 변수가 동일합니다. 사용 사례에 따라 다른 이름이 필요할 수도 있습니다.

extra_files 변수는 사이트맵.xml 파일에 포함되지 않은 S3에 백업하려는 추가 파일이 포함된 목록입니다.

dotenv.read_dotenv()
backup_region_name = os.environ.get("backup_region_name", "")
backup_aws_access_key_id = os.environ.get("backup_aws_access_key_id", "")
backup_aws_secret_access_key = os.environ.get("backup_aws_secret_access_key", "")
 
html_mime_type = 'text/html; charset=utf-8'
bucket_name = 'www.example.com'
extra_files = ['/', '/robots.txt', '/sitemap.xml', 'favicon.ico']
 
s3 = boto3.resource(
    's3',
    region_name=backup_region_name,
    aws_access_key_id=backup_aws_access_key_id,
    aws_secret_access_key=backup_aws_secret_access_key,
)

웹 페이지 목록 가져오기

원본 웹사이트에서 가져올 모든 웹 페이지 목록을 가져오기 위해 가장 간단한 옵션인 사이트의 sitemap.xml 파일을 사용하기로 결정했습니다. 100% 완벽하지는 않지만 가장 최신 옵션 중 하나라고 할 수 있습니다.

get_sitemap 함수의 목적은 웹사이트의 sitemap.xml 파일을 읽고 모든 <loc> URI를 열거하는 것입니다. 반환 코드가 200인 경우 각 페이지를 읽고 SaveFile 함수를 호출하여 콘텐츠를 S3에 저장합니다.

아름다워지는 웹페이지를 읽고 있기 때문에 페이지를 구문 분석하고 나중에 검색할 수 있도록 .css 파일을 extra_files 파일 목록에 추가합니다.

def get_sitemap(url):
    global extra_files
    full_url = f"https://{url}/sitemap.xml"
    with requests.Session() as req:
        r = req.get(full_url)
        soup = BeautifulSoup(r.content, 'lxml')
        links = [item.text for item in soup.select("loc")]
        for link in links:
            r = req.get(link)
            if r.status_code == 200:
                html_content = r.content
            else:
                print(f'\033[1;31;1m{link} {r.status_code}')
                continue
            soup = BeautifulSoup(r.content, 'html.parser')
            SaveFile(link, r.content, html_mime_type, soup.html["lang"])
 
            # Get all CSS links
            for css in soup.findAll("link", rel="stylesheet"):
                if css['href'] not in extra_files:
                    print('\033[1;37;1m', "Found the URL:", css['href'])
                    extra_files.append(css['href'])
    return

웹 페이지를 S3에 저장하기

SaveFile 함수의 목적은 웹 페이지, 이미지, .css 또는 기타 파일을 S3에 저장하는 것입니다. 저는 비용을 절감하고 필요에 맞게 조정하기 위해 S3에 "REDUCED_REDUNDANCY" 클래스를 선택했습니다.

다국어 사이트가 있기 때문에 'text/html; charset=utf-8' 밈 유형을 사용하고, S3 개체에서 설정할 수 있도록 HTML 파일의 언어도 읽으려고 합니다. 또한 스크립트는 URL을 S3 개체에 적합한 'utf-8' 이름으로 변환합니다.

내 웹사이트는 ".html" 확장자를 사용하지 않고 웹 페이지 이름 끝에 "/"를 추가합니다. S3에서는 웹 페이지 이름이 포함된 "폴더" 이름과 HTML 콘텐츠가 포함된 "/"라는 이름의 S3 개체로 변환됩니다.

웹사이트의 기본 루트 페이지 이름은 CloudFront 배포에 구성된 기본 웹 페이지이므로 'index.html'로 지정했습니다.

def SaveFile(file_name, file_content, mime_type, lang):
    global bucket_name
    my_url = urllib.parse.unquote(file_name, encoding='utf-8', errors='replace')
    my_path = urllib.parse.urlparse(my_url).path
    if my_path == '/':
        my_path = 'index.html'
    if my_path.startswith('/'):
        my_path = my_path[1:]
    print(f'\033[1;32;1m{file_name} -> {my_path} {lang}')
    bucket = s3.Bucket(bucket_name)
    if lang is not None:
        bucket.put_object(Key= my_path, Body=file_content, ContentType=mime_type, StorageClass='REDUCED_REDUNDANCY', CacheControl='max-age=0', ContentLanguage=lang)
    else:
        bucket.put_object(Key= my_path, Body=file_content, ContentType=mime_type, StorageClass='REDUCED_REDUNDANCY', CacheControl='max-age=0')

추가 파일 저장

get_others 함수의 목적은 사이트맵 파일에 포함되지 않은 "추가" 파일을 검색하는 것입니다. 여기에는 '/robots.txt', '/sitemap.xml', 'favicon.ico' 등이 포함될 수 있습니다. mimetypes 라이브러리를 사용하여 S3에서 적절한 mime-types 태그를 추측하고 설정합니다. 동일한 SaveFile 함수를 호출하여 S3에 저장합니다.

def get_others(url):
    global extra_files
    with requests.Session() as req:
        for file in extra_files:
            my_url = requests.compat.urljoin(f"https://{url}", file)
 
            # Get MIME type using guess_type
            mime_type, encoding = mimetypes.guess_type(my_url)
            if mime_type is None:
                mime_type = html_mime_type
            print("\033[1;37;1mMIME Type:", mime_type)
 
            r = req.get(my_url)
            if r.status_code == 200:
                if mime_type == html_mime_type:
                    soup = BeautifulSoup(r.content, 'html.parser')
                    lang = soup.html["lang"]
                else:
                    lang = None
                SaveFile(my_url, r.content, mime_type, lang)
            else:
                print(f'\033[1;31;1m{my_url} {r.status_code}')
                continue
    return

완성된 코드

이 문서의 전체 소스 코드는 https://github.com/Christophe-Gauge/python/blob/main/backup_website.py 에서 확인할 수 있습니다 .

이 스크립트로 모든 사용 사례를 처리할 수는 없지만 좋은 시작이 되길 바랍니다. 아래에 의견을 남겨주시거나 개선 사항이 있으면 풀 리퀘스트를 제출해 주세요!

댓글을 게시했습니다: 0

Tagged with:
AWS web