import time import pandas as pd import undetected_chromedriver as uc from bs4 import BeautifulSoup from selenium.webdriver.common.keys import Keys import os import json def scrape_alternativeto(url, headless=True): # Konfiguracja Selenium (undetected-chromedriver) options = uc.ChromeOptions() options.add_argument("--disable-blink-features=AutomationControlled") # Dodaj opcję headless, jeśli ustawiona na True if headless: options.add_argument("--headless") options.add_argument("--window-size=1920,1080") options.add_argument("--start-maximized") print("🔍 Uruchamiam przeglądarkę w trybie headless (zminimalizowanym)") else: print("🔍 Uruchamiam przeglądarkę w trybie widocznym") driver = uc.Chrome(options=options) driver.get(url) # Poczekaj na załadowanie strony time.sleep(5) # Zwiększam czas oczekiwania do 5 sekund # Akceptacja cookies jeśli pojawi się banner try: consent_button = driver.find_element("xpath", "//button[contains(., 'Consent') or contains(., 'Zgadzam się')]") consent_button.click() time.sleep(2) # Zwiększam czas oczekiwania po kliknięciu print("✅ Zaakceptowano cookies") except Exception as e: print(f"ℹ️ Nie znaleziono bannera cookies lub inny błąd: {str(e)}") # Przewiń stronę, aby załadować więcej informacji print("🔄 Przewijanie strony...") body = driver.find_element("tag name", "body") for i in range(15): # Zwiększam liczbę przewinięć body.send_keys(Keys.PAGE_DOWN) time.sleep(1) if i % 5 == 0: print(f" - Przewinięto {i+1} razy") # Pobierz HTML strony print("📄 Pobieranie pełnego HTML strony...") html = driver.page_source driver.quit() print("🔍 Rozpoczynam analizę HTML...") # Zapisz HTML do pliku dla debugowania with open("debug_page.html", "w", encoding="utf-8") as f: f.write(html) print("💾 Zapisano HTML do pliku debug_page.html dla analizy") # Parsowanie HTML za pomocą BeautifulSoup soup = BeautifulSoup(html, "html.parser") # Użyj podanego selektora do znalezienia informacji o CMS-ach cms_items = soup.select("#mainContent > section > div.blue-box.PageIntroWrapper_wrapper__bHA4J.PageIntroWrapper_noClouds__yfmJz > div > header") print(f"🔍 Znaleziono {len(cms_items)} głównych elementów CMS") if not cms_items: print("⚠️ Nie znaleziono głównego elementu CMS! Sprawdź poprawny selektor w przeglądarce.") # Próba znalezienia głównych elementów strony dla diagnostyki main_content = soup.select("#mainContent") print(f" - Czy istnieje #mainContent? {len(main_content) > 0}") sections = soup.select("#mainContent > section") print(f" - Liczba sekcji w #mainContent: {len(sections)}") return # Lista na wyniki extracted_data = [] # Pobranie głównych danych for item in cms_items: try: name_element = item.select_one("h1") name = name_element.text.strip() if name_element else "Brak nazwy" print(f"📌 Nazwa CMS: {name}") # Próba wydobycia linku (jeśli istnieje) link_element = item.select_one("a") link = link_element.get("href") if link_element else "" print(f"🔗 Link: {link}") # Przygotowanie słownika na dane cms_data = { "name": name, "link": link, "source": "alternativeto.net" } # Dodajemy do listy wyników extracted_data.append(cms_data) except Exception as e: print(f"⚠️ Błąd podczas wydobywania podstawowych danych: {e}") # Jeśli znaleziono co najmniej jeden CMS, pobierz pozostałe dane if extracted_data: cms_data = extracted_data[0] # W tym przypadku skupiamy się na pierwszym znalezionym CMS # 1. Pobieranie description z podanego selektora (zaktualizowany) print("🔍 Próba pobrania opisu...") try: # Nowy selektor dla opisu description_selector = "#mainContent > section > div.blue-box.PageIntroWrapper_wrapper__bHA4J.PageIntroWrapper_noClouds__yfmJz > div > header > div.AppItem_shared_itemBox__fcqom.AppItemAbout_itemBoxAbout__egNDI > div.AppItem_shared_info__uqY4g.AppItemAbout_infoAbout__szbqZ > div.AppItem_shared_desc___QPnv.AppItemAbout_descAbout__Z7Qlv" description_element = soup.select_one(description_selector) if description_element: # Pobierz tekst ze wszystkich paragrafów wewnątrz tego elementu paragraphs = description_element.find_all("p") if paragraphs: cms_data["description"] = " ".join([p.text.strip() for p in paragraphs]) print(f"📝 Opis: {cms_data['description'][:50]}...") else: # Jeśli nie ma paragrafów, pobierz cały tekst z tego elementu cms_data["description"] = description_element.text.strip() print(f"📝 Opis (bezpośrednio z div): {cms_data['description'][:50]}...") else: cms_data["description"] = "" print("⚠️ Nie znaleziono elementu opisu") # Próbujmy znaleźć elementy pośrednie desc_container = soup.select_one(".AppItem_shared_desc___QPnv") print(f" - Czy istnieje .AppItem_shared_desc___QPnv? {desc_container is not None}") except Exception as e: print(f"⚠️ Błąd podczas pobierania opisu: {e}") cms_data["description"] = "" # 2. Pobieranie licence z podanego selektora print("🔍 Próba pobrania licencji...") try: licence_selector = "#mainContent > section > div.blue-box.PageIntroWrapper_wrapper__bHA4J.PageIntroWrapper_noClouds__yfmJz > div > header > div.AppItemAbout_lowerAbout__gLc_7 > div.flex.flex-row.flex-wrap.gap-x-10.grow > div:nth-child(1) > ul > li" licence_element = soup.select_one(licence_selector) if licence_element: cms_data["licence"] = licence_element.text.strip() print(f"📄 Licencja: {cms_data['licence']}") else: cms_data["licence"] = "" print("⚠️ Nie znaleziono elementu licencji") # Spróbujmy prostszego selektora licence_items = soup.select("div.AppItemAbout_lowerAbout__gLc_7 li") print(f" - Znaleziono {len(licence_items)} elementów li w sekcji dolnej") except Exception as e: print(f"⚠️ Błąd podczas pobierania licencji: {e}") cms_data["licence"] = "" # 3. Pobieranie operation_system jako tablicy z podanego selektora print("🔍 Próba pobrania systemów operacyjnych...") try: os_selector = "#mainContent > section > div.blue-box.PageIntroWrapper_wrapper__bHA4J.PageIntroWrapper_noClouds__yfmJz > div > header > div.AppItemAbout_lowerAbout__gLc_7 > div.flex.flex-row.flex-wrap.gap-x-10.grow > div.grow > ul > li" os_elements = soup.select(os_selector) if os_elements: cms_data["operation_system"] = [os_item.text.strip() for os_item in os_elements] print(f"💻 Systemy operacyjne: {cms_data['operation_system']}") else: cms_data["operation_system"] = [] print("⚠️ Nie znaleziono elementów systemów operacyjnych") # Spróbujmy prostszego selektora os_items = soup.select("div.grow > ul > li") print(f" - Znaleziono {len(os_items)} elementów li w sekcji systemów") except Exception as e: print(f"⚠️ Błąd podczas pobierania systemów operacyjnych: {e}") cms_data["operation_system"] = [] # 4. Pobieranie properties jako tablicy print("🔍 Próba pobrania właściwości...") try: property_selector = "#mainContent > section > div:nth-child(5) > div.Box_box__20D8q.Box_transparentBox__Dj_jC.commonBoxList > div.HighlightFeatures_featureContainer__85Z5p > div > ol:nth-child(2) > li" property_elements = soup.select(property_selector) if property_elements: cms_data["properties"] = [prop.text.strip() for prop in property_elements] print(f"🔧 Właściwości: znaleziono {len(cms_data['properties'])} elementów") if cms_data["properties"]: print(f" Przykład: {cms_data['properties'][0]}") else: cms_data["properties"] = [] print("⚠️ Nie znaleziono elementów właściwości") # Spróbujmy prostszego selektora all_ol = soup.select("div.HighlightFeatures_featureContainer__85Z5p ol") print(f" - Znaleziono {len(all_ol)} list ol w kontenerze funkcji") except Exception as e: print(f"⚠️ Błąd podczas pobierania właściwości: {e}") cms_data["properties"] = [] # 5. Pobieranie features jako tablicy print("🔍 Próba pobrania funkcji...") try: feature_selector = "#mainContent > section > div:nth-child(5) > div.Box_box__20D8q.Box_transparentBox__Dj_jC.commonBoxList > div.HighlightFeatures_featureContainer__85Z5p > div > ol:nth-child(6) > li" feature_elements = soup.select(feature_selector) if feature_elements: cms_data["features"] = [feature.text.strip() for feature in feature_elements] print(f"✨ Funkcje: znaleziono {len(cms_data['features'])} elementów") if cms_data["features"]: print(f" Przykład: {cms_data['features'][0]}") else: cms_data["features"] = [] print("⚠️ Nie znaleziono elementów funkcji") # Spróbujmy innego selektora dla list funkcji all_lists = soup.select("div.HighlightFeatures_featureContainer__85Z5p ol li") print(f" - Znaleziono {len(all_lists)} elementów li w listach funkcji") except Exception as e: print(f"⚠️ Błąd podczas pobierania funkcji: {e}") cms_data["features"] = [] # 6. Pobieranie url z podanego selektora (zaktualizowany) print("🔍 Próba pobrania URL...") try: # Zaktualizowany selektor dla URL url_selector = "#mainContent > section > div:nth-child(6) > div.styles_mainGrid__5RbQK > div.styles_mainGridRight__gHR6o > div > div:nth-child(3) > div.AppExternalLinks_linkContainer__1kDP1 > a:nth-child(1) > button > span" url_span_element = soup.select_one(url_selector) if url_span_element: # Pobierz tekst ze spana url_text = url_span_element.text.strip() cms_data["url"] = url_text print(f"🌐 URL (tekst z przycisku): {url_text}") else: # Próba alternatywnego podejścia - znalezienie linku (tagu a) url_link_selector = "#mainContent > section > div:nth-child(6) > div.styles_mainGrid__5RbQK > div.styles_mainGridRight__gHR6o > div > div:nth-child(3) > div.AppExternalLinks_linkContainer__1kDP1 > a:nth-child(1)" url_link_element = soup.select_one(url_link_selector) if url_link_element: href = url_link_element.get("href", "") # Sprawdź, czy href zawiera rzeczywisty URL (pełny), a nie tylko ścieżkę względną if href.startswith("http"): cms_data["url"] = href else: # Jeśli to ścieżka względna, spróbuj pobrać tekst z przycisku wewnątrz linku button_element = url_link_element.select_one("button") if button_element: cms_data["url"] = button_element.text.strip() else: cms_data["url"] = href # Użyj ścieżki względnej jako ostateczność print(f"🌐 URL (z tagu a): {cms_data['url']}") else: cms_data["url"] = "" print("⚠️ Nie znaleziono elementu URL") # Spróbujmy prostszego selektora link_containers = soup.select("div.AppExternalLinks_linkContainer__1kDP1 a") print(f" - Znaleziono {len(link_containers)} linków w kontenerze linków zewnętrznych") if link_containers: for i, link in enumerate(link_containers[:3]): # Pokaż tylko pierwsze 3 print(f" Link {i+1}: href={link.get('href', 'brak')}, tekst={link.text.strip()[:30]}...") except Exception as e: print(f"⚠️ Błąd podczas pobierania URL: {e}") cms_data["url"] = "" # Aktualizuj listę wyników extracted_data[0] = cms_data if not extracted_data: print("⚠️ Nie udało się wydobyć żadnych danych.") return # Dopisywanie do istniejących danych JSON (jeśli plik istnieje) json_path = "cms_all.json" if os.path.exists(json_path): with open(json_path, "r", encoding="utf-8") as f: try: existing_data = json.load(f) print(f"📊 Wczytano {len(existing_data)} istniejących rekordów z JSON") except Exception as e: print(f"⚠️ Błąd podczas odczytu pliku JSON: {e}") existing_data = [] else: existing_data = [] print("ℹ️ Plik JSON nie istnieje, tworzę nowy") # Dodaj nowe dane, unikając duplikatów po nazwie i linku existing_keys = {(d["name"], d["link"]) for d in existing_data if "name" in d and "link" in d} added = 0 for row in extracted_data: if (row["name"], row["link"]) not in existing_keys: existing_data.append(row) added += 1 print(f"📊 Dodano {added} nowych rekordów do JSON") # Zapisz zaktualizowane dane with open(json_path, "w", encoding="utf-8") as f: json.dump(existing_data, f, indent=4, ensure_ascii=False) print(f"✅ Dane dopisane do {json_path}") # Analogicznie dla CSV import pandas as pd df_new = pd.DataFrame(extracted_data) csv_path = "cms_all.csv" if os.path.exists(csv_path): try: df_existing = pd.read_csv(csv_path, encoding="utf-8") print(f"📊 Wczytano {len(df_existing)} istniejących rekordów z CSV") df_all = pd.concat([df_existing, df_new], ignore_index=True) df_all = df_all.drop_duplicates(subset=["name", "link"]) except Exception as e: print(f"⚠️ Błąd podczas odczytu pliku CSV: {e}") df_all = df_new else: df_all = df_new print("ℹ️ Plik CSV nie istnieje, tworzę nowy") df_all.to_csv(csv_path, index=False, encoding="utf-8") print(f"✅ Dane dopisane do {csv_path}") print(f"📊 Łącznie zapisano {len(df_all)} rekordów do CSV") def show_descriptions_from_csv(): """ Funkcja otwierająca plik cms_alternatives.csv, pobierająca atrybut link i wyświetlająca jego wartość dla każdego rekordu. """ print("\n🔗 Odczytywanie linków z pliku cms_alternatives.csv...") try: # Sprawdź, czy plik istnieje alternative_csv_path = "cms_alternatives.csv" if not os.path.exists(alternative_csv_path): print(f"⚠️ Plik {alternative_csv_path} nie istnieje!") return # Wczytaj plik CSV df = pd.read_csv(alternative_csv_path, encoding="utf-8") # Sprawdź, czy kolumna link istnieje if "link" not in df.columns: print("⚠️ Kolumna 'link' nie istnieje w pliku CSV!") print(f"Dostępne kolumny: {', '.join(df.columns)}") return # Wyświetl informacje o pliku print(f"📊 Znaleziono {len(df)} rekordów w pliku {alternative_csv_path}") # Wyświetl linki dla każdego rekordu print("\n=== LINKI CMS-ÓW ===") for i, row in df.iterrows(): name = row.get("name", f"Rekord {i+1}") link = row.get("link", "Brak linku") print(f"\n📌 {name}:") print(f"🔗 {link}") scrape_alternativeto(link, headless=True) print("\n=== KONIEC LINKÓW ===") except Exception as e: print(f"⚠️ Wystąpił błąd podczas odczytywania pliku: {str(e)}") # Uruchamiamy skrypt if __name__ == "__main__": print("🚀 Rozpoczynam scraping AlternativeTo...") ## scrape_alternativeto('https://alternativeto.net/software/boomla/about/') print("✅ Scraping zakończony") # Uruchom funkcję pokazującą linki z pliku CSV show_descriptions_from_csv()