feat: Implement new database management and scraping utilities, and update application version to 13.0.0.

This commit is contained in:
kiyreload27
2025-12-31 19:50:14 +00:00
parent d7d1318a55
commit a2ac99e8b6
22 changed files with 812 additions and 32 deletions

118
debug_kitasan_scrape.py Normal file
View File

@@ -0,0 +1,118 @@
import os
import sys
from playwright.sync_api import sync_playwright
# Add parent dir to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
def debug_kitasan_scrape():
url = "https://gametora.com/umamusume/supports/30028-kitasan-black"
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context()
page = context.new_page()
page.goto(url)
page.wait_for_load_state("networkidle")
page.wait_for_timeout(2000)
# 1. Get Skill Rarity Map
rarity_map = page.evaluate("""
() => {
const map = {};
const sections = Array.from(document.querySelectorAll('div, span, h3')).filter(el =>
el.innerText.trim().startsWith('Skills from events')
);
if (sections.length === 0) return { error: "Section not found" };
const root = sections[0].closest('div');
const containers = Array.from(root.querySelectorAll('div')).filter(d =>
d.innerText.includes('Details') && d.children.length > 1
);
containers.forEach(c => {
const textNodes = Array.from(c.querySelectorAll('div, span')).filter(n => n.children.length === 0);
const name = textNodes[0] ? textNodes[0].innerText.trim() : "";
if (name && name.length > 1 && !name.includes('Details')) {
const style = window.getComputedStyle(c);
const isGold = style.backgroundImage.includes('linear-gradient') ||
style.backgroundColor.includes('rgb(255, 193, 7)') ||
c.className.includes('kkspcu');
map[name] = isGold;
}
});
return map;
}
""")
print(f"Skill Rarity Map: {rarity_map}")
# 2. Click Golden Perk Button
page.evaluate("() => { const h = Array.from(document.querySelectorAll('h2, h1')).find(el => el.innerText.includes('Training Events')); if (h) h.scrollIntoView(); }")
page.wait_for_timeout(500)
btn_found = page.evaluate("""
() => {
const labels = Array.from(document.querySelectorAll('div, span, h2, h3')).filter(el =>
el.innerText.trim() === 'Chain Events'
);
const buttons = [];
labels.forEach(label => {
let container = label.parentElement;
while (container && container.querySelectorAll('button').length === 0) {
container = container.nextElementSibling || container.parentElement;
if (container && container.tagName === 'BODY') break;
}
if (container) {
const btns = Array.from(container.querySelectorAll('button'));
btns.forEach(btn => {
const text = btn.innerText.trim();
if (text.includes('>') || text.includes('')) buttons.push(btn);
});
}
});
let goldenBtn = buttons.find(b => b.innerText.includes(''));
if (!goldenBtn) {
// Fallback to max arrows
let maxArrows = 0;
buttons.forEach(b => {
const count = (b.innerText.match(/>|/g) || []).length;
if (count > maxArrows) { maxArrows = count; goldenBtn = b; }
});
}
if (goldenBtn) {
goldenBtn.click();
return goldenBtn.innerText;
}
return null;
}
""")
print(f"Clicked button: {btn_found}")
page.wait_for_timeout(1000)
# 3. Get Skills from Tooltip
tooltip_skills = page.evaluate("""
() => {
const popovers = Array.from(document.querySelectorAll('div')).filter(d =>
window.getComputedStyle(d).zIndex > 50 &&
d.innerText.length < 2500
);
if (popovers.length === 0) return { error: "No popovers found" };
const pop = popovers[popovers.length - 1];
const skillLinks = Array.from(pop.querySelectorAll('span, a')).filter(el =>
el.innerText.length > 2 &&
!el.innerText.includes('Energy') &&
!el.innerText.includes('bond')
);
return skillLinks.map(s => s.innerText.trim());
}
""")
print(f"Tooltip Skills: {tooltip_skills}")
browser.close()
if __name__ == "__main__":
debug_kitasan_scrape()