Skip to content

Instantly share code, notes, and snippets.

@ve3xone
Last active May 1, 2025 13:13
Show Gist options
  • Select an option

  • Save ve3xone/9d964754478fbf2dc6d5362d093f2211 to your computer and use it in GitHub Desktop.

Select an option

Save ve3xone/9d964754478fbf2dc6d5362d093f2211 to your computer and use it in GitHub Desktop.
Скрипт на питон который делает очень красивые скриншоты всего бранча с гитхаба, нужно это для проектного практикума чтоб на контрольных точках показывать прогресс по коммитам из private репозиториев
import asyncio
import glob
from PIL import Image
from urllib.parse import urlparse
from pathlib import Path
from playwright.async_api import async_playwright
TAMPERMONKEY_SCRIPT = """
// ==UserScript==
// @name GitHub Commits Cleaner
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Удаляет лишние элементы со страниц коммитов
// @author ve3xone
// @match https://github.com/*/*/commits/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
const baseSelectors = [
'.AppHeader-localBar',
'button[aria-label="Open global navigation menu"]',
'.AppHeader-globalBar-end',
'.d-flex.flex-column.flex-sm-row.gap-2',
'.text-center',
'footer.footer.pt-8.pb-6.f6.color-fg-muted.p-responsive[role="contentinfo"]',
'svg.octicon-three-bars'
];
const extraSelectorsIfAfterParam = [
'.mb-3.prc-Stack-Stack-WJVsK',
'header.Box-sc-g0xbh4-0.fDhSWy.prc-PageLayout-Header-mQXK1',
'.position-relative.header-wrapper.js-header-wrapper'
];
function removeElement(selector) {
const el = document.querySelector(selector);
if (el) el.remove();
}
function removeAllElements(selectors) {
selectors.forEach(selector => removeElement(selector));
}
const urlHasAfterParam = window.location.search.includes('after=');
const allSelectors = urlHasAfterParam
? baseSelectors.concat(extraSelectorsIfAfterParam)
: baseSelectors;
window.addEventListener('load', () => {
removeAllElements(allSelectors);
});
const observer = new MutationObserver(() => {
removeAllElements(allSelectors);
});
observer.observe(document.body, {
childList: true,
subtree: true
});
})();
"""
def images_to_pdf(prefix):
images = sorted(glob.glob(f"commits_page_{prefix}_*.png"))
if not images:
print(f"❌ Нет изображений для {prefix}")
return
image_list = [Image.open(img).convert("RGB") for img in images]
pdf_filename = f"{prefix}.pdf"
image_list[0].save(pdf_filename, save_all=True, append_images=image_list[1:])
print(f"📄 PDF сохранён: {pdf_filename}")
for img in images:
Path(img).unlink()
async def main(url, name_repo):
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
context = await browser.new_context(
viewport={'width': 1280, 'height': 1280},
device_scale_factor=1.0
is_mobile=False,
storage_state="github_state.json"
)
page = await context.new_page()
await page.add_init_script(TAMPERMONKEY_SCRIPT)
page_num = 1
while True:
print(f"🔄 Переход на страницу {page_num}: {url}")
await page.goto(url)
# await page.wait_for_load_state("networkidle")
await page.wait_for_timeout(5000) # ждём очистки DOM
# input()
# Запоминаем ссылку на "next"
try:
next_link = await page.eval_on_selector(
'[data-testid="pagination-next-button"]',
'el => el?.getAttribute("href")'
)
# Удаляем блок пагинации, чтобы не мешал скриншоту
await page.evaluate("""
const el = document.querySelector("div.d-flex.flex-justify-center.mt-2");
if (el) el.remove();
""")
except:
next_link = None
if await page.locator("text=No commits history").is_visible():
print("⚠️ Нет истории коммитов. Выход.")
break
# Делаем скриншот
filename = f"commits_page_{name_repo}_{page_num:02}.png"
await page.screenshot(path=filename, full_page=True)
print(f"📸 Скриншот сохранён: {filename}")
# Если нет ссылки на следующую страницу — выход
if not next_link:
print("✅ Больше нет кнопки Next. Завершаем.")
break
# Обновляем URL
url = "https://github.com" + next_link
page_num += 1
await browser.close()
links = [
'https://github.com/it-professions-ru/client_DMK-dev/commits/master/',
'https://github.com/it-professions-ru/HH_model-dev/commits/master/',
'https://github.com/it-professions-ru/HH_model-dev/commits/dev',
'https://github.com/it-professions-ru/HH_model-dev/commits/deep_network',
'https://github.com/it-professions-ru/analytics-worker/commits/main/',
'https://github.com/it-professions-ru/Analytics_ClickHouse-dev/commits/main/',
'https://github.com/it-professions-ru/configs/commits/main/',
'https://github.com/it-professions-ru/data-miner-dev/commits/main/',
'https://github.com/it-professions-ru/dmk.ai-info.ru-dev/commits/main/',
'https://github.com/it-professions-ru/automated_analytics-dev/commits/main/',
'https://github.com/it-professions-ru/data-miner-old/commits/main/'
]
for link in links:
parts = urlparse(link).path.strip("/").split("/")
repo = parts[1]
branch = parts[3]
prefix = f"it-professions-ru_{repo}_{branch}"
asyncio.run(main(link, prefix))
images_to_pdf(prefix)
import asyncio
from playwright.async_api import async_playwright
async def save_github_login():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
context = await browser.new_context()
page = await context.new_page()
await page.goto("https://github.com/login")
print("🔑 Войди в GitHub вручную в открывшемся окне...")
# Ждём, пока пользователь не войдёт
await page.wait_for_url("https://github.com/**", timeout=120000)
await page.wait_for_timeout(120000)
# Сохраняем куки/локалстораж
await context.storage_state(path="github_state.json")
print("✅ Сессия сохранена.")
await browser.close()
asyncio.run(save_github_login())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment