← На главную

Скрипт на Python для создания оффлайн копии блога

Народная мудрость гласит, что интернет все помнит. Практика, однако, этого не подтверждает. Как человеку, который 10+ лет исправляет битые ссылки в данном блоге, можете мне поверить. Названные обстоятельства заставили задуматься о создании надежной копии «Записок программиста».

Был написал следующий скрипт на Python:

#!/usr/bin/env python3 # https://eax.me/ full offline copy # Aleksander Alekseev, 2024 from datetime import datetime as dt import subprocess import glob import sys import os import re dirname = dt.today().strftime('%Y-%m-%d') os.makedirs(dirname+"/files/github", exist_ok=True) code = subprocess.call("wget -r -l 0 -k -nH https://eax.me/ -P " + dirname +" 2>&1 | tee wget.log", shell=True) if code != 0: print("Wget returned {}, check wget.log for 404s," + " 301s, etc".format(code)) sys.exit(1) os.remove("wget.log") for page_path in glob.glob(dirname + '/**/index.html', recursive=True): page_data = "" with open(page_path) as page_f: page_data = page_f.read() re_str = """<a[ ]+href=['"]https?://github.com/afiskon/([^'"/]+)""" for m in re.finditer(re_str, page_data): repo_name = m.group(1) print("Found repo: {} in {}".format(repo_name, page_path)) repo_backup_dir = dirname+"/files/github/"+repo_name if not os.path.isdir(repo_backup_dir): print(" New one, cloning...") code = subprocess.call( "git clone --depth 1 https://github.com/afiskon/" + repo_name + ".git " + repo_backup_dir, shell=True) if code != 0: print("Failed to clone {}, git returned {}".format( repo_name, code)) sys.exit(1) page_data = re.sub( """(<a[ ]+href=["'])https?://github.com/afiskon/([^'"/]+)""" + """(/tree/master|/tree/main|/blob/master|/blob/main)?""" + """([^'"]*["'])""", r"\1../files/github/\2\4", page_data) # remove LiveInternet JavaScript code page_data = re.sub( """(?s)<script type="text/javascript">.*?</script>""", "", page_data) print("Updating {}".format(page_path)) with open(page_path, 'w') as page_f: page_f.write(page_data) subprocess.check_call( "rm -rf "+dirname+"/files/github/*/.git", shell=True) subprocess.check_call( "tar -cvzf "+dirname+".tgz "+dirname, shell=True) subprocess.check_call( "rm -rf "+dirname, shell=True) print("DONE!")

Примечание: Обратите внимание на пост Памятка по регулярным выражениям, если вдруг вы его пропустили.

Большую часть работы здесь выполняет команда wget -r -k. Она рекурсивно скачивает сайт, а затем исправляет абсолютные адреса на относительные. Это работает с большинством статических и CGI сайтов. Главное – следить, чтобы в robots.txt через директиву Disallow: не был запрещен доступ к каким-нибудь важным файлам.

Существенная часть контента «Записок программиста» приходится на исходники, которые я публикую на GitHub. Их тоже хотелось бы сохранять. Основная часть скрипта этим и занимается.

Сохраненные посты изучается на предмет наличия ссылок, которые начинаются с github.com/afiskon/. Когда находится ссылка на репозиторий, он скачивается командой git clone --depth 1. История коммитов при этом теряется, но в моем коде она не несет особой ценности. Копия поста исправляется так, чтобы ссылка вела не на GitHub, а на локальную копию репозитория. Допускается ссылаться на конкретные файлы и каталоги внутри репозитория, но не на коммиты.

Копия сайта сохраняется на надежном носителе:

Оффлайн копия блога EAX.ME на оптическом диске

Примечательный факт: с диска блог можно читать даже на 25-летнем ноутбуке в браузере Internet Explorer 8.0 или Firefox 1.0. Сайт верстался в конце 2000-х, и потому прекрасно совместим как с новыми, так и со старыми браузерами.

Теперь, даже если дата-центры моего хостера и GitHub’а одновременно накроет цунами, у меня останется копия блога на CD. Резервные копии скриптов и базы данных у меня тоже есть. Однако «Записки программиста» работают на старых технологиях. Мне кажется, что перенести блог к новому хостеру будет непросто. В случае чего мне проще «заморозить» текущую версию блога и начать блог 2.0 на более новых технологиях.

Дополнение: Улучшенная версия скрипта доступна здесь. Суть доработок описана в комментариях к коду.

Дополнение: В продолжение темы см статью Как я переносил блог с WordPress на статику.