May 17, 2026, 11:40 PM

This commit is contained in:
Paweł Domański
2026-05-18 06:40:19 +00:00
commit 64944cf004
896 changed files with 310709 additions and 0 deletions
+355
View File
@@ -0,0 +1,355 @@
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()