Files
2026-05-18 06:40:19 +00:00

356 lines
17 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()