Я случайно наткнулся на статью автора Lucas Neves Pereira под названием "Build your own LinkTree with Go and GitHub Pages". В статье описано, как создать подобие LinkTree (аналог Taplink) на языке Go и GitHub Pages. Я, как любитель языка Python, решил реализовать проект на этом языке.

king-tri-ton.github.io/pythonpagelink
king-tri-ton.github.io/pythonpagelink

Шаг 1: Подготовка файловой структуры проекта

Первым делом создадим файловую структуру для нашего проекта. Мы организуем наш проект таким образом, чтобы он был легко поддерживаемым и удобным для развертывания на GitHub Pages.

Файловая структура:

/ (root)
|-- /docs
|   |-- index.html
|   |-- /assets
|       |-- (файлы стилей, скриптов, иконок и т.д.)
|-- config.yml
|-- generate_site.py
|-- /themes
  • /docs: В этой папке будут храниться сгенерированные HTML-файлы и все необходимые ассеты (изображения, стили, скрипты). Эта папка будет использоваться для развертывания сайта на GitHub Pages.

  • config.yml: Файл конфигурации, который содержит все данные для персонализации сайта.

  • generate_site.py: Скрипт на Python, который будет генерировать сайт на основе данных из config.yml.

  • /themes: Папка с темами для сайта. В нашем случае, здесь хранится единственная тема custom, которая включает в себя шаблон HTML, стили, скрипты и изображения.

Шаг 2: Настройка файла конфигурации (config.yml)

Файл config.yml содержит данные о пользователе и ссылки, которые будут отображаться на сайте. Вот его содержимое:

name: "King Triton"
picture: "assets/img/picture.jpg"
bio: "Programmer python and php/laravel"
meta:
  lang: "en"
  description: "Programmer python and php/laravel"
  title: "King Triton"
  author: "King Triton"
  siteUrl: "https://king-tri-ton.github.io/pythonpagelink/"
links:
  - name: "Github"
    url: "https://github.com/king-tri-ton"
  - name: "Dev.to"
    url: "https://dev.to/king_triton"
  - name: "Patreon"
    url: "https://www.patreon.com/king_triton"
  - name: "Telegram"
    url: "https://t.me/king_triton"
  - name: "Instagram"
    url: "https://www.instagram.com/king_tri_ton"
theme: "custom"
  • name: Имя пользователя, которое будет отображаться на сайте.

  • picture: Путь к изображению пользователя.

  • bio: Краткая биография пользователя.

  • meta: Метаинформация сайта (язык, описание, заголовок, автор, URL сайта).

  • links: Список ссылок, которые будут отображаться на сайте. Каждый элемент содержит название и URL.

  • theme: Тема сайта, которую следует использовать.

Шаг 3: Разработка Python-скрипта для генерации сайта (generate_site.py)

Далее мы напишем скрипт на Python, который будет использовать шаблон из темы, данные из config.yml и генерировать готовый HTML-файл.

import os
import shutil
from jinja2 import Environment, FileSystemLoader
import yaml

# Загрузка конфигурации
with open('config.yml', 'r') as config_file:
    config = yaml.safe_load(config_file)

# Создание выходной директории
output_dir = 'docs'
os.makedirs(output_dir, exist_ok=True)

# Настройка Jinja2
env = Environment(loader=FileSystemLoader('themes/custom'))
template = env.get_template('index.html')

# Генерация HTML файла
output_html = template.render(config=config)
with open(os.path.join(output_dir, 'index.html'), 'w') as fh:
    fh.write(output_html)

# Копирование папки assets в выходной каталог
assets_source = os.path.join('themes', config['theme'], 'assets')
assets_dest = os.path.join(output_dir, 'assets')
if os.path.exists(assets_source):
    shutil.copytree(assets_source, assets_dest, dirs_exist_ok=True)

print("Site generated successfully.")
  • Загрузка конфигурации: Скрипт загружает данные из файла config.yml.

  • Создание выходной директории: Папка docs создается автоматически, если она не существует.

  • Настройка Jinja2: Используется Jinja2 для загрузки шаблона HTML и рендеринга контента.

  • Генерация HTML-файла: Скрипт генерирует файл index.html с использованием данных из конфигурации и сохраняет его в папке docs.

  • Копирование ассетов: Все ассеты (CSS, изображения, скрипты) копируются в папку docs/assets.

Шаг 4: Создание темы и ассетов

Теперь создадим тему, которая будет использоваться для нашего сайта. В папке themes/custom/ должны находиться следующие файлы:

themes/custom/index.html

Это основной HTML-шаблон сайта. Он использует переменные из файла конфигурации.

<!DOCTYPE html>
<html lang="{{ config.meta.lang }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="{{ config.meta.description }}">
    <title>{{ config.meta.title }}</title>
    <meta name="author" content="{{ config.meta.author }}">
    <link rel="canonical" href="{{ config.meta.siteUrl }}">
    <link rel="icon" type="image/x-icon" href="assets/icons/favicon.ico">
    <link rel="stylesheet" href="assets/css/styles.css">
    <meta property="og:title" content="{{ config.meta.title }}">
    <meta property="og:site_name" content="{{ config.meta.title }}">
    <meta property="og:description" content="{{ config.meta.description }}">
    <meta property="og:locale" content="{{ config.meta.lang }}">
    <meta name="twitter:title" content="{{ config.meta.title }}">
    <meta name="twitter:description" content="{{ config.meta.description }}">
</head>
<body>
    <header>
        <img src="{{ config.picture }}" alt="Picture" class="avatar">
        <h1>{{ config.name }}</h1>
        <small class="bio">{{ config.bio }}</small>
    </header>
    <main>
        <section class="links">
            {% for link in config.links %}
            <a class="link-item" href="{{ link.url }}" target="_blank" rel="noopener noreferrer">
                <p>{{ link.name }}</p>
            </a>
            {% endfor %}
        </section>
    </main>
    <footer>
        <small>© <span class="year"></span> {{ config.meta.author }}</small>
    </footer>
    <script src="assets/js/script.js"></script>
</body>
</html>

themes/custom/assets/styles.css

Файл CSS для стилизации страницы.

/* CSS Reset */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Variables */
:root {
  --max-width: 600px;
  --font-family: 'Inter', sans-serif;
  --padding: 1rem;
  --header-margin-bottom: 1rem;
  --line-height: 2;
  --font-size: 16px;

  --primary-color-light: #ffffff;
  --background-color-light: #f0f0f0;
  --text-color-light: #333;
  --link-color-light: #1a73e8;
  --bio-color-light: #666;

  --primary-color-dark: #1e1e1e;
  --background-color-dark: #121212;
  --text-color-dark: #e0e0e0;
  --link-color-dark: #8ab4f8;
  --bio-color-dark: #aaa;
}

/* Light Theme */
@media (prefers-color-scheme: light) {
  :root {
    --primary-color: var(--primary-color-light);
    --background-color: var(--background-color-light);
    --text-color: var(--text-color-light);
    --link-color: var(--link-color-light);
    --bio-color: var(--bio-color-light);
  }
}

/* Dark Theme */
@media (prefers-color-scheme: dark) {
  :root {
    --primary-color: var(--primary-color-dark);
    --background-color: var(--background-color-dark);
    --text-color: var(--text-color-dark);
    --link-color: var(--link-color-dark);
    --bio-color: var(--bio-color-dark);
  }
}

/* Global Styles */
html {
  font-family: var(--font-family);
  font-size: var(--font-size);
  line-height: var(--line-height);
}

body {
  max-width: var(--max-width);
  min-height: 100vh;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: var(--background-color);
  color: var(--text-color);
  padding: var(--padding);
}

/* Header Styles */
header {
  padding: var(--padding) 0;
  margin-bottom: var(--header-margin-bottom);
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.avatar {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--primary-color);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

h1 {
  font-size: 24px;
  margin-bottom: 0.5rem;
}

.bio {
  font-size: 14px;
  color: var(--bio-color);
  margin-bottom: 1rem;
}

/* Main Content Styles */
main {
  width: 100%;
  flex: 1;
}

.links {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  text-align: center;
  overflow-y: auto;
  max-height: 400px;
}

.link-item {
  display: block;
  padding: 16px 20px;
  text-decoration: none;
  color: var(--link-color);
  background: var(--primary-color);
  border-radius: 12px;
  border: 1px solid var(--link-color);
  transition: background-color 0.25s, color 0.25s;
}

.link-item:hover,
.link-item:focus {
  background-color: var(--link-color);
  color: var(--primary-color);
}

.link-item p {
  line-height: 1.5;
  font-weight: 500;
}

/* Footer Styles */
footer {
  width: 100%;
  text-align: center;
  padding: 1rem 0;
  font-size: 14px;
  gap: 1rem;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* ScrollBar */
::-webkit-scrollbar {
  width: 5px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: transparent;
}

::-webkit-scrollbar-thumb:hover {
  background: transparent;
}

themes/custom/assets/js/script.js

Файл JavaScript для базовых функциональностей.

console.log("scripts loaded");

const yearDate = new Date().getFullYear().toString();
document.querySelector(".year").innerText = yearDate;

themes/custom/assets/img/picture.jpg

Фотография, которая будет использоваться в качестве аватара.

Шаг 5: Генерация сайта

После того как все файлы созданы, запустите скрипт generate_site.py, чтобы сгенерировать сайт:

python generate_site.py

Сайт будет сгенерирован в папке docs.

Шаг 6: Развертывание на GitHub Pages

Развертывание на GitHub Pages
Развертывание на GitHub Pages
  • Создайте новый репозиторий на GitHub.

  • Загрузите все файлы, включая папку docs, в репозиторий.

  • Перейдите в раздел Settings репозитория.

  • В разделе Pages выберите ветку master и папку /docs как источник.

  • Сохраните изменения и подождите, пока GitHub Pages развернет ваш сайт.

Теперь ваш сайт будет доступен по адресу https://[username].github.io/[repository-name]/


Вот и все! Теперь у вас есть собственный сайт в стиле Taplink, созданный на Python и развернутый на GitHub Pages. Вы можешь посмотреть мой готовый результат по адресу https://king-tri-ton.github.io/pythonpagelink/.

Благодарю за внимание!

Комментарии (11)


  1. Andrey_Solomatin
    29.08.2024 13:31
    +1

    Копирование ассетов: Все ассеты (CSS, изображения, скрипты) копируются в папку docs/assets.

    Не проще ли сразу полужить туда, для простоты.


    1. KING_TRITON Автор
      29.08.2024 13:31

      да


  1. Andrey_Solomatin
    29.08.2024 13:31

    Зачем кстати такая страничка нужна?

    Можно же просто поместить всё это на свою страницу на GitHub?


    1. KING_TRITON Автор
      29.08.2024 13:31

      да


  1. Andrey_Solomatin
    29.08.2024 13:31

    Я обычно не запускаю скрипт который создаёт страницы, а использую GitHub actions для этого.


    1. KING_TRITON Автор
      29.08.2024 13:31

      к сожалению я еще не знаком с GitHub Actions


  1. testtest4312
    29.08.2024 13:31
    +1

    Спасибо за материал) Да?


    1. KING_TRITON Автор
      29.08.2024 13:31

      да пожалуйста)


  1. MAXH0
    29.08.2024 13:31

    Я чешу репу и думаю. Возможно я чего не понял. Объясните.
    Taplink - это конструктор сайтов. Этаких сайтов - визиток. Зачем, чтобы сделать не конструктор, а индивидуальную визитку нужны Go и Python?


    1. KING_TRITON Автор
      29.08.2024 13:31

      в англоязычной статье написано про likntree, а этот сервис по большей части предназначен для ссылок, ну то есть страничка со ссылками, такая тема популярна в запретграме, таплинк - на сколько я помню изначально и был сервисом как линктри, но потом перерос в конструктор, но ассоциации у меня с таплинком как линктри, поэтому так

      по поводу Go и Python - а почему нет?)


  1. Shbr
    29.08.2024 13:31
    +1

    Спасибо, интересно! Напишу как веб-редактор. Думал сделать как-то свой аналог Таплинка страницей сайта или на отдельном субдомене, но потом заметил, что у Таплинка ИКС 7300. Соответственно, для поисковой оптимизации ссылки с Таплинка будут лучше, чем с любого моего сайта. И передумал делать свой аналог.