🚀 Full project sync: Hotels RAG & Audit System

 Major Features:
- Complete RAG system for hotel website analysis
- Hybrid audit with BGE-M3 embeddings + Natasha NER
- Universal horizontal Excel reports with dashboards
- Multi-region processing (SPb, Orel, Chukotka, Kamchatka)

📊 Completed Regions:
- Орловская область: 100% (36/36)
- Чукотский АО: 100% (4/4)
- г. Санкт-Петербург: 93% (893/960)
- Камчатский край: 87% (89/102)

🔧 Infrastructure:
- PostgreSQL with pgvector extension
- BGE-M3 embeddings API
- Browserless for web scraping
- N8N workflows for automation
- S3/Nextcloud file storage

📝 Documentation:
- Complete DB schemas
- API documentation
- Setup guides
- Status reports
This commit is contained in:
Фёдор
2025-10-27 22:49:42 +03:00
parent 0cf3297290
commit 684fada337
94 changed files with 14891 additions and 911 deletions

158
test_browserless_scrape.py Normal file
View File

@@ -0,0 +1,158 @@
#!/usr/bin/env python3
"""
Тест Browserless Scrape API для сравнения качества с регулярками
"""
import requests
import json
import psycopg2
from psycopg2.extras import RealDictCursor
from urllib.parse import unquote
import re
# Конфигурация БД
DB_CONFIG = {
'host': '147.45.189.234',
'port': 5432,
'database': 'default_db',
'user': 'gen_user',
'password': unquote('2~~9_%5EkVsU%3F2%5CS')
}
# Browserless API
BROWSERLESS_URL = "http://147.45.146.17:3000/function?token=9ahhnpjkchxtcho9"
def get_html_from_db():
"""Получаем HTML из БД для тестирования"""
conn = psycopg2.connect(**DB_CONFIG, cursor_factory=RealDictCursor)
cur = conn.cursor()
cur.execute("""
SELECT html, h.full_name
FROM hotel_website_raw hwr
INNER JOIN hotel_main h ON h.id = hwr.hotel_id
WHERE h.region_name = 'г. Санкт-Петербург'
AND hwr.html IS NOT NULL
LIMIT 1
""")
result = cur.fetchone()
cur.close()
conn.close()
return result['html'], result['full_name']
def clean_with_regex(html):
"""Очистка HTML регулярками (текущий метод)"""
# Удаляем script и style теги
text = re.sub(r'<script[^>]*>.*?</script>', ' ', html, flags=re.DOTALL | re.IGNORECASE)
text = re.sub(r'<style[^>]*>.*?</style>', ' ', text, flags=re.DOTALL | re.IGNORECASE)
# Удаляем все HTML теги
text = re.sub(r'<[^>]+>', ' ', text)
# Декодируем HTML entities
import html as html_module
text = html_module.unescape(text)
# Убираем лишние пробелы
text = re.sub(r'\s+', ' ', text).strip()
return text
def clean_with_browserless_scrape(html):
"""Очистка HTML через Browserless Function API"""
# JavaScript функция для извлечения текста
scrape_function = """
export default async function ({ page, context }) {
const html = context.html;
// Устанавливаем HTML в страницу
await page.setContent(html);
// Извлекаем весь текст
const text = await page.evaluate(() => {
// Удаляем script и style элементы
const scripts = document.querySelectorAll('script, style');
scripts.forEach(el => el.remove());
// Получаем весь текст
return document.body.innerText || document.body.textContent || '';
});
return {
text: text,
length: text.length
};
}
"""
payload = {
"code": scrape_function,
"context": {"html": html}
}
try:
response = requests.post(BROWSERLESS_URL, json=payload, timeout=30)
response.raise_for_status()
result = response.json()
if result and 'text' in result:
return result['text']
return ""
except Exception as e:
print(f"❌ Ошибка Browserless API: {e}")
return ""
def compare_methods():
"""Сравниваем оба метода"""
print("🔍 Получаем HTML из БД...")
html, hotel_name = get_html_from_db()
print(f"📄 Отель: {hotel_name}")
print(f"📊 Размер HTML: {len(html):,} символов")
print("\n" + "="*60)
print("🧹 МЕТОД 1: РЕГУЛЯРКИ")
print("="*60)
regex_text = clean_with_regex(html)
print(f"📏 Размер текста: {len(regex_text):,} символов")
print(f"📄 Первые 500 символов:")
print("-" * 40)
print(regex_text[:500])
print("-" * 40)
print("\n" + "="*60)
print("🌐 МЕТОД 2: BROWSERLESS SCRAPE")
print("="*60)
browserless_text = clean_with_browserless_scrape(html)
print(f"📏 Размер текста: {len(browserless_text):,} символов")
print(f"📄 Первые 500 символов:")
print("-" * 40)
print(browserless_text[:500])
print("-" * 40)
print("\n" + "="*60)
print("📊 СРАВНЕНИЕ")
print("="*60)
print(f"Регулярки: {len(regex_text):,} символов")
print(f"Browserless: {len(browserless_text):,} символов")
print(f"Разница: {len(browserless_text) - len(regex_text):,} символов")
# Анализ качества
regex_lines = regex_text.split('\n')
browserless_lines = browserless_text.split('\n')
print(f"\n📈 КАЧЕСТВО:")
print(f"Регулярки - строк: {len(regex_lines)}")
print(f"Browserless - строк: {len(browserless_lines)}")
# Подсчет пустых строк
regex_empty = sum(1 for line in regex_lines if not line.strip())
browserless_empty = sum(1 for line in browserless_lines if not line.strip())
print(f"Пустые строки (регулярки): {regex_empty}")
print(f"Пустые строки (browserless): {browserless_empty}")
if __name__ == "__main__":
compare_methods()