Spaces:
Sleeping
Sleeping
| import sys | |
| import asyncio | |
| import streamlit as st | |
| from bs4 import BeautifulSoup | |
| from openai import OpenAI | |
| import json | |
| import pandas as pd | |
| import re | |
| from playwright.sync_api import sync_playwright | |
| # ========================================== | |
| # 🎨 НАСТРОЙКИ СТРАНИЦЫ И КАСТОМНЫЙ ДИЗАЙН | |
| # ========================================== | |
| st.set_page_config(page_title="AI Scraper Pro", page_icon="🕷️", layout="wide", initial_sidebar_state="expanded") | |
| st.markdown(""" | |
| <style> | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| header {visibility: hidden;} | |
| .stButton>button { | |
| border-radius: 8px; | |
| height: 3em; | |
| font-weight: bold; | |
| transition: 0.3s; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| import os | |
| import subprocess | |
| # Принудительная установка браузера ДО запуска основной логики | |
| try: | |
| import playwright | |
| # Проверяем, установлен ли браузер, если нет - ставим | |
| subprocess.run(["playwright", "install", "chromium"], check=True) | |
| except Exception as e: | |
| print(f"Установка браузера: {e}") | |
| # ========================================== | |
| # 🧠 ЛОГИКА ПАРСИНГА (PLAYWRIGHT + DEEPSEEK) | |
| # ========================================== | |
| def fetch_and_clean_page(url: str) -> str: | |
| import sys | |
| import asyncio | |
| # 1. ЖЕСТКИЙ ФИКС ДЛЯ WINDOWS (Умеет запускать подпроцессы) | |
| if sys.platform == 'win32': | |
| asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) | |
| # 2. Создаем новый цикл для потока Streamlit | |
| loop = asyncio.new_event_loop() | |
| asyncio.set_event_loop(loop) | |
| with sync_playwright() as p: | |
| browser = p.chromium.launch(headless=True) | |
| # Маскируемся под обычный Chrome на Windows | |
| context = browser.new_context( | |
| user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", | |
| viewport={'width': 1920, 'height': 1080} | |
| ) | |
| page = context.new_page() | |
| try: | |
| # Заходим на сайт и даем время прогрузиться | |
| page.goto(url, wait_until="domcontentloaded", timeout=30000) | |
| page.wait_for_timeout(3000) | |
| html = page.content() | |
| except Exception as e: | |
| st.error(f"❌ Ошибка загрузки страницы браузером: {e}") | |
| return "" | |
| finally: | |
| browser.close() | |
| # Сжимаем HTML | |
| soup = BeautifulSoup(html, 'html.parser') | |
| for element in soup(["script", "style", "noscript", "svg", "nav", "footer", "header"]): | |
| element.decompose() | |
| clean_text = soup.get_text(separator=' | ', strip=True) | |
| return clean_text[:15000] | |
| def extract_data_with_ai(text: str, user_request: str, api_key: str) -> list: | |
| """Отправляет сжатый текст в нейросеть для извлечения JSON.""" | |
| client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com") | |
| system_prompt = """Ты — универсальный экстрактор данных. | |
| Твоя задача: | |
| 1. Проанализировать запрос пользователя и понять, какие именно данные он хочет вытащить. | |
| 2. Создать массив JSON с объектами, где ключи соответствуют смыслу запроса. | |
| 3. Если в тексте есть информация, не подходящая под запрос — игнорируй её. | |
| 4. Верни СТРОГО чистый JSON массив. Без пояснений и без markdown-разметки (```json).""" | |
| user_prompt = f"Запрос: {user_request}\n\nТекст:\n{text}" | |
| try: | |
| response = client.chat.completions.create( | |
| model="deepseek-chat", | |
| messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}], | |
| temperature=0.0, | |
| response_format={"type": "json_object"} | |
| ) | |
| result_text = response.choices[0].message.content.strip() | |
| result_text = re.sub(r'^```json\s*', '', result_text) | |
| result_text = re.sub(r'\s*```$', '', result_text) | |
| data = json.loads(result_text) | |
| if isinstance(data, dict): | |
| for key, val in data.items(): | |
| if isinstance(val, list): return val | |
| return [data] | |
| return data if isinstance(data, list) else [] | |
| except Exception as e: | |
| st.error(f"❌ Ошибка ИИ (проверь API-ключ): {e}") | |
| return [] | |
| # ========================================== | |
| # 🖥️ ПОЛЬЗОВАТЕЛЬСКИЙ ИНТЕРФЕЙС (UI) | |
| # ========================================== | |
| # --- БОКОВАЯ ПАНЕЛЬ --- | |
| with st.sidebar: | |
| st.image("https://cdn-icons-png.flaticon.com/512/2103/2103837.png", width=80) | |
| st.title("⚙️ Настройки") | |
| st.markdown("Для работы парсера нужен API-ключ DeepSeek.") | |
| api_key_input = st.text_input("DeepSeek API Key", type="password", placeholder="sk-...") | |
| st.divider() | |
| st.markdown("### 💡 Как использовать:") | |
| st.markdown("1. Вставь ссылку на любой магазин.\n2. Напиши, что именно нужно найти.\n3. Скачай готовую таблицу!") | |
| st.caption("Создано тобой © 2026") | |
| # --- ГЛАВНЫЙ ЭКРАН --- | |
| st.title("🕷️ Smart AI Web Scraper") | |
| st.markdown("Извлекай структурированные данные с **любого сайта**, обходя блокировки.") | |
| with st.container(): | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| url_input = st.text_input("🌐 Ссылка на сайт", placeholder="https://...") | |
| with col2: | |
| query_input = st.text_input("🎯 Что ищем?", placeholder="Например: Собери 10 товаров, названия и цены") | |
| submit_button = st.button("🚀 ЗАПУСТИТЬ ПАРСИНГ", type="primary", use_container_width=True) | |
| if submit_button: | |
| if not api_key_input: | |
| st.warning("⚠️ Пожалуйста, введи API-ключ DeepSeek в боковой панели слева!") | |
| elif not url_input or not query_input: | |
| st.warning("⚠️ Заполни поля ссылки и запроса!") | |
| else: | |
| with st.status("Идет магия парсинга...", expanded=True) as status: | |
| st.write("🕵️♂️ Открываем невидимый браузер и обходим защиту...") | |
| text = fetch_and_clean_page(url_input) | |
| if text: | |
| st.write("🧠 ИИ анализирует текст и собирает JSON...") | |
| data = extract_data_with_ai(text, query_input, api_key_input) | |
| if data: | |
| status.update(label="✅ Данные успешно собраны!", state="complete", expanded=False) | |
| st.success("Ура! ИИ успешно структурировал информацию.") | |
| df = pd.DataFrame(data) | |
| st.dataframe(df, use_container_width=True) | |
| st.divider() | |
| col_csv, col_json = st.columns(2) | |
| with col_json: | |
| json_str = json.dumps(data, ensure_ascii=False, indent=4) | |
| st.download_button("📥 Скачать JSON", data=json_str, file_name="parsed.json", | |
| mime="application/json", use_container_width=True) | |
| with col_csv: | |
| csv_str = df.to_csv(index=False).encode('utf-8') | |
| st.download_button("📊 Скачать CSV", data=csv_str, file_name="parsed.csv", mime="text/csv", | |
| use_container_width=True) | |
| else: | |
| status.update(label="❌ ИИ не нашел данные", state="error") |