Spaces:
Running
Running
Download index.html from mhmtx/anycoder-3fba633c: direct link, hf CLI and curl.
- Browser
- Download file 39.4 kB
-
https://huggingface.co/spaces/mhmtx/anycoder-3fba633c/resolve/main/index.html
- Command line
-
hf download hf://spaces/mhmtx/anycoder-3fba633c/index.html
-
curl -L -o index.html https://huggingface.co/spaces/mhmtx/anycoder-3fba633c/resolve/main/index.html
39.4 kB
| <html lang="tr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Teknofest Veri Toplama Otomasyonu</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| --primary: #2563eb; | |
| --primary-dark: #1d4ed8; | |
| --secondary: #7c3aed; | |
| --success: #10b981; | |
| --warning: #f59e0b; | |
| --danger: #ef4444; | |
| --dark: #1e293b; | |
| --light: #f8fafc; | |
| --gray: #64748b; | |
| --card-bg: #ffffff; | |
| --gradient: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%); | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| body { | |
| background: linear-gradient(180deg, #0f172a 0%, #1e293b 100%); | |
| min-height: 100vh; | |
| color: #e2e8f0; | |
| } | |
| /* Header */ | |
| .header { | |
| background: rgba(15, 23, 42, 0.95); | |
| backdrop-filter: blur(20px); | |
| padding: 1rem 2rem; | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| border-bottom: 1px solid rgba(255,255,255,0.1); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| font-weight: 700; | |
| font-size: 1.25rem; | |
| } | |
| .logo-icon { | |
| width: 40px; | |
| height: 40px; | |
| background: var(--gradient); | |
| border-radius: 10px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1.25rem; | |
| } | |
| .header-actions { | |
| display: flex; | |
| gap: 1rem; | |
| align-items: center; | |
| } | |
| .btn { | |
| padding: 0.625rem 1.25rem; | |
| border-radius: 8px; | |
| font-weight: 500; | |
| font-size: 0.875rem; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| border: none; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .btn-primary { | |
| background: var(--gradient); | |
| color: white; | |
| box-shadow: 0 4px 15px rgba(37, 99, 235, 0.3); | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 20px rgba(37, 99, 235, 0.4); | |
| } | |
| .btn-outline { | |
| background: transparent; | |
| border: 1px solid rgba(255,255,255,0.2); | |
| color: #e2e8f0; | |
| } | |
| .btn-outline:hover { | |
| background: rgba(255,255,255,0.1); | |
| } | |
| /* Main Container */ | |
| .container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 2rem; | |
| } | |
| /* Hero Section */ | |
| .hero { | |
| text-align: center; | |
| padding: 3rem 0; | |
| } | |
| .hero h1 { | |
| font-size: 2.5rem; | |
| font-weight: 800; | |
| margin-bottom: 1rem; | |
| background: linear-gradient(135deg, #60a5fa 0%, #a78bfa 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .hero p { | |
| color: #94a3b8; | |
| font-size: 1.125rem; | |
| max-width: 600px; | |
| margin: 0 auto; | |
| } | |
| /* Process Flow */ | |
| .process-flow { | |
| display: flex; | |
| justify-content: center; | |
| gap: 1rem; | |
| margin: 2rem 0; | |
| flex-wrap: wrap; | |
| } | |
| .process-step { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| padding: 1rem 1.5rem; | |
| background: rgba(255,255,255,0.05); | |
| border-radius: 12px; | |
| border: 1px solid rgba(255,255,255,0.1); | |
| transition: all 0.3s ease; | |
| } | |
| .process-step:hover { | |
| background: rgba(255,255,0.1); | |
| border-color: rgba(37, 99, 235, 0.5); | |
| transform: translateY(-2px); | |
| } | |
| .step-number { | |
| width: 32px; | |
| height: 32px; | |
| background: var(--gradient); | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: 700; | |
| font-size: 0.875rem; | |
| } | |
| .step-arrow { | |
| color: var(--gray); | |
| font-size: 1.25rem; | |
| } | |
| /* Tabs */ | |
| .tabs { | |
| display: flex; | |
| gap: 0.5rem; | |
| margin-bottom: 2rem; | |
| background: rgba(255,255,255,0.05); | |
| padding: 0.5rem; | |
| border-radius: 12px; | |
| width: fit-content; | |
| } | |
| .tab { | |
| padding: 0.75rem 1.5rem; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| font-weight: 500; | |
| color: #94a3b8; | |
| border: none; | |
| background: transparent; | |
| } | |
| .tab.active { | |
| background: var(--gradient); | |
| color: white; | |
| } | |
| .tab:hover:not(.active) { | |
| background: rgba(255,255,255,0.1); | |
| color: #e2e8f0; | |
| } | |
| /* Code Section */ | |
| .code-section { | |
| background: #0d1117; | |
| border-radius: 16px; | |
| overflow: hidden; | |
| border: 1px solid rgba(255,255,255,0.1); | |
| } | |
| .code-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 1rem 1.5rem; | |
| background: rgba(255,255,255,0.03); | |
| border-bottom: 1px solid rgba(255,255,255,0.1); | |
| } | |
| .code-title { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| font-weight: 600; | |
| } | |
| .code-title i { | |
| color: #f59e0b; | |
| } | |
| .code-content { | |
| padding: 1.5rem; | |
| overflow-x: auto; | |
| } | |
| pre { | |
| font-family: 'Fira Code', 'Consolas', monospace; | |
| font-size: 0.8rem; | |
| line-height: 1.6; | |
| color: #c9d1d9; | |
| white-space: pre-wrap; | |
| word-wrap: break-word; | |
| } | |
| .keyword { color: #ff7b72; } | |
| .string { color: #a5d6ff; } | |
| .function { color: #d2a8ff; } | |
| .comment { color: #8b949e; } | |
| .number { color: #79c0ff; } | |
| .decorator { color: #d2a8ff; } | |
| /* Configuration Panel */ | |
| .config-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 1.5rem; | |
| margin-bottom: 2rem; | |
| } | |
| .config-card { | |
| background: rgba(255,255,255,0.05); | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| border: 1px solid rgba(255,255,255,0.1); | |
| } | |
| .config-card h3 { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| margin-bottom: 1rem; | |
| font-size: 1rem; | |
| color: #e2e8f0; | |
| } | |
| .config-card h3 i { | |
| color: var(--primary); | |
| } | |
| .config-item { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 0.75rem 0; | |
| border-bottom: 1px solid rgba(255,255,255,0.05); | |
| } | |
| .config-item:last-child { | |
| border-bottom: none; | |
| } | |
| .config-label { | |
| color: #94a3b8; | |
| font-size: 0.875rem; | |
| } | |
| .config-value { | |
| font-weight: 500; | |
| color: #e2e8f0; | |
| font-family: monospace; | |
| font-size: 0.8rem; | |
| background: rgba(0,0,0,0.3); | |
| padding: 0.25rem 0.5rem; | |
| border-radius: 4px; | |
| } | |
| /* Status Panel */ | |
| .status-panel { | |
| background: rgba(255,255,255,0.05); | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| margin-top: 2rem; | |
| border: 1px solid rgba(255,255,255,0.1); | |
| } | |
| .status-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 1rem; | |
| } | |
| .status-title { | |
| font-weight: 600; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .status-badge { | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 20px; | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| } | |
| .status-badge.ready { | |
| background: rgba(16, 185, 129, 0.2); | |
| color: #10b981; | |
| } | |
| .status-badge.running { | |
| background: rgba(245, 158, 11, 0.2); | |
| color: #f59e0b; | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .log-container { | |
| background: #0d1117; | |
| border-radius: 8px; | |
| padding: 1rem; | |
| max-height: 300px; | |
| overflow-y: auto; | |
| font-family: 'Fira Code', monospace; | |
| font-size: 0.75rem; | |
| } | |
| .log-entry { | |
| padding: 0.25rem 0; | |
| display: flex; | |
| gap: 0.75rem; | |
| } | |
| .log-time { | |
| color: #64748b; | |
| } | |
| .log-info { color: #60a5fa; } | |
| .log-success { color: #10b981; } | |
| .log-warning { color: #f59e0b; } | |
| .log-error { color: #ef4444; } | |
| /* Input Fields */ | |
| .input-group { | |
| margin-bottom: 1rem; | |
| } | |
| .input-group label { | |
| display: block; | |
| margin-bottom: 0.5rem; | |
| font-size: 0.875rem; | |
| color: #94a3b8; | |
| } | |
| .input-field { | |
| width: 100%; | |
| padding: 0.75rem 1rem; | |
| background: rgba(0,0,0,0.3); | |
| border: 1px solid rgba(255,255,255,0.1); | |
| border-radius: 8px; | |
| color: #e2e8f0; | |
| font-size: 0.875rem; | |
| transition: all 0.2s ease; | |
| } | |
| .input-field:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2); | |
| } | |
| /* Footer */ | |
| .footer { | |
| text-align: center; | |
| padding: 2rem; | |
| color: #64748b; | |
| font-size: 0.875rem; | |
| border-top: 1px solid rgba(255,255,255,0.1); | |
| margin-top: 3rem; | |
| } | |
| .footer a { | |
| color: var(--primary); | |
| text-decoration: none; | |
| } | |
| .footer a:hover { | |
| text-decoration: underline; | |
| } | |
| /* Responsive */ | |
| @media (max-width: 768px) { | |
| .header { | |
| padding: 1rem; | |
| } | |
| .container { | |
| padding: 1rem; | |
| } | |
| .hero h1 { | |
| font-size: 1.75rem; | |
| } | |
| .process-flow { | |
| flex-direction: column; | |
| } | |
| .step-arrow { | |
| display: none; | |
| } | |
| .tabs { | |
| width: 100%; | |
| overflow-x: auto; | |
| } | |
| } | |
| /* Copy Button */ | |
| .copy-btn { | |
| position: absolute; | |
| right: 1rem; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| padding: 0.5rem 1rem; | |
| background: var(--primary); | |
| border: none; | |
| border-radius: 6px; | |
| color: white; | |
| cursor: pointer; | |
| font-size: 0.75rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| transition: all 0.2s ease; | |
| } | |
| .copy-btn:hover { | |
| background: var(--primary-dark); | |
| } | |
| .code-wrapper { | |
| position: relative; | |
| } | |
| /* Scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: rgba(0,0,0,0.2); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: rgba(255,255,255,0.2); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: rgba(255,255,255,0.3); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header class="header"> | |
| <div class="logo"> | |
| <div class="logo-icon"> | |
| <i class="fas fa-rocket"></i> | |
| </div> | |
| <span>Teknofest Otomasyon</span> | |
| </div> | |
| <div class="header-actions"> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="color: #94a3b8; text-decoration: none; font-size: 0.875rem;"> | |
| Built with anycoder | |
| </a> | |
| </div> | |
| </header> | |
| <div class="container"> | |
| <div class="hero"> | |
| <h1>Teknofest Veri Toplama Otomasyonu</h1> | |
| <p>Teknofest yarışmalarından İlkokul/Ortaokul kategorisindeki başarılı projelerin raporlarını otomatik olarak indirin ve proje adlarını çıkarın.</p> | |
| </div> | |
| <!-- Process Flow --> | |
| <div class="process-flow"> | |
| <div class="process-step"> | |
| <span class="step-number">1</span> | |
| <span>Sezonları Tara</span> | |
| </div> | |
| <i class="fas fa-chevron-right step-arrow"></i> | |
| <div class="process-step"> | |
| <span class="step-number">2</span> | |
| <span>Ortaokul Kategorisi</span> | |
| </div> | |
| <i class="fas fa-chevron-right step-arrow"></i> | |
| <div class="process-step"> | |
| <span class="step-number">3</span> | |
| <span>Yarışmaları Bul</span> | |
| </div> | |
| <i class="fas fa-chevron-right step-arrow"></i> | |
| <div class="process-step"> | |
| <span class="step-number">4</span> | |
| <span>Raporları İndir</span> | |
| </div> | |
| <i class="fas fa-chevron-right step-arrow"></i> | |
| <div class="process-step"> | |
| <span class="step-number">5</span> | |
| <span>PDF'ten Proje Adı Al</span> | |
| </div> | |
| </div> | |
| <!-- Tabs --> | |
| <div class="tabs"> | |
| <button class="tab active" onclick="showTab('script')"> | |
| <i class="fas fa-code"></i> Python Script | |
| </button> | |
| <button class="tab" onclick="showTab('config')"> | |
| <i class="fas fa-cog"></i> Yapılandırma | |
| </button> | |
| <button class="tab" onclick="showTab('guide')"> | |
| <i class="fas fa-book"></i> Kullanım Kılavuzu | |
| </button> | |
| </div> | |
| <!-- Script Tab --> | |
| <div id="script" class="tab-content"> | |
| <div class="code-section"> | |
| <div class="code-header"> | |
| <div class="code-title"> | |
| <i class="fab fa-python"></i> | |
| teknofest_scraper.py | |
| </div> | |
| <button class="btn btn-primary" onclick="copyScript()"> | |
| <i class="fas fa-copy"></i> Kopyala | |
| </button> | |
| </div> | |
| <div class="code-content"> | |
| <div class="code-wrapper"> | |
| <pre id="python-script">""" | |
| Teknofest Yarışma Veri Toplama Otomasyonu | |
| ========================================== | |
| Bu script Teknofest web sitesinden İlkokul/Ortaokul kategorisindeki | |
| yarışma raporlarını otomatik olarak indirir ve proje adlarını çıkarır. | |
| Kullanım: | |
| python teknofest_scraper.py | |
| Gerekli Kütüphaneler: | |
| pip install selenium requests pytesseract pdf2image python-docx PyPDF2 | |
| """ | |
| import os | |
| import time | |
| import json | |
| import hashlib | |
| import requests | |
| from datetime import datetime | |
| from pathlib import Path | |
| # Selenium imports | |
| from selenium import webdriver | |
| from selenium.webdriver.common.by import By | |
| from selenium.webdriver.chrome.options import Options | |
| from selenium.webdriver.chrome.service import Service | |
| from selenium.webdriver.support.ui import WebDriverWait | |
| from selenium.webdriver.support import expected_conditions as EC | |
| from selenium.common.exceptions import TimeoutException, NoSuchElementException | |
| # PDF ve OCR imports | |
| from PyPDF2 import PdfReader | |
| from pdf2image import convert_from_path | |
| import pytesseract | |
| from docx import Document | |
| # ==================== KONFİGÜRASYON ==================== | |
| class Config: | |
| """Uygulama konfigürasyonu""" | |
| # Chrome Browser Path | |
| CHROME_PATH = r"C:\Program Files\Google\Chrome\Application\chrome.exe" | |
| # Tesseract OCR Path | |
| TESSERACT_PATH = r"C:\Users\mhmtx\AppData\Local\Programs\Tesseract-OCR\tesseract.exe" | |
| # Poppler Path | |
| POPPLER_PATH = r"D:\teknofest\poppler-25.12.0\Library\bin" | |
| # Çıktı Klasörü | |
| OUTPUT_BASE = r"D:\teknofest\output" | |
| # Sezon URL'leri | |
| SEASON_URLS = [ | |
| "https://www.teknofest.org/tr/season/2025", | |
| "https://www.teknofest.org/tr/season/2024", | |
| "https://www.teknofest.org/tr/season/2023", | |
| "https://www.teknofest.org/tr/season/2022", | |
| "https://www.teknofest.org/tr/season/2021", | |
| "https://www.teknofest.org/tr/season/2020", | |
| "https://www.teknofest.org/tr/season/2019", | |
| ] | |
| # Ortaokul kategori URL'si | |
| CATEGORY_URL = "https://www.teknofest.org/tr/yarismalar/#tabsCategory2" | |
| # Bekleme süreleri (saniye) | |
| PAGE_LOAD_TIMEOUT = 30 | |
| IMPLICIT_WAIT = 10 | |
| ELEMENT_WAIT = 15 | |
| # İndirme ayarları | |
| DOWNLOAD_TIMEOUT = 120 | |
| MAX_RETRIES = 3 | |
| class TeknofestScraper: | |
| """Teknofest veri toplama sınıfı""" | |
| def __init__(self): | |
| self.driver = None | |
| self.session = requests.Session() | |
| self.results = [] | |
| self.downloaded_files = [] | |
| # Klasör yapısı oluştur | |
| self.setup_folders() | |
| # Tesseract path ayarla | |
| if os.path.exists(Config.TESSERACT_PATH): | |
| pytesseract.pytesseract.tesseract_cmd = Config.TESSERACT_PATH | |
| def setup_folders(self): | |
| """Gerekli klasörleri oluştur""" | |
| Path(Config.OUTPUT_BASE).mkdir(parents=True, exist_ok=True) | |
| def init_driver(self): | |
| """Chrome driver başlat""" | |
| print("🌐 Chrome driver başlatılıyor...") | |
| chrome_options = Options() | |
| chrome_options.binary_location = Config.CHROME_PATH | |
| chrome_options.add_argument('--headless') | |
| chrome_options.add_argument('--no-sandbox') | |
| chrome_options.add_argument('--disable-dev-shm-usage') | |
| chrome_options.add_argument('--disable-gpu') | |
| chrome_options.add_argument('--window-size=1920,1080') | |
| chrome_options.add_argument('--disable-blink-features=AutomationControlled') | |
| chrome_options.add_experimental_option('excludeSwitches', ['enable-automation']) | |
| chrome_options.add_experimental_option('useAutomationExtension', False) | |
| # İndirme ayarları | |
| prefs = { | |
| "download.default_directory": Config.OUTPUT_BASE, | |
| "download.prompt_for_download": False, | |
| "download.directory_upgrade": True, | |
| "safebrowsing.enabled": True | |
| } | |
| chrome_options.add_experimental_option("prefs", prefs) | |
| try: | |
| self.driver = webdriver.Chrome(options=chrome_options) | |
| self.driver.set_page_load_timeout(Config.PAGE_LOAD_TIMEOUT) | |
| self.driver.implicitly_wait(Config.IMPLICIT_WAIT) | |
| print("✅ Chrome driver başarıyla başlatıldı") | |
| return True | |
| except Exception as e: | |
| print(f"❌ Driver başlatma hatası: {e}") | |
| return False | |
| def navigate_to_category(self, season_url): | |
| """Sezon sayfasına git ve ortaokul kategorisini seç""" | |
| try: | |
| # Ana sayfaya git | |
| self.driver.get(season_url) | |
| time.sleep(3) | |
| # Kategori sayfasına git | |
| self.driver.get(Config.CATEGORY_URL) | |
| time.sleep(3) | |
| # Ortaokul sekmesine tıkla (tabsCategory2) | |
| try: | |
| tab_button = self.driver.find_element(By.ID, "tabsCategory2") | |
| tab_button.click() | |
| time.sleep(2) | |
| print(f" ✅ Ortaokul kategorisi seçildi") | |
| except: | |
| # Alternatif selector dene | |
| tab_button = self.driver.find_element(By.CSS_SELECTOR, '[href*="tabsCategory2"]') | |
| tab_button.click() | |
| time.sleep(2) | |
| return True | |
| except Exception as e: | |
| print(f" ❌ Kategori navigasyon hatası: {e}") | |
| return False | |
| def get_competitions(self): | |
| """Yarışmaları bul""" | |
| competitions = [] | |
| try: | |
| # "yarışması" içeren başlıkları bul | |
| competition_elements = self.driver.find_elements( | |
| By.CSS_SELECTOR, | |
| "a[href*='yarismalar'], div.title:contains('yarışması'), h3:contains('yarışması')" | |
| ) | |
| for elem in competition_elements: | |
| try: | |
| title = elem.text.strip() | |
| if "yarışması" in title.lower(): | |
| href = elem.get_attribute("href") | |
| competitions.append({ | |
| "title": title, | |
| "url": href | |
| }) | |
| except: | |
| continue | |
| except Exception as e: | |
| print(f" ❌ Yarışma bulma hatası: {e}") | |
| return competitions | |
| def get_past_reports(self, competition_url, category_name): | |
| """Geçmiş yıl raporlarını getir""" | |
| reports = [] | |
| try: | |
| self.driver.get(competition_url) | |
| time.sleep(3) | |
| # "Geçmiş Yıl Raporları" sekmesini bul | |
| try: | |
| reports_tab = self.driver.find_element( | |
| By.XPATH, | |
| "//a[contains(text(),'Geçmiş Yıl Raporları')]" | |
| ) | |
| reports_tab.click() | |
| time.sleep(2) | |
| except: | |
| pass | |
| # İlkokul/Ortaokul filtresi | |
| try: | |
| filter_element = self.driver.find_element( | |
| By.XPATH, | |
| "//option[contains(text(),'İlkokul/Ortaokul')]" | |
| ) | |
| filter_element.click() | |
| time.sleep(2) | |
| except: | |
| pass | |
| # Etiket kontrolü (İnsanlık Yararına Teknoloji için) | |
| tags = [] | |
| if "İnsanlık Yararına Teknoloji" in category_name: | |
| tag_elements = self.driver.find_elements(By.CSS_SELECTOR, ".tag, .label, .badge") | |
| for tag in tag_elements: | |
| tag_text = tag.text.lower() | |
| if any(t in tag_text for t in ["afet", "inovasyon", "sağlık"]): | |
| tags.append(tag.text) | |
| # Başarılı/Finalist veya Derece kontrolü | |
| status = None | |
| try: | |
| status_elem = self.driver.find_element( | |
| By.XPATH, | |
| "//span[contains(text(),'Başarılı') or contains(text(),'Finalist') or contains(text(),'Derece')]" | |
| ) | |
| status = status_elem.text | |
| except: | |
| pass | |
| # Rapor linklerini bul | |
| report_links = self.driver.find_elements( | |
| By.CSS_SELECTOR, | |
| "a[href$='.pdf'], a[href$='.doc'], a[href$='.docx']" | |
| ) | |
| for link in report_links: | |
| try: | |
| href = link.get_attribute("href") | |
| if href: | |
| reports.append({ | |
| "url": href, | |
| "tags": tags, | |
| "status": status | |
| }) | |
| except: | |
| continue | |
| except Exception as e: | |
| print(f" ⚠️ Rapor bulma hatası: {e}") | |
| return reports | |
| def download_file(self, url, folder_path, filename=None): | |
| """Dosya indir""" | |
| try: | |
| response = self.session.get(url, stream=True, timeout=Config.DOWNLOAD_TIMEOUT) | |
| response.raise_for_status() | |
| # Dosya adını al | |
| if not filename: | |
| filename = url.split('/')[-1].split('?')[0] | |
| filepath = os.path.join(folder_path, filename) | |
| # Dosyayı kaydet | |
| with open(filepath, 'wb') as f: | |
| for chunk in response.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
| print(f" 📥 İndirildi: {filename}") | |
| return filepath | |
| except Exception as e: | |
| print(f" ❌ İndirme hatası: {e}") | |
| return None | |
| def extract_project_name_from_pdf(self, pdf_path): | |
| """PDF'ten proje adını çıkar""" | |
| project_name = None | |
| try: | |
| # PDF oku | |
| reader = PdfReader(pdf_path) | |
| first_page = reader.pages[0] | |
| text = first_page.extract_text() | |
| # Proje adını bul (heuristic yaklaşım) | |
| lines = text.split('\n') | |
| for i, line in enumerate(lines): | |
| if any(keyword in line.lower() for keyword in ['proje adı', 'proje adi', 'project name']): | |
| if i + 1 < len(lines): | |
| project_name = lines[i + 1].strip() | |
| break | |
| # Alternatif: İlk büyük metin bloğunu al | |
| if not project_name: | |
| for line in lines: | |
| if len(line) > 10 and len(line) < 100: | |
| project_name = line.strip() | |
| break | |
| except Exception as e: | |
| print(f" ⚠️ PDF okuma hatası: {e}") | |
| # OCR dene | |
| try: | |
| images = convert_from_path(pdf_path, poppler_path=Config.POPPLER_PATH) | |
| if images: | |
| text = pytesseract.image_to_string(images[0], lang='tur') | |
| lines = text.split('\n') | |
| for line in lines: | |
| if len(line) > 10 and len(line) < 100: | |
| project_name = line.strip() | |
| break | |
| except Exception as ocr_error: | |
| print(f" ⚠️ OCR hatası: {ocr_error}") | |
| return project_name | |
| def extract_project_name_from_doc(self, doc_path): | |
| """DOC/DOCX'ten proje adını çıkar""" | |
| try: | |
| doc = Document(doc_path) | |
| for para in doc.paragraphs: | |
| if len(para.text) > 10 and len(para.text) < 100: | |
| return para.text.strip() | |
| except Exception as e: | |
| print(f" ⚠️ DOC okuma hatası: {e}") | |
| return None | |
| def process_competition(self, competition, season_year): | |
| """Bir yarışmayı işle""" | |
| print(f"\n📋 İşleniyor: {competition['title']}") | |
| # Klasör yapısı oluştur | |
| # Örnek: 2025 -> İnsanlık Yararına Teknoloji -> Afet Yönetimi | |
| category_folder = self.create_folder_structure(competition, season_year) | |
| # Raporları al | |
| reports = self.get_past_reports(competition['url'], competition.get('title', '')) | |
| for report in reports: | |
| # Dosyayı indir | |
| filename = report['url'].split('/')[-1].split('?')[0] | |
| filepath = self.download_file(report['url'], category_folder, filename) | |
| if filepath and os.path.exists(filepath): | |
| # Proje adını çıkar | |
| ext = os.path.splitext(filepath)[1].lower() | |
| if ext == '.pdf': | |
| project_name = self.extract_project_name_from_pdf(filepath) | |
| elif ext in ['.doc', '.docx']: | |
| project_name = self.extract_project_name_from_doc(filepath) | |
| else: | |
| project_name = None | |
| # Dosya adını yeniden adlandır | |
| if project_name: | |
| status_label = "" | |
| if report.get('status'): | |
| if 'derece' in report['status'].lower(): | |
| status_label = " - Derece" | |
| elif 'başarılı' in report['status'].lower() or 'finalist' in report['status'].lower(): | |
| status_label = " - Başarılı/Finalist" | |
| new_filename = f"{project_name}{status_label}{ext}" | |
| new_filepath = os.path.join(category_folder, new_filename) | |
| try: | |
| os.rename(filepath, new_filepath) | |
| print(f" ✅ Yeniden adlandırıldı: {new_filename}") | |
| self.downloaded_files.append(new_filepath) | |
| except Exception as e: | |
| print(f" ⚠️ Yeniden adlandırma hatası: {e}") | |
| self.downloaded_files.append(filepath) | |
| else: | |
| self.downloaded_files.append(filepath) | |
| def create_folder_structure(self, competition, season_year): | |
| """Klasör yapısı oluştur""" | |
| # Kategori adını belirle | |
| title = competition.get('title', 'Bilinmeyen Kategori') | |
| # Alt klasör belirle | |
| subfolder = "Genel" | |
| if "İnsanlık Yararına Teknoloji" in title: | |
| if "afet" in title.lower(): | |
| subfolder = "Afet Yönetimi" | |
| elif "inovasyon" in title.lower(): | |
| subfolder = "İnovasyon" | |
| elif "sağlık" in title.lower() or "health" in title.lower(): | |
| subfolder = "Sağlık" | |
| # Klasör yolu | |
| folder_path = os.path.join(Config.OUTPUT_BASE, str(season_year), title, subfolder) | |
| Path(folder_path).mkdir(parents=True, exist_ok=True) | |
| return folder_path | |
| def run(self): | |
| """Ana çalışma fonksiyonu""" | |
| print("=" * 60) | |
| print("🚀 Teknofest Veri Toplama Otomasyonu Başladı") | |
| print("=" * 60) | |
| # Driver başlat | |
| if not self.init_driver(): | |
| print("❌ Driver başlatılamadı. Lütfen Chrome'un kurulu olduğundan emin olun.") | |
| return | |
| try: | |
| for season_url in Config.SEASON_URLS: | |
| # Sezon yılını al | |
| season_year = season_url.split('/')[-1] | |
| print(f"\n{'='*60}") | |
| print(f"📅 Sezon: {season_year}") | |
| print(f"{'='*60}") | |
| # Kategoriye git | |
| if not self.navigate_to_category(season_url): | |
| continue | |
| # Yarışmaları bul | |
| competitions = self.get_competitions() | |
| print(f" 📊 {len(competitions)} yarışma bulundu") | |
| # Her yarışmayı işle | |
| for competition in competitions: | |
| self.process_competition(competition, season_year) | |
| except KeyboardInterrupt: | |
| print("\n⚠️ İşlem kullanıcı tarafından durduruldu") | |
| except Exception as e: | |
| print(f"\n❌ Beklenmeyen hata: {e}") | |
| finally: | |
| # Driver kapat | |
| if self.driver: | |
| self.driver.quit() | |
| # Özet | |
| print(f"\n{'='*60}") | |
| print(f"📊 Özet") | |
| print(f"{'='*60}") | |
| print(f"Toplam indirilen: {len(self.downloaded_files)}") | |
| print(f"Çıktı klasörü: {Config.OUTPUT_BASE}") | |
| print("✅ İşlem tamamlandı!") | |
| if __name__ == "__main__": | |
| scraper = TeknofestScraper() | |
| scraper.run()</pre> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Config Tab --> | |
| <div id="config" class="tab-content" style="display: none;"> | |
| <div class="config-grid"> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-folder"></i> Chrome Ayarları</h3> | |
| <div class="config-item"> | |
| <span class="config-label">Chrome Path</span> | |
| <span class="config-value">C:\Program Files\Google\Chrome\Application\chrome.exe</span> | |
| </div> | |
| <div class="config-item"> | |
| <span class="config-label">Driver Mode</span> | |
| <span class="config-value">Headless</span> | |
| </div> | |
| <div class="config-item"> | |
| <span class="config-label">Download Prompt</span> | |
| <span class="config-value">Disabled</span> | |
| </div> | |
| </div> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-eye"></i> OCR Ayarları</h3> | |
| <div class="config-item"> | |
| <span class="config-label">Tesseract Path</span> | |
| <span class="config-value">C:\Users\mhmtx\AppData\Local\Programs\Tesseract-OCR</span> | |
| </div> | |
| <div class="config-item"> | |
| <span class="config-label">OCR Language</span> | |
| <span class="config-value">Turkish (tur)</span> | |
| </div> | |
| <div class="config-item"> | |
| <span class="config-label">Poppler Path</span> | |
| <span class="config-value">D:\teknofest\poppler-25.12.0\Library\bin</span> | |
| </div> | |
| </div> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-download"></i> İndirme Ayarları</h3> | |
| <div class="config-item"> | |
| <span class="config-label">Output Base</span> | |
| <span class="config-value">D:\teknofest\output</span> | |
| </div> | |
| <div class="config-item"> | |
| <span class="config-label">Timeout</span> | |
| <span class="config-value">120 seconds</span> | |
| </div> | |
| <div class="config-item"> | |
| <span class="config-label">Max Retries</span> | |
| <span class="config-value">3</span> | |
| </div> | |
| </div> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-globe"></i> Sezon URL'leri</h3> | |
| <div class="config-item"> | |
| <span class="config-label">2025</span> | |
| <span class="config-value">Active</span> | |
| </div> | |
| <div class="config-item"> | |
| <span class="config-label">2024</span> | |
| <span class="config-value">Active</span> | |
| </div> | |
| <div class="config-item"> | |
| <span class="config-label">2023-2019</span> | |
| <span class="config-value">Active</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-folder-tree"></i> Klasör Yapısı Örneği</h3> | |
| <pre style="margin-top: 1rem; color: #94a3b8; font-size: 0.85rem;">D:\teknofest\output\ | |
| ├── 2025\ | |
| │ ├── İnsanlık Yararına Teknoloji\ | |
| │ │ ├── Afet Yönetimi\ | |
| │ │ │ ├── Deprem Erken Uyarı Sistemi - Derece.pdf | |
| │ │ │ └── Acil Kurtarma Drone - Başarılı.pdf | |
| │ │ ├── İnovasyon\ | |
| │ │ │ └── Akıllı Tarım Sistemi - Derece.pdf | |
| │ │ └── Sağlık\ | |
| │ │ └── Sağlık Takip App - Finalist.pdf | |
| │ └── ... | |
| ├── 2024\ | |
| │ └── ... | |
| └── ...</pre> | |
| </div> | |
| </div> | |
| <!-- Guide Tab --> | |
| <div id="guide" class="tab-content" style="display: none;"> | |
| <div class="config-grid"> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-step-forward"></i> Adım 1: Kurulum</h3> | |
| <p style="color: #94a3b8; font-size: 0.9rem; line-height: 1.6;"> | |
| Python kütüphanelerini yükleyin:<br><br> | |
| <code style="background: rgba(0,0,0,0.3); padding: 0.25rem 0.5rem; border-radius: 4px;">pip install selenium requests pytesseract pdf2image python-docx PyPDF2</code> | |
| </p> | |
| </div> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-folder"></i> Adım 2: ChromeDriver</h3> | |
| <p style="color: #94a3b8; font-size: 0.9rem; line-height: 1.6;"> | |
| ChromeDriver'ı <a href="https://chromedriver.chromium.org/" target="_blank" style="color: var(--primary);">chromedriver.chromium.org</a> adresinden indirin ve path'e ekleyin. | |
| </p> | |
| </div> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-cog"></i> Adım 3: Tesseract OCR</h3> | |
| <p style="color: #94a3b8; font-size: 0.9rem; line-height: 1.6;"> | |
| Tesseract OCR'ı <a href="https://github.com/UB-Mannheim/tesseract/wiki" target="_blank" style="color: var(--primary);">buradan</a> indirin ve Turkish language pack ekleyin. | |
| </p> | |
| </div> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-play"></i> Adım 4: Çalıştırma</h3> | |
| <p style="color: #94a3b8; font-size: 0.9rem; line-height: 1.6;"> | |
| Scripti çalıştırın:<br><br> | |
| <code style="background: rgba(0,0,0,0.3); padding: 0.25rem 0.5rem; border-radius: 4px;">python teknofest_scraper.py</code> | |
| </p> | |
| </div> | |
| </div> | |
| <div class="config-card"> | |
| <h3><i class="fas fa-info-circle"></ |