complete task INDX-0002. parse one site from list
This commit is contained in:
		
							
								
								
									
										5
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -50,6 +50,11 @@ | ||||
|             <artifactId>spring-dotenv</artifactId> | ||||
|             <version>4.0.0</version> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.jsoup</groupId> | ||||
|             <artifactId>jsoup</artifactId> | ||||
|             <version>1.18.3</version> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|  | ||||
|     <build> | ||||
|   | ||||
							
								
								
									
										42
									
								
								src/main/java/searchengine/model/Link.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/main/java/searchengine/model/Link.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| package searchengine.model; | ||||
|  | ||||
| import java.net.URI; | ||||
| import java.util.HashSet; | ||||
| import java.util.Objects; | ||||
| import java.util.Set; | ||||
|  | ||||
| public record Link(URI uri, int depth, Set<Link> children, Link parent) { | ||||
|     public Link { | ||||
|         children = new HashSet<>(); | ||||
|     } | ||||
|  | ||||
|     public Link(URI uri, Link parent) { | ||||
|         this(uri, (parent == null) ? 0 : parent.depth() + 1, new HashSet<>(), parent); | ||||
|     } | ||||
|  | ||||
|     public void addChild(URI childUri) { | ||||
|         children.add(new Link(childUri, this)); | ||||
|     } | ||||
|  | ||||
|     // Переопределяем equals и hashCode, исключая поля children и parent | ||||
|     @Override | ||||
|     public boolean equals(Object o) { | ||||
|         if (this == o) return true; | ||||
|         if (o == null || getClass() != o.getClass()) return false; | ||||
|         Link link = (Link) o; | ||||
|         return depth == link.depth && Objects.equals(uri, link.uri); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int hashCode() { | ||||
|         return Objects.hash(uri, depth); // Исключаем children и parent | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String toString() { // А он нужен? | ||||
|         return "Link{" + | ||||
|                 "uri=" + uri + | ||||
|                 ", depth=" + depth + | ||||
|                 '}'; // Исключаем children и parent | ||||
|     } | ||||
| } | ||||
| @@ -1,14 +1,27 @@ | ||||
| package searchengine.services; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.jsoup.Jsoup; | ||||
| import org.jsoup.nodes.Document; | ||||
| import org.springframework.stereotype.Service; | ||||
| import searchengine.config.SitesList; | ||||
| import searchengine.model.Link; | ||||
|  | ||||
| import java.net.URI; | ||||
| import java.net.URISyntaxException; | ||||
| import java.net.URLEncoder; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.Random; | ||||
| import java.util.Set; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| import java.util.regex.Pattern; | ||||
|  | ||||
| @Service | ||||
| @RequiredArgsConstructor | ||||
| public class IndexingServiceImpl implements IndexingService { | ||||
|     boolean indexingIsRunning = false; | ||||
|     private boolean indexingIsRunning = false; | ||||
|     private final SitesList sitesList; | ||||
|     private final Random random = new Random(); | ||||
|  | ||||
|     @Override | ||||
|     public void startIndexing() { | ||||
| @@ -16,12 +29,137 @@ public class IndexingServiceImpl implements IndexingService { | ||||
|             System.out.println("Индексация уже запущена"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         System.out.println("Список сайтов для индексации:"); | ||||
|         sitesList.getSites().forEach(site -> System.out.println("URL: " + site.getUrl() + ", Название: " + site.getName())); | ||||
|  | ||||
|         System.out.println("Запуск индексации"); | ||||
|         indexingIsRunning = true; | ||||
|         System.out.println("Индексация запущена"); | ||||
|  | ||||
|         // Выводим список сайтов для индексации | ||||
|         System.out.println("Список сайтов для индексации:"); | ||||
|         sitesList.getSites().forEach(site -> | ||||
|                 System.out.println("URL: " + site.getUrl() + ", Название: " + site.getName()) | ||||
|         ); | ||||
|  | ||||
|         // Начинаем парсинг первого сайта | ||||
|         String startUrl = sitesList.getSites().get(0).getUrl(); | ||||
|         String siteName = sitesList.getSites().get(0).getName(); | ||||
|  | ||||
|         System.out.println("Начинаем парсинг сайта " + siteName + " (" + startUrl + ")"); | ||||
|  | ||||
|         Set<URI> visitedLinks = ConcurrentHashMap.newKeySet(); // Набор для отслеживания посещенных URL | ||||
|  | ||||
|         try { | ||||
|             URI startUri = new URI(startUrl); | ||||
|             System.out.printf("\n=== Начало индексации %s (%s) ===\n", siteName, startUri); | ||||
|             Link rootLink = new Link(startUri, null); // Создаем корневой Link | ||||
|             parse(rootLink, visitedLinks); // Запуск парсинга | ||||
|         } catch (URISyntaxException e) { | ||||
|             System.out.println("Некорректный URL: " + startUrl); | ||||
|         } catch (InterruptedException e) { | ||||
|             System.out.println("Индексация была прервана: " + e.getMessage()); | ||||
|             Thread.currentThread().interrupt(); | ||||
|         } finally { | ||||
|             indexingIsRunning = false; | ||||
|             System.out.println("\n=== Индексация завершена ==="); | ||||
|             System.out.println("Всего страниц проиндексировано: " + visitedLinks.size()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     private void parse(Link link, Set<URI> visitedLinks) throws InterruptedException { | ||||
|         // Добавляем текущий URL в visitedLinks до начала обработки | ||||
|         if (!visitedLinks.add(link.uri())) { | ||||
|             return; // Если URL уже был обработан, выходим | ||||
|         } | ||||
|  | ||||
|         // Задержка для соблюдения правил robots.txt | ||||
|         Thread.sleep(50 + random.nextInt(150)); | ||||
|         System.out.println("Парсим страницу: " + link.uri()); | ||||
|  | ||||
|         // Добавляем дочерние ссылки | ||||
|         addChildLinks(link, visitedLinks); | ||||
|  | ||||
|         // Рекурсивно обрабатываем дочерние ссылки | ||||
|         link.children().forEach(child -> { | ||||
|             try { | ||||
|                 parse(child, visitedLinks); | ||||
|             } catch (InterruptedException e) { | ||||
|                 Thread.currentThread().interrupt(); | ||||
|                 throw new RuntimeException("Парсинг был прерван: " + e.getMessage()); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private void addChildLinks(Link link, Set<URI> visitedLinks) { | ||||
|         try { | ||||
|             Document document = Jsoup.connect(link.uri().toString()) | ||||
|                     .userAgent("Mozilla/5.0") | ||||
|                     .referrer("https://www.google.com") | ||||
|                     .get(); | ||||
|  | ||||
|             document.select("a[href]").forEach(element -> { | ||||
|                 String hrefStr = element.attr("href").strip(); | ||||
|                 try { | ||||
|                     // Кодируем URL перед созданием объекта URI | ||||
|                     String encodedHrefStr = URLEncoder.encode(hrefStr, StandardCharsets.UTF_8) | ||||
|                             .replaceAll("%3A", ":") | ||||
|                             .replaceAll("%2F", "/"); | ||||
|                     URI href = URI.create(encodedHrefStr); | ||||
|  | ||||
|                     if (!href.isAbsolute()) { | ||||
|                         href = link.uri().resolve(href); | ||||
|                     } | ||||
|                     href = normalizeUri(href); | ||||
|  | ||||
|                     // Проверяем, что ссылка корректна | ||||
|                     if (isCorrectUrl(link.uri(), href, visitedLinks)) { | ||||
|                         link.addChild(href); // Используем метод addChild из класса Link | ||||
|                     } | ||||
|                 } catch (Exception e) { | ||||
|                     // Игнорируем некорректные URL без вывода сообщений | ||||
|                 } | ||||
|             }); | ||||
|         } catch (Exception e) { | ||||
|             // Игнорируем ошибки без вывода сообщений | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private URI normalizeUri(URI href) { | ||||
|         try { | ||||
|             String path = href.getPath(); // Получаем путь | ||||
|             if (path == null) { | ||||
|                 path = ""; // Если путь равен null, заменяем на пустую строку | ||||
|             } else { | ||||
|                 path = path.replaceAll("/+$", ""); // Убираем завершающие слэши | ||||
|             } | ||||
|             return new URI( | ||||
|                     href.getScheme(), | ||||
|                     href.getAuthority(), | ||||
|                     path, // Используем обработанный путь | ||||
|                     null, | ||||
|                     href.getFragment() | ||||
|             ); | ||||
|         } catch (URISyntaxException e) { | ||||
|             return href; // В случае ошибки возвращаем исходный URI | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean isCorrectUrl(URI baseUri, URI href, Set<URI> visitedLinks) { | ||||
|         // Проверка на некорректные протоколы | ||||
|         String hrefStr = href.toString(); | ||||
|         if (hrefStr.startsWith("javascript:") || hrefStr.startsWith("mailto:") || hrefStr.startsWith("tel:")) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Проверка на принадлежность тому же домену | ||||
|         if (href.getHost() == null || !href.getHost().equals(baseUri.getHost())) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Проверка на файлы и якоря | ||||
|         Pattern patternNotFile = Pattern.compile("(\\S+(\\.(?i)(jpg|png|gif|bmp|pdf|php|doc|docx|rar))$)"); | ||||
|         Pattern patternNotAnchor = Pattern.compile("#([\\w\\-]+)?$"); | ||||
|         if (href.isOpaque() || patternNotFile.matcher(hrefStr).find() || patternNotAnchor.matcher(hrefStr).find()) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Проверка на уже посещенные ссылки | ||||
|         return !visitedLinks.contains(href); | ||||
|     } | ||||
| } | ||||
| @@ -16,9 +16,11 @@ spring: | ||||
|  | ||||
| indexing-settings: | ||||
|   sites: | ||||
|     - url: https://www.lenta.ru | ||||
|       name: Лента.ру | ||||
|     - url: https://wiki.kuksa.dev | ||||
|       name: Wiki Kuksa.Dev | ||||
|     - url: https://www.autelrobotics.com | ||||
|       name: Autel Robotics | ||||
|     - url: https://www.skillbox.ru | ||||
|       name: Skillbox | ||||
|     - url: https://www.playback.ru | ||||
|       name: PlayBack.Ru | ||||
| #    - url: https://www.playback.ru | ||||
| #      name: PlayBack.Ru | ||||
		Reference in New Issue
	
	Block a user
	 Eduard Kuksa
					Eduard Kuksa