ウェブサイトをS3にバックアップする

実行中のウェブサイトをS3とCloudFrontにクローンする。

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

このPythonスクリプトの目的は、実行中のウェブサイトからすべてのページをS3バケットにバックアップまたは移行し、CloudFrontディストリビューションで提供することです。

私の使用ケースは単純で、ウェブサーバーに何か問題が発生した場合に備えて、ウェブサイトの安価なバックアップを取りたいのだ。現在、ウェブサイトを運営する最も安価な方法の1つは、ウェブ対応のS3バケットを持つことだ。非常に安いだけでなく、拡張性も抜群だ。

ウェブ対応S3バケット」と言っても、もちろん、もうみんなそんなことはしない(しないでほしい)、CloudFrontディストリビューションを前に置く(これも超スケーラブルで安価)。

この部分を始めるには、私のCloudFormationテンプレートを使うことができる。

コード

図書館

そのために、以下のPythonライブラリを使用する:

pip3 install --upgrade django-dotenv beautifulsoup4 lxml boto3

変数

AWS固有の変数を".env "ファイルから読み込みます(.gitignoreファイルで除外してください)。

S3ウェブサイトを実際のウェブサイトのフェイルオーバーにしたいので("ライブ "にするためにDNSレコードを再指定する必要があるだけ)、Bucket_name変数は、ファイルを取り出したい元のウェブサイトとS3バケット名で同じです。あなたのユースケースでは異なる名前が必要かもしれない。

extra_files変数は、sitemap.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に保存します。

beautifulsoupでウェブページを読み込んでいるので、ページを解析し、任意の.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'mime-typeを使用し、S3オブジェクトに設定できるようにHTMLファイルの言語も読み取ろうとしている。スクリプトはまた、URLをS3オブジェクトの適切な'utf-8'名に変換する。

私のウェブサイトは拡張子".html "を使用せず、ウェブページ名の最後に"/"を付加している。S3では、これはウェブページの名前を持つ "フォルダ "名と、HTMLコンテンツを含む"/"という名前のS3オブジェクトに変換される。

私のウェブサイトのデフォルトのルートページに'index.html'という名前をつけたのは、それがCloudFrontディストリビューションで設定されているデフォルトのウェブページだからだ。

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ライブラリを使用して、適切なmime-typesタグを推測し、S3に設定します。同じ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