Király Zoltán commited on
Commit
acbf5ce
·
1 Parent(s): a245fbf

Fix: Clean up requirements.txt to resolve build conflicts

Browse files
Files changed (1) hide show
  1. web_indexer_universal_v7.py +213 -390
web_indexer_universal_v7.py CHANGED
@@ -1,6 +1,5 @@
1
  # web_indexer_universal_v7.py
2
- # JAVÍTVA: A hitelesítő adatok és a konfiguráció a GitHub Actions Secrets-ből érkeznek.
3
- # Robusztusabb logikával, a backendből importált funkciók helyett.
4
 
5
  import os
6
  import time
@@ -18,86 +17,65 @@ GREEN = '\033[92m'
18
  YELLOW = '\033[93m'
19
  RED = '\033[91m'
20
  RESET = '\033[0m'
21
- BLUE = '\033[94m'
22
  CYAN = '\033[96m'
23
- MAGENTA = '\033[95m'
24
 
25
- # --- LLM és egyéb könyvtárak ellenőrzése és importálása ---
26
  try:
27
  import torch
28
  TORCH_AVAILABLE = True
29
  except ImportError:
30
  TORCH_AVAILABLE = False
31
- print(f"{RED}FIGYELEM: Torch nincs telepítve. Egyes funkciók nem működnek.{RESET}")
32
 
33
  try:
34
  import together
35
  from dotenv import load_dotenv
36
-
37
- load_dotenv()
38
- # <<< JAVÍTVA: A together_api_key betöltése a környezeti változókból
39
  together_api_key = os.getenv("TOGETHER_API_KEY")
40
  if not together_api_key:
41
- print(f"{RED}Hiba: TOGETHER_API_KEY környezeti változó nincs beállítva. LLM funkciók nem működnek.{RESET}")
42
  together_client = None
43
  else:
44
  together_client = together.Together(api_key=together_api_key)
45
  print(f"{GREEN}Together AI kliens inicializálva.{RESET}")
46
  except ImportError:
47
- print(f"{YELLOW}Figyelem: together könyvtár nincs telepítve. LLM funkciók nem fognak működni.{RESET}")
48
- together_client = None
49
- except Exception as e:
50
- print(f"{RED}Hiba LLM backend inicializálásakor: {e}{RESET}")
51
  together_client = None
52
 
53
- # ... (a többi import változatlan)
54
  try:
55
  import tiktoken
56
  tiktoken_encoder = tiktoken.get_encoding("cl100k_base")
57
  TIKTOKEN_AVAILABLE = True
58
  except ImportError:
59
  TIKTOKEN_AVAILABLE = False
60
- print(f"{YELLOW}Figyelem: tiktoken nincs telepítve. Token darabolás a beállított karakterszámmal történik.{RESET}")
61
 
62
  try:
63
  import nltk
64
  try:
65
  nltk.data.find('tokenizers/punkt')
66
  except LookupError:
67
- print(f"{CYAN}NLTK 'punkt' letöltése...{RESET}");
68
  nltk.download('punkt', quiet=True)
69
  NLTK_AVAILABLE = True
70
  except ImportError:
71
  NLTK_AVAILABLE = False
72
- print(f"{RED}HIBA: 'nltk' nincs telepítve! Szövegtördelés nem lesz pontos.{RESET}")
73
 
74
  try:
75
  from sentence_transformers import SentenceTransformer
76
  SENTENCE_TRANSFORMER_AVAILABLE = True
77
  except ImportError:
78
  SENTENCE_TRANSFORMER_AVAILABLE = False
79
- print(f"{RED}HIBA: 'sentence-transformers' nincs telepítve! Embedding nem működik.{RESET}")
80
-
81
- try:
82
- sys.stdout.reconfigure(encoding='utf-8')
83
- sys.stderr.reconfigure(encoding='utf-8')
84
- except AttributeError:
85
- print(f"{YELLOW}Figyelem: Kódolás beállítása nem sikerült.{RESET}")
86
 
87
  # --- Konfiguráció ---
88
- # <<< JAVÍTVA: A hitelesítő adatok betöltése környezeti változókból
89
- # Ezeket a GitHub Actions Secrets-ben kell beállítanod!
90
  ES_CLOUD_ID = os.getenv("ES_CLOUD_ID")
91
  ES_API_KEY = os.getenv("ES_API_KEY")
92
- # A TOGETHER_API_KEY már korábban betöltésre került
93
 
94
  START_URL = "https://www.dunaelektronika.com/"
95
  TARGET_DOMAIN = "dunaelektronika.com"
96
  MAX_DEPTH = 2
97
  REQUEST_DELAY = 1
98
- USER_AGENT = "MyPythonCrawler/1.0 (+http://example.com/botinfo)"
99
  VECTOR_INDEX_NAME = "dunawebindexai"
100
- SYNONYM_FILE_PATH_IN_ES_CONFIG = "analysis/synonyms_hu.txt"
101
  BATCH_SIZE = 50
102
  ES_CLIENT_TIMEOUT = 120
103
  EMBEDDING_MODEL_NAME = 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2'
@@ -108,36 +86,122 @@ CHUNK_SIZE_TOKENS = 500
108
  CHUNK_OVERLAP_TOKENS = 50
109
  MIN_CHUNK_SIZE_CHARS = 50
110
  DEBUG_MODE = True
111
- LLM_MODEL_NAME = "meta-llama/Llama-3.3-70B-Instruct-Turbo-Free"
112
  LLM_CHUNK_MODEL = "mistralai/Mixtral-8x7B-Instruct-v0.1"
113
 
114
 
115
- # --- LLM HÁTTÉR FUNKCIÓK ---
116
- # ... (ez a rész változatlan)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  def generate_categories_with_llm(llm_client, soup, text):
118
- """
119
- Kategóriát generál HTML menüből/címből, vagy LLM-mel, ha az előző nem sikerül.
120
- """
121
- # Előre definiált kategórialista, hogy az LLM mindig pontosan egyező kategóriát adjon.
122
  category_list = ['IT biztonsági szolgáltatások', 'szolgáltatások', 'hardver', 'szoftver', 'hírek',
123
  'audiovizuális konferenciatechnika']
124
-
125
- # 1. Keresés HTML menüpontok/útvonalak alapján (breadcrumb)
126
  try:
127
  breadcrumb = soup.find('nav', class_='breadcrumb')
128
  if breadcrumb:
129
  categories = [li.get_text(strip=True) for li in breadcrumb.find_all('li')]
130
  if categories:
131
  final_category_from_html = categories[-1]
132
- # Ezt a kategóriát is egyeztesse a listával
133
  for cat in category_list:
134
  if cat.lower() in final_category_from_html.lower():
135
  print(f"{GREEN} -> Kategória a breadcrumb alapján: '{cat}'{RESET}")
136
  return [cat]
137
- except Exception as e:
138
- print(f"{YELLOW} Figyelem: Hiba a breadcrumb feldolgozásakor: {e}{RESET}")
139
-
140
- # 2. Keresés <h1> cím alapján
141
  try:
142
  h1_tag = soup.find('h1')
143
  if h1_tag and h1_tag.get_text(strip=True):
@@ -146,14 +210,12 @@ def generate_categories_with_llm(llm_client, soup, text):
146
  if cat.lower() in h1_text.lower():
147
  print(f"{GREEN} -> Kategória a H1 cím alapján: '{cat}'{RESET}")
148
  return [cat]
149
- except Exception as e:
150
- print(f"{YELLOW} Figyelem: Hiba a H1 cím feldolgozásakor: {e}{RESET}")
151
-
152
- # 3. LLM hívás, ha semmi más nem működik
153
  if not llm_client: return ['egyéb']
154
  try:
155
  categories_text = ", ".join([f"'{cat}'" for cat in category_list])
156
- prompt = f"""Adott egy weboldal szövege. Adj meg egyetlen, rövid kategóriát a következő listából, ami a legjobban jellemzi a tartalmát. A válaszodban csak a kategória szerepeljen, más szöveg, magyarázat, vagy írásjelek nélkül.
157
  Lehetséges kategóriák: {categories_text}
158
  Szöveg: {text[:1000]}
159
  Kategória:"""
@@ -166,8 +228,6 @@ Kategória:"""
166
  if cat.lower() in category.lower():
167
  print(f"{GREEN} -> Kategória LLM generálás alapján: '{cat}'{RESET}")
168
  return [cat]
169
- print(
170
- f"{YELLOW} -> Az LLM nem talált megfelelő kategóriát a listán. 'egyéb' kategória használata.{RESET}")
171
  return ['egyéb']
172
  else:
173
  return ["egyéb"]
@@ -176,16 +236,11 @@ Kategória:"""
176
  return ['egyéb']
177
 
178
  def generate_summary_with_llm(llm_client, text):
179
- """
180
- Összefoglalást generál a szöveg első feléből egy LLM segítségével.
181
- """
182
  if not llm_client: return text[:300] + "..."
183
  try:
184
- prompt = f"""Készíts egy rövid, de informatív összefoglalót a következő szövegről. A lényeges pontokat emeld ki, de ne lépd túl a 200 szó terjedelmet.
185
- Szöveg: {text}
186
  Összefoglalás:"""
187
- # Csak a szöveg első 4000 tokenjét használjuk, hogy elkerüljük a token limitet
188
- text_for_llm = text[:4000]
189
  response = llm_client.chat.completions.create(model=LLM_CHUNK_MODEL,
190
  messages=[{"role": "user", "content": prompt}], temperature=0.5,
191
  max_tokens=500)
@@ -193,26 +248,18 @@ Szöveg: {text}
193
  summary = response.choices[0].message.content.strip()
194
  print(f"{GREEN} -> Sikeres LLM összefoglalás generálás.{RESET}")
195
  return summary
196
- else:
197
- return text[:300] + "..."
198
  except Exception as e:
199
  print(f"{RED}Hiba LLM összefoglaláskor: {e}{RESET}")
200
- return text[:300] + "..." # Visszaesés a manuális csonkolásra hiba esetén
201
 
202
  def chunk_text_by_tokens(text, chunk_size, chunk_overlap):
203
- """
204
- Szöveg feldarabolása tokenek szerint, átfedéssel.
205
- """
206
- if not TIKTOKEN_AVAILABLE or not NLTK_AVAILABLE:
207
- # Fallback a karakterszám alapú darabolásra, ha a tokenizáló nincs telepítve
208
- print(f"{YELLOW}Figyelmeztetés: Tiktoken/NLTK hiányzik. Karakterszám alapú darabolás.{RESET}")
209
- # Egyszerű karakter alapú tördelés
210
  chunks = []
211
  start = 0
212
  while start < len(text):
213
- end = start + chunk_size
214
  chunks.append(text[start:end])
215
- start += chunk_size - chunk_overlap
216
  return chunks
217
 
218
  tokens = tiktoken_encoder.encode(text)
@@ -225,162 +272,27 @@ def chunk_text_by_tokens(text, chunk_size, chunk_overlap):
225
  start += chunk_size - chunk_overlap
226
  return chunks
227
 
228
- # --- Modellek és Eszközök Inicializálása ---
229
- # ... (ez a rész változatlan)
230
- def load_embedding_model():
231
- global embedding_model, EMBEDDING_DIM, device
232
- if not TORCH_AVAILABLE or not SENTENCE_TRANSFORMER_AVAILABLE:
233
- EMBEDDING_DIM = 768
234
- device = 'cpu'
235
- print(f"{RED}Hiba: PyTorch vagy SentenceTransformer nincs telepítve.{RESET}")
236
- return None, EMBEDDING_DIM, device
237
- if embedding_model and EMBEDDING_DIM:
238
- return embedding_model, EMBEDDING_DIM, device
239
- print(f"\n'{EMBEDDING_MODEL_NAME}' embedding modell betöltése (SentenceTransformer)...")
240
- try:
241
- current_device = 'cuda' if torch.cuda.is_available() else 'cpu'
242
- model = SentenceTransformer(EMBEDDING_MODEL_NAME, device=current_device)
243
- print(f"ST modell betöltve, használt eszköz: {model.device}")
244
- dim = model.get_sentence_embedding_dimension()
245
- if not dim: raise ValueError("Nem sikerült meghatározni az embedding dimenziót.")
246
- embedding_model = model
247
- EMBEDDING_DIM = dim
248
- device = current_device
249
- return embedding_model, EMBEDDING_DIM, device
250
- except Exception as e:
251
- print(f"{RED}Hiba embedding modell betöltésekor: {e}{RESET}")
252
- traceback.print_exc()
253
- embedding_model = None
254
- EMBEDDING_DIM = 768
255
- device = 'cpu'
256
- return None, EMBEDDING_DIM, device
257
-
258
- embedding_model, EMBEDDING_DIM, device = load_embedding_model()
259
-
260
- # === Index Beállítások & Mapping ===
261
- # ... (ez a rész változatlan)
262
- INDEX_SETTINGS_SEPARATE_ANALYZER = {
263
- "analysis": {
264
- "filter": {
265
- "hungarian_stop": {"type": "stop", "stopwords": "_hungarian_"},
266
- "hungarian_stemmer": {"type": "stemmer", "language": "hungarian"},
267
- "synonym_filter": {"type": "synonym_graph", "synonyms_path": SYNONYM_FILE_PATH_IN_ES_CONFIG,
268
- "updateable": True}
269
- },
270
- "analyzer": {
271
- "hungarian_indexing_analyzer": {"tokenizer": "standard",
272
- "filter": ["lowercase", "hungarian_stop", "hungarian_stemmer"]},
273
- "hungarian_search_analyzer": {"tokenizer": "standard",
274
- "filter": ["lowercase", "hungarian_stop", "synonym_filter",
275
- "hungarian_stemmer"]}
276
- }
277
- }
278
- }
279
-
280
- INDEX_MAPPINGS_WEB = {
281
- "properties": {
282
- "text_content": {"type": "text", "analyzer": "hungarian_indexing_analyzer",
283
- "search_analyzer": "hungarian_search_analyzer"},
284
- "embedding": {"type": "dense_vector", "dims": EMBEDDING_DIM, "index": True, "similarity": "cosine"},
285
- "source_origin": {"type": "keyword"},
286
- "source_url": {"type": "keyword"},
287
- "source_type": {"type": "keyword"},
288
- "category": {"type": "keyword"},
289
- "heading": {"type": "text", "analyzer": "hungarian_indexing_analyzer",
290
- "search_analyzer": "hungarian_search_analyzer"},
291
- "summary": {"type": "text", "analyzer": "hungarian_indexing_analyzer",
292
- "search_analyzer": "hungarian_search_analyzer"}
293
- }
294
- }
295
-
296
-
297
- # --- Segédfüggvények ---
298
- # <<< JAVÍTVA: A függvény most már a környezeti változókat használja
299
- def initialize_es_client():
300
- if DEBUG_MODE: print("\nKapcsolódás az Elasticsearch-hez a GitHub Secrets adatokkal...")
301
-
302
- # Ellenőrizzük, hogy a szükséges környezeti változók be vannak-e állítva
303
- if not ES_CLOUD_ID:
304
- print(f"{RED}Hiba: ES_CLOUD_ID környezeti változó hiányzik! Ezt a GitHub Secrets-ben kell beállítani.{RESET}")
305
- return None
306
- if not ES_API_KEY:
307
- print(f"{RED}Hiba: ES_API_KEY környezeti változó hiányzik! Ezt a GitHub Secrets-ben kell beállítani.{RESET}")
308
- return None
309
-
310
- client = None
311
- try:
312
- # A kliens inicializálása cloud_id és api_key segítségével
313
- client = Elasticsearch(
314
- cloud_id=ES_CLOUD_ID,
315
- api_key=ES_API_KEY,
316
- request_timeout=ES_CLIENT_TIMEOUT
317
- )
318
- if not client.ping():
319
- raise ConnectionError("Nem sikerült pingelni az Elasticsearch-t.")
320
- if DEBUG_MODE: print(f"{GREEN}Sikeres Elasticsearch kapcsolat!{RESET}")
321
- return client
322
- except Exception as e:
323
- print(f"{RED}Hiba az Elasticsearch kapcsolódás során: {e}{RESET}")
324
- traceback.print_exc()
325
- return None
326
-
327
- # ... (a többi segédfüggvény változatlan)
328
  def get_embedding(text):
329
  if not embedding_model: return None
330
  if not text or not isinstance(text, str): return None
331
  try:
332
- vector = embedding_model.encode(text, normalize_embeddings=True)
333
- return vector.tolist()
334
  except Exception as e:
335
  print(f"{RED}Hiba embedding közben: {e}{RESET}")
336
  return None
337
 
338
-
339
  def create_es_index(client, index_name, index_settings, index_mappings):
340
- if not EMBEDDING_DIM:
341
- print(f"{RED}Hiba: Embed dim nincs.{RESET}")
342
- return False
343
- try:
344
- embedding_mapping = index_mappings.get("properties", {}).get("embedding", {})
345
- if not embedding_mapping: raise KeyError("Az 'embedding' kulcs hiányzik a mapping 'properties'-ből!")
346
- if embedding_mapping.get("dims") != EMBEDDING_DIM:
347
- print(f"{YELLOW}FIGYELEM: Mapping dim != Modell dim. Mapping frissítése {EMBEDDING_DIM}-re.{RESET}")
348
- index_mappings["properties"]["embedding"]["dims"] = EMBEDDING_DIM
349
- except KeyError as e:
350
- print(f"{RED}Hiba: Mapping struktúra érvénytelen! Kulcs: {e}{RESET}")
351
- return False
352
- except Exception as e:
353
- print(f"{RED}Hiba: Mapping hiba! {e}{RESET}")
354
- return False
355
- if DEBUG_MODE: print(f"\nIndex check: '{index_name}'?")
356
  try:
357
  if not client.indices.exists(index=index_name):
358
- print(f"'{index_name}' létrehozása...")
359
- resp = client.indices.create(index=index_name, settings=index_settings, mappings=index_mappings,
360
- request_timeout=ES_CLIENT_TIMEOUT)
361
- if resp.get('acknowledged'):
362
- print(f"{GREEN}Index OK.{RESET}")
363
- time.sleep(2)
364
- return True
365
- else:
366
- print(f"{RED}Hiba: Create no ack.{RESET}")
367
- return False
368
  else:
369
  print(f"Index '{index_name}' már létezik.")
370
- return True
371
- except es_exceptions.RequestError as e:
372
- err_str = str(e).lower()
373
- if 'resource_already_exists_exception' in err_str:
374
- if DEBUG_MODE: print("Index már létezik (exception).")
375
- return True
376
- elif 'resource_not_found_exception' in err_str and ('synonyms_path' in err_str or (
377
- SYNONYM_FILE_PATH_IN_ES_CONFIG and SYNONYM_FILE_PATH_IN_ES_CONFIG.split('/')[-1] in err_str)):
378
- print(f"{RED}!!! Hiba: Szinonima fájl nincs ES-ben: '{SYNONYM_FILE_PATH_IN_ES_CONFIG}'?{RESET}")
379
- else:
380
- print(f"{RED}!!! Hiba index create (RequestError): {e}{RESET}")
381
- return False
382
  except Exception as e:
383
- print(f"{RED}!!! Hiba index check/create: {e}{RESET}")
384
  traceback.print_exc()
385
  return False
386
 
@@ -392,224 +304,135 @@ def extract_text_from_html(html_content):
392
  main_content = soup.find('main') or soup.find('article') or soup.body
393
  if main_content:
394
  text = main_content.get_text(separator='\n', strip=True)
395
- cleaned_text = "\n".join(line for line in text.splitlines() if line.strip())
396
- return cleaned_text
397
- else:
398
- return ""
399
  except Exception as e:
400
- print(f"{RED}Hiba HTML elemzés: {e}{RESET}")
401
- return ""
402
-
403
 
404
  def extract_and_filter_links(soup, base_url, target_domain):
405
  links = set()
406
- try:
407
- for a_tag in soup.find_all('a', href=True):
408
- href = a_tag['href'].strip()
409
- if href and not href.startswith(('#', 'mailto:', 'javascript:')):
410
- full_url = urljoin(base_url, href)
411
- parsed_url = urlparse(full_url)
412
- if parsed_url.scheme in ['http', 'https'] and parsed_url.netloc == target_domain:
413
- normalized_url = parsed_url._replace(fragment="").geturl()
414
- links.add(normalized_url)
415
- except Exception as e:
416
- print(f"{RED}Hiba link kinyerés: {e}{RESET}")
417
  return links
418
 
419
  def crawl_and_index_website(start_url, max_depth, es_client, index_name):
420
- if not es_client: print(f"{RED}Hiba: ES kliens nincs init.{RESET}"); return 0
421
- if not embedding_model: print(f"{RED}Hiba: Embedding modell nincs init.{RESET}"); return 0
422
- try:
423
- import requests;
424
- from bs4 import BeautifulSoup;
425
- from urllib.parse import urljoin, \
426
- urlparse;
427
- from collections import deque
428
- except ImportError:
429
- print(f"{RED}Hiba: Crawling könyvtárak hiányoznak.{RESET}");
430
- return 0
431
-
432
- global together_client, BATCH_SIZE, CHUNK_SIZE_TOKENS, CHUNK_OVERLAP_TOKENS, MIN_CHUNK_SIZE_CHARS
433
-
434
  visited_urls = set()
435
  urls_to_visit = deque([(start_url, 0)])
436
  bulk_actions = []
437
- total_prepared = 0;
438
  total_indexed = 0
439
- try:
440
- target_domain = urlparse(start_url).netloc;
441
- except Exception as url_err:
442
- print(f"{RED}Hiba: Start URL feldolgozása ({start_url}): {url_err}{RESET}");
443
- return 0
444
  print(f"Web crawling indítása: {start_url} (Max mélység: {max_depth}, Cél: {target_domain})")
445
 
446
  while urls_to_visit:
447
- current_url = None
 
 
 
 
 
 
448
  try:
449
- current_url, current_depth = urls_to_visit.popleft()
450
- try:
451
- parsed_check = urlparse(current_url);
452
- except Exception as parse_err:
453
- print(f" {YELLOW}-> Hibás URL formátum, kihagyva: {current_url}{RESET}");
454
- continue
455
- if current_url in visited_urls: continue
456
- if current_depth > max_depth: continue
457
- print(f"\n--- Feldolgozás (Mélység: {current_depth}): {current_url} ---")
458
- visited_urls.add(current_url)
459
- html_content = None
460
- try:
461
- headers = {'User-Agent': USER_AGENT}
462
- response = requests.get(current_url, headers=headers, timeout=15)
463
- response.raise_for_status()
464
- content_type = response.headers.get('content-type', '').lower()
465
- if 'text/html' not in content_type: print(
466
- f" {YELLOW}-> Nem HTML ({content_type}), kihagyva.{RESET}"); continue
467
- html_content = response.content
468
- except requests.exceptions.RequestException as req_err:
469
- print(f" {RED}!!! Hiba letöltés: {req_err}{RESET}");
470
  continue
471
- except Exception as fetch_err:
472
- print(f" {RED}!!! Váratlan hiba letöltés: {fetch_err}{RESET}");
473
- continue
474
-
475
- if DEBUG_MODE: print(" HTML elemzése és kategorizálása...")
476
  soup = BeautifulSoup(html_content, 'html.parser')
477
  page_text = extract_text_from_html(html_content)
 
478
  if not page_text or len(page_text) < MIN_CHUNK_SIZE_CHARS:
479
- print(f" {YELLOW}-> Nem sikerült szöveget kinyerni vagy túl rövid.{RESET}");
480
  continue
481
-
482
- # --- ÚJ, JAVÍTOTT LOGIKA ---
483
- # 1. Szövegtördelés token alapján
484
  final_chunks = chunk_text_by_tokens(page_text, CHUNK_SIZE_TOKENS, CHUNK_OVERLAP_TOKENS)
485
- chunk_type = "token_chunking"
486
-
487
- # 2. LLM-alapú kategorizálás (az első 1000 karakter alapján)
488
- url_category = generate_categories_with_llm(together_client, soup, page_text[:1000])[0]
489
-
490
- # 3. LLM-alapú összefoglalás a teljes oldalhoz, ha van LLM kliens
491
  page_summary = generate_summary_with_llm(together_client, page_text)
492
-
493
- # --- INDEXELÉS ELŐKÉSZÍTÉSE ---
494
- if final_chunks:
495
- print(
496
- f"{GREEN} Indexelendő chunkok: {len(final_chunks)} (Típus: {chunk_type}, Kategória: {url_category}){RESET}")
497
- else:
498
- print(f"{RED} Nincs indexelendő chunk ({chunk_type}).{RESET}");
499
- continue
500
-
501
- if DEBUG_MODE: print(f" Chunkok indexelésének előkészítése...")
502
- page_chunk_count = 0
503
  for chunk_text in final_chunks:
504
  element_vector = get_embedding(chunk_text)
505
  if element_vector:
506
- total_prepared += 1;
507
- page_chunk_count += 1
508
- doc = {"text_content": chunk_text, "embedding": element_vector, "source_origin": "website",
509
- "source_url": current_url, "source_type": chunk_type, "category": url_category,
510
- "summary": page_summary}
 
 
 
 
511
  bulk_actions.append({"_index": index_name, "_source": doc})
512
- if len(bulk_actions) >= BATCH_SIZE:
513
- if DEBUG_MODE: print(f" -> {len(bulk_actions)} web chunk indexelése (batch)...")
514
- try:
515
- success_count, errors = helpers.bulk(es_client, bulk_actions, raise_on_error=False,
516
- request_timeout=ES_CLIENT_TIMEOUT)
517
- total_indexed += success_count
518
- if errors: print(f"{RED}!!! Hiba web bulk: {len(errors)} sikertelen.{RESET}")
519
- except Exception as be:
520
- print(f"{RED}!!! Váratlan web bulk hiba: {be}{RESET}")
521
- finally:
522
- bulk_actions = []
523
- print(f" Oldal ({current_url}) feldolgozása kész ({page_chunk_count} chunk indexelve).")
524
  if current_depth < max_depth:
525
- if DEBUG_MODE: print(" Linkek keresése...")
526
- try:
527
- soup_for_links = BeautifulSoup(html_content, 'html.parser')
528
- new_links = extract_and_filter_links(soup_for_links, current_url, target_domain)
529
- if DEBUG_MODE: print(f" Talált {len(new_links)} új, belső linket.")
530
- for link in new_links:
531
- if link not in visited_urls:
532
- if len(urls_to_visit) < 5000:
533
- urls_to_visit.append((link, current_depth + 1))
534
- else:
535
- print(
536
- f"{YELLOW}Figyelmeztetés: A bejárási sor túl hosszú, új link kihagyva: {link}{RESET}")
537
- except Exception as link_err:
538
- print(f"{RED}!!! Hiba linkek kinyerése: {link_err}{RESET}")
539
- if DEBUG_MODE: print(f" Várakozás {REQUEST_DELAY} mp..."); time.sleep(REQUEST_DELAY)
540
- except KeyboardInterrupt:
541
- print("\nFolyamat megszakítva.");
542
- break
543
- except Exception as loop_err:
544
- print(f"{RED}!!! Hiba ciklusban ({current_url}): {loop_err}{RESET}");
545
- traceback.print_exc();
546
- time.sleep(5)
547
 
548
  if bulk_actions:
549
- if DEBUG_MODE: print(f" -> Maradék {len(bulk_actions)} web chunk indexelése...")
550
- try:
551
- success_count, errors = helpers.bulk(es_client, bulk_actions, raise_on_error=False,
552
- request_timeout=ES_CLIENT_TIMEOUT)
553
- total_indexed += success_count
554
- if errors: print(f"{RED}!!! Hiba maradék web bulk: {len(errors)} sikertelen.{RESET}")
555
- except Exception as be:
556
- print(f"{RED}!!! Maradék web bulk hiba: {be}{RESET}")
557
 
558
  print(f"\n--- Web Crawling és Indexelés Befejezve ---")
559
  print(f"Meglátogatott URL-ek: {len(visited_urls)}")
560
- print(f"Előkészített chunk: {total_prepared}")
561
- final_success = min(total_indexed, total_prepared)
562
- print(f"Sikeresen indexelt chunk: {final_success}")
563
- return final_success
564
 
565
- # ... (a main függvény változatlan)
566
  if __name__ == "__main__":
567
- print(f"----- Web Crawler és Indexelő Indítása a '{VECTOR_INDEX_NAME}' indexbe (LLM-alapú) -----")
568
- print(f"----- Cél URL: {START_URL} (Max mélység: {MAX_DEPTH}) -----")
569
- print("****** FIGYELEM ******")
570
- print(f"Ez a script létrehozza/használja a '{VECTOR_INDEX_NAME}' indexet.")
571
- print(f"Győződj meg róla, hogy a szinonima beállítások az ES-ben léteznek ehhez az indexhez.")
572
- print(f"{RED}Ha a '{VECTOR_INDEX_NAME}' index már létezik, TÖRÖLD manuálisan futtatás előtt!{RESET}")
573
- print("SZÜKSÉGES KÖNYVTÁRAK: requests, beautifulsoup4, nltk, tiktoken, together stb.")
574
- print("******------------******")
575
-
576
- if not TORCH_AVAILABLE: print(f"{RED}Hiba: PyTorch.{RESET}"); exit(1)
577
- if not SENTENCE_TRANSFORMER_AVAILABLE: print(f"{RED}Hiba: SentenceTransformer.{RESET}"); exit(1)
578
- if not embedding_model: print(f"{RED}Hiba: Embedding modell.{RESET}"); exit(1)
579
- if not EMBEDDING_DIM: print(f"{RED}Hiba: Embedding dim.{RESET}"); exit(1)
580
- try:
581
- import requests;
582
- from bs4 import BeautifulSoup;
583
- from urllib.parse import urljoin, \
584
- urlparse;
585
- from collections import deque
586
- except ImportError:
587
- print(f"{RED}Hiba: Crawling könyvtárak.{RESET}");
588
  exit(1)
589
 
590
  es_client = initialize_es_client()
591
- final_success_count = 0
592
- index_ready = False
593
  if es_client:
594
- index_ready = create_es_index(client=es_client, index_name=VECTOR_INDEX_NAME,
595
- index_settings=INDEX_SETTINGS_SEPARATE_ANALYZER,
596
- index_mappings=INDEX_MAPPINGS_WEB)
 
 
 
 
 
 
 
 
 
597
  if index_ready:
598
  print(f"\nIndex '{VECTOR_INDEX_NAME}' kész. Web crawling és indexelés indítása...")
599
  final_success_count = crawl_and_index_website(START_URL, MAX_DEPTH, es_client, VECTOR_INDEX_NAME)
 
 
 
 
600
  else:
601
- print(f"{RED}Hiba: Index nem áll készen.{RESET}")
602
- else:
603
- print(f"{RED}Hiba: ES kliens nem elérhető.{RESET}")
604
- print("\n----- Feldolgozás Befejezve -----")
605
- if index_ready and final_success_count > 0:
606
- print(
607
- f"\n{GREEN}Crawling és indexelés sikeres. {final_success_count} chunk indexelve '{VECTOR_INDEX_NAME}'-be.{RESET}");
608
- print(f"Ellenőrzés: GET /{VECTOR_INDEX_NAME}/_count");
609
- print(f"\nFontos: A RAG scriptet módosítani kell, hogy '{VECTOR_INDEX_NAME}' indexben IS keressen.")
610
- elif index_ready and final_success_count == 0:
611
- print(f"{YELLOW}Crawling lefutott, de 0 chunk indexelve.{RESET}")
612
- elif not index_ready:
613
- print(f"{RED}Index nem jött létre.{RESET}")
614
  else:
615
- print(f"{RED}Folyamat hibával zárult.{RESET}")
 
1
  # web_indexer_universal_v7.py
2
+ # VÉGLEGES VERZIÓ: GitHub Secrets integrációval és a feltöltött szinonima készlet használatával.
 
3
 
4
  import os
5
  import time
 
17
  YELLOW = '\033[93m'
18
  RED = '\033[91m'
19
  RESET = '\033[0m'
 
20
  CYAN = '\033[96m'
 
21
 
22
+ # --- Könyvtárak importálása ---
23
  try:
24
  import torch
25
  TORCH_AVAILABLE = True
26
  except ImportError:
27
  TORCH_AVAILABLE = False
28
+ print(f"{RED}FIGYELEM: Torch nincs telepítve.{RESET}")
29
 
30
  try:
31
  import together
32
  from dotenv import load_dotenv
33
+ load_dotenv() # Helyi fejlesztéshez (.env fájl)
 
 
34
  together_api_key = os.getenv("TOGETHER_API_KEY")
35
  if not together_api_key:
36
+ print(f"{YELLOW}Figyelem: TOGETHER_API_KEY környezeti változó nincs beállítva. LLM funkciók nem működnek.{RESET}")
37
  together_client = None
38
  else:
39
  together_client = together.Together(api_key=together_api_key)
40
  print(f"{GREEN}Together AI kliens inicializálva.{RESET}")
41
  except ImportError:
 
 
 
 
42
  together_client = None
43
 
 
44
  try:
45
  import tiktoken
46
  tiktoken_encoder = tiktoken.get_encoding("cl100k_base")
47
  TIKTOKEN_AVAILABLE = True
48
  except ImportError:
49
  TIKTOKEN_AVAILABLE = False
 
50
 
51
  try:
52
  import nltk
53
  try:
54
  nltk.data.find('tokenizers/punkt')
55
  except LookupError:
56
+ print(f"{CYAN}NLTK 'punkt' letöltése...{RESET}")
57
  nltk.download('punkt', quiet=True)
58
  NLTK_AVAILABLE = True
59
  except ImportError:
60
  NLTK_AVAILABLE = False
 
61
 
62
  try:
63
  from sentence_transformers import SentenceTransformer
64
  SENTENCE_TRANSFORMER_AVAILABLE = True
65
  except ImportError:
66
  SENTENCE_TRANSFORMER_AVAILABLE = False
 
 
 
 
 
 
 
67
 
68
  # --- Konfiguráció ---
69
+ # Adatok betöltése környezeti változókból (a GitHub Actions a Secrets-ből adja át)
 
70
  ES_CLOUD_ID = os.getenv("ES_CLOUD_ID")
71
  ES_API_KEY = os.getenv("ES_API_KEY")
 
72
 
73
  START_URL = "https://www.dunaelektronika.com/"
74
  TARGET_DOMAIN = "dunaelektronika.com"
75
  MAX_DEPTH = 2
76
  REQUEST_DELAY = 1
77
+ USER_AGENT = "MyPythonCrawler/1.0"
78
  VECTOR_INDEX_NAME = "dunawebindexai"
 
79
  BATCH_SIZE = 50
80
  ES_CLIENT_TIMEOUT = 120
81
  EMBEDDING_MODEL_NAME = 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2'
 
86
  CHUNK_OVERLAP_TOKENS = 50
87
  MIN_CHUNK_SIZE_CHARS = 50
88
  DEBUG_MODE = True
 
89
  LLM_CHUNK_MODEL = "mistralai/Mixtral-8x7B-Instruct-v0.1"
90
 
91
 
92
+ # === Index Beállítások & Mapping ===
93
+ # <<< JAVÍTVA: Ez a verzió már a Kibana-ban létrehozott "synonyms-hu" készletre hivatkozik
94
+ INDEX_SETTINGS_SEPARATE_ANALYZER = {
95
+ "analysis": {
96
+ "filter": {
97
+ "hungarian_stop": {"type": "stop", "stopwords": "_hungarian_"},
98
+ "hungarian_stemmer": {"type": "stemmer", "language": "hungarian"},
99
+ "synonym_filter": {
100
+ "type": "synonym_graph",
101
+ "synonyms_set": "synonyms-hu" # Hivatkozás a feltöltött szinonima készletre
102
+ }
103
+ },
104
+ "analyzer": {
105
+ "hungarian_indexing_analyzer": {
106
+ "tokenizer": "standard",
107
+ "filter": ["lowercase", "hungarian_stop", "hungarian_stemmer"]
108
+ },
109
+ "hungarian_search_analyzer": {
110
+ "tokenizer": "standard",
111
+ "filter": ["lowercase", "hungarian_stop", "synonym_filter", "hungarian_stemmer"]
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ INDEX_MAPPINGS_WEB = {
118
+ "properties": {
119
+ "text_content": {"type": "text", "analyzer": "hungarian_indexing_analyzer", "search_analyzer": "hungarian_search_analyzer"},
120
+ "embedding": {"type": "dense_vector", "dims": 768, "index": True, "similarity": "cosine"}, # Dimenziót betöltés után frissítjük
121
+ "source_origin": {"type": "keyword"},
122
+ "source_url": {"type": "keyword"},
123
+ "source_type": {"type": "keyword"},
124
+ "category": {"type": "keyword"},
125
+ "heading": {"type": "text", "analyzer": "hungarian_indexing_analyzer", "search_analyzer": "hungarian_search_analyzer"},
126
+ "summary": {"type": "text", "analyzer": "hungarian_indexing_analyzer", "search_analyzer": "hungarian_search_analyzer"}
127
+ }
128
+ }
129
+
130
+
131
+ # --- Segédfüggvények ---
132
+ def initialize_es_client():
133
+ if DEBUG_MODE: print("\nKapcsolódás az Elasticsearch-hez a GitHub Secrets adatokkal...")
134
+
135
+ if not ES_CLOUD_ID:
136
+ print(f"{RED}Hiba: ES_CLOUD_ID környezeti változó hiányzik! Ezt a GitHub Secrets-ben kell beállítani.{RESET}")
137
+ return None
138
+ if not ES_API_KEY:
139
+ print(f"{RED}Hiba: ES_API_KEY környezeti változó hiányzik! Ezt a GitHub Secrets-ben kell beállítani.{RESET}")
140
+ return None
141
+
142
+ try:
143
+ client = Elasticsearch(
144
+ cloud_id=ES_CLOUD_ID,
145
+ api_key=ES_API_KEY,
146
+ request_timeout=ES_CLIENT_TIMEOUT
147
+ )
148
+ if not client.ping():
149
+ raise ConnectionError("Nem sikerült pingelni az Elasticsearch-t.")
150
+ if DEBUG_MODE: print(f"{GREEN}Sikeres Elasticsearch kapcsolat!{RESET}")
151
+ return client
152
+ except Exception as e:
153
+ print(f"{RED}Hiba az Elasticsearch kapcsolódás során: {e}{RESET}")
154
+ traceback.print_exc()
155
+ return None
156
+
157
+ def load_embedding_model():
158
+ global embedding_model, EMBEDDING_DIM, device
159
+ if not TORCH_AVAILABLE or not SENTENCE_TRANSFORMER_AVAILABLE:
160
+ EMBEDDING_DIM = 768
161
+ device = 'cpu'
162
+ print(f"{RED}Hiba: PyTorch vagy SentenceTransformer nincs telepítve.{RESET}")
163
+ return None, EMBEDDING_DIM, device
164
+
165
+ print(f"\n'{EMBEDDING_MODEL_NAME}' embedding modell betöltése (SentenceTransformer)...")
166
+ try:
167
+ current_device = 'cuda' if torch.cuda.is_available() else 'cpu'
168
+ model = SentenceTransformer(EMBEDDING_MODEL_NAME, device=current_device)
169
+ print(f"ST modell betöltve, használt eszköz: {model.device}")
170
+ dim = model.get_sentence_embedding_dimension()
171
+ if not dim: raise ValueError("Nem sikerült meghatározni az embedding dimenziót.")
172
+ embedding_model = model
173
+ EMBEDDING_DIM = dim
174
+ device = current_device
175
+ # Dinamikusan frissítjük a mappinget a modell dimenziójával
176
+ INDEX_MAPPINGS_WEB["properties"]["embedding"]["dims"] = dim
177
+ return embedding_model, EMBEDDING_DIM, device
178
+ except Exception as e:
179
+ print(f"{RED}Hiba embedding modell betöltésekor: {e}{RESET}")
180
+ traceback.print_exc()
181
+ embedding_model = None
182
+ EMBEDDING_DIM = 768
183
+ device = 'cpu'
184
+ return None, EMBEDDING_DIM, device
185
+
186
+ # ... A többi függvény (generate_categories_with_llm, get_embedding, create_es_index, stb.)
187
+ # az eredeti formájában maradhat, mivel azok már helyesen működnek.
188
+ # Itt beillesztem őket a teljesség kedvéért.
189
+
190
  def generate_categories_with_llm(llm_client, soup, text):
 
 
 
 
191
  category_list = ['IT biztonsági szolgáltatások', 'szolgáltatások', 'hardver', 'szoftver', 'hírek',
192
  'audiovizuális konferenciatechnika']
 
 
193
  try:
194
  breadcrumb = soup.find('nav', class_='breadcrumb')
195
  if breadcrumb:
196
  categories = [li.get_text(strip=True) for li in breadcrumb.find_all('li')]
197
  if categories:
198
  final_category_from_html = categories[-1]
 
199
  for cat in category_list:
200
  if cat.lower() in final_category_from_html.lower():
201
  print(f"{GREEN} -> Kategória a breadcrumb alapján: '{cat}'{RESET}")
202
  return [cat]
203
+ except Exception:
204
+ pass
 
 
205
  try:
206
  h1_tag = soup.find('h1')
207
  if h1_tag and h1_tag.get_text(strip=True):
 
210
  if cat.lower() in h1_text.lower():
211
  print(f"{GREEN} -> Kategória a H1 cím alapján: '{cat}'{RESET}")
212
  return [cat]
213
+ except Exception:
214
+ pass
 
 
215
  if not llm_client: return ['egyéb']
216
  try:
217
  categories_text = ", ".join([f"'{cat}'" for cat in category_list])
218
+ prompt = f"""Adott egy weboldal szövege. Adj meg egyetlen, rövid kategóriát a következő listából, ami a legjobban jellemzi a tartalmát. A válaszodban csak a kategória szerepeljen, más szöveg nélkül.
219
  Lehetséges kategóriák: {categories_text}
220
  Szöveg: {text[:1000]}
221
  Kategória:"""
 
228
  if cat.lower() in category.lower():
229
  print(f"{GREEN} -> Kategória LLM generálás alapján: '{cat}'{RESET}")
230
  return [cat]
 
 
231
  return ['egyéb']
232
  else:
233
  return ["egyéb"]
 
236
  return ['egyéb']
237
 
238
  def generate_summary_with_llm(llm_client, text):
 
 
 
239
  if not llm_client: return text[:300] + "..."
240
  try:
241
+ prompt = f"""Készíts egy rövid, de informatív összefoglalót a következő szövegről magyarul.
242
+ Szöveg: {text[:4000]}
243
  Összefoglalás:"""
 
 
244
  response = llm_client.chat.completions.create(model=LLM_CHUNK_MODEL,
245
  messages=[{"role": "user", "content": prompt}], temperature=0.5,
246
  max_tokens=500)
 
248
  summary = response.choices[0].message.content.strip()
249
  print(f"{GREEN} -> Sikeres LLM összefoglalás generálás.{RESET}")
250
  return summary
 
 
251
  except Exception as e:
252
  print(f"{RED}Hiba LLM összefoglaláskor: {e}{RESET}")
253
+ return text[:300] + "..."
254
 
255
  def chunk_text_by_tokens(text, chunk_size, chunk_overlap):
256
+ if not TIKTOKEN_AVAILABLE:
 
 
 
 
 
 
257
  chunks = []
258
  start = 0
259
  while start < len(text):
260
+ end = start + (chunk_size * 4) # Approximation
261
  chunks.append(text[start:end])
262
+ start = end - (chunk_overlap * 4)
263
  return chunks
264
 
265
  tokens = tiktoken_encoder.encode(text)
 
272
  start += chunk_size - chunk_overlap
273
  return chunks
274
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  def get_embedding(text):
276
  if not embedding_model: return None
277
  if not text or not isinstance(text, str): return None
278
  try:
279
+ return embedding_model.encode(text, normalize_embeddings=True).tolist()
 
280
  except Exception as e:
281
  print(f"{RED}Hiba embedding közben: {e}{RESET}")
282
  return None
283
 
 
284
  def create_es_index(client, index_name, index_settings, index_mappings):
285
+ if DEBUG_MODE: print(f"\nIndex ellenőrzése: '{index_name}'...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  try:
287
  if not client.indices.exists(index=index_name):
288
+ print(f"'{index_name}' index létrehozása...")
289
+ client.indices.create(index=index_name, settings=index_settings, mappings=index_mappings)
290
+ print(f"{GREEN}Index sikeresen létrehozva.{RESET}")
 
 
 
 
 
 
 
291
  else:
292
  print(f"Index '{index_name}' már létezik.")
293
+ return True
 
 
 
 
 
 
 
 
 
 
 
294
  except Exception as e:
295
+ print(f"{RED}!!! Hiba az index létrehozásakor: {e}{RESET}")
296
  traceback.print_exc()
297
  return False
298
 
 
304
  main_content = soup.find('main') or soup.find('article') or soup.body
305
  if main_content:
306
  text = main_content.get_text(separator='\n', strip=True)
307
+ return "\n".join(line for line in text.splitlines() if line.strip())
 
 
 
308
  except Exception as e:
309
+ print(f"{RED}Hiba a HTML tartalom kinyerésekor: {e}{RESET}")
310
+ return ""
 
311
 
312
  def extract_and_filter_links(soup, base_url, target_domain):
313
  links = set()
314
+ for a_tag in soup.find_all('a', href=True):
315
+ href = a_tag['href'].strip()
316
+ if href and not href.startswith(('#', 'mailto:', 'javascript:')):
317
+ full_url = urljoin(base_url, href)
318
+ parsed_url = urlparse(full_url)
319
+ if parsed_url.scheme in ['http', 'https'] and parsed_url.netloc == target_domain:
320
+ links.add(parsed_url._replace(fragment="").geturl())
 
 
 
 
321
  return links
322
 
323
  def crawl_and_index_website(start_url, max_depth, es_client, index_name):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  visited_urls = set()
325
  urls_to_visit = deque([(start_url, 0)])
326
  bulk_actions = []
 
327
  total_indexed = 0
328
+ target_domain = urlparse(start_url).netloc
 
 
 
 
329
  print(f"Web crawling indítása: {start_url} (Max mélység: {max_depth}, Cél: {target_domain})")
330
 
331
  while urls_to_visit:
332
+ current_url, current_depth = urls_to_visit.popleft()
333
+ if current_url in visited_urls or current_depth > max_depth:
334
+ continue
335
+
336
+ print(f"\n--- Feldolgozás (Mélység: {current_depth}): {current_url} ---")
337
+ visited_urls.add(current_url)
338
+
339
  try:
340
+ headers = {'User-Agent': USER_AGENT}
341
+ response = requests.get(current_url, headers=headers, timeout=15)
342
+ response.raise_for_status()
343
+ if 'text/html' not in response.headers.get('content-type', '').lower():
344
+ print(f" {YELLOW}-> Nem HTML tartalom, kihagyva.{RESET}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
  continue
346
+
347
+ html_content = response.content
 
 
 
348
  soup = BeautifulSoup(html_content, 'html.parser')
349
  page_text = extract_text_from_html(html_content)
350
+
351
  if not page_text or len(page_text) < MIN_CHUNK_SIZE_CHARS:
352
+ print(f" {YELLOW}-> Nem sikerült szöveget kinyerni vagy túl rövid.{RESET}")
353
  continue
354
+
 
 
355
  final_chunks = chunk_text_by_tokens(page_text, CHUNK_SIZE_TOKENS, CHUNK_OVERLAP_TOKENS)
356
+ url_category = generate_categories_with_llm(together_client, soup, page_text)[0]
 
 
 
 
 
357
  page_summary = generate_summary_with_llm(together_client, page_text)
358
+
359
+ print(f"{GREEN} Indexelésre előkészítve: {len(final_chunks)} darab (Kategória: {url_category}){RESET}")
360
+
 
 
 
 
 
 
 
 
361
  for chunk_text in final_chunks:
362
  element_vector = get_embedding(chunk_text)
363
  if element_vector:
364
+ doc = {
365
+ "text_content": chunk_text,
366
+ "embedding": element_vector,
367
+ "source_origin": "website",
368
+ "source_url": current_url,
369
+ "source_type": "token_chunking",
370
+ "category": url_category,
371
+ "summary": page_summary
372
+ }
373
  bulk_actions.append({"_index": index_name, "_source": doc})
374
+
375
+ if len(bulk_actions) >= BATCH_SIZE:
376
+ print(f" -> {len(bulk_actions)} chunk indexelése (batch)...")
377
+ success_count, _ = helpers.bulk(es_client, bulk_actions)
378
+ total_indexed += success_count
379
+ bulk_actions = []
380
+
 
 
 
 
 
381
  if current_depth < max_depth:
382
+ new_links = extract_and_filter_links(soup, current_url, target_domain)
383
+ for link in new_links:
384
+ if link not in visited_urls:
385
+ urls_to_visit.append((link, current_depth + 1))
386
+
387
+ time.sleep(REQUEST_DELAY)
388
+
389
+ except requests.exceptions.RequestException as req_err:
390
+ print(f" {RED}!!! Hiba a letöltés során: {req_err}{RESET}")
391
+ except Exception as e:
392
+ print(f" {RED}!!! Váratlan hiba a ciklusban ({current_url}): {e}{RESET}")
393
+ traceback.print_exc()
 
 
 
 
 
 
 
 
 
 
394
 
395
  if bulk_actions:
396
+ print(f" -> Maradék {len(bulk_actions)} chunk indexelése...")
397
+ success_count, _ = helpers.bulk(es_client, bulk_actions)
398
+ total_indexed += success_count
 
 
 
 
 
399
 
400
  print(f"\n--- Web Crawling és Indexelés Befejezve ---")
401
  print(f"Meglátogatott URL-ek: {len(visited_urls)}")
402
+ print(f"Sikeresen indexelt chunkok: {total_indexed}")
403
+ return total_indexed
 
 
404
 
 
405
  if __name__ == "__main__":
406
+ print(f"----- Web Crawler és Indexelő Indítása -----")
407
+
408
+ embedding_model, EMBEDDING_DIM, device = load_embedding_model()
409
+
410
+ if not all([embedding_model, EMBEDDING_DIM]):
411
+ print(f"{RED}Hiba: Az embedding modell betöltése sikertelen. A program leáll.{RESET}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  exit(1)
413
 
414
  es_client = initialize_es_client()
 
 
415
  if es_client:
416
+ if es_client.indices.exists(index=VECTOR_INDEX_NAME):
417
+ print(f"{YELLOW}Figyelem: A '{VECTOR_INDEX_NAME}' index már létezik. A script feltételezi, hogy a beállításai helyesek.{RESET}")
418
+ print(f"{YELLOW}Ha újra akarod építeni, töröld manuálisan: DELETE /{VECTOR_INDEX_NAME}{RESET}")
419
+ index_ready = True
420
+ else:
421
+ index_ready = create_es_index(
422
+ client=es_client,
423
+ index_name=VECTOR_INDEX_NAME,
424
+ index_settings=INDEX_SETTINGS_SEPARATE_ANALYZER,
425
+ index_mappings=INDEX_MAPPINGS_WEB
426
+ )
427
+
428
  if index_ready:
429
  print(f"\nIndex '{VECTOR_INDEX_NAME}' kész. Web crawling és indexelés indítása...")
430
  final_success_count = crawl_and_index_website(START_URL, MAX_DEPTH, es_client, VECTOR_INDEX_NAME)
431
+ if final_success_count > 0:
432
+ print(f"\n{GREEN}A folyamat sikeresen lefutott. {final_success_count} dokumentum indexelve.{RESET}")
433
+ else:
434
+ print(f"\n{YELLOW}A folyamat lefutott, de 0 új dokumentum került indexelésre.{RESET}")
435
  else:
436
+ print(f"{RED}Hiba: Az index nem áll készen a használatra. A program leáll.{RESET}")
 
 
 
 
 
 
 
 
 
 
 
 
437
  else:
438
+ print(f"{RED}Hiba: Az Elasticsearch kliens nem elérhető. A program leáll.{RESET}")