4 분 소요

아이디어

요즘 어학 공부를 진행중인데, 모르는 단어의 뜻을 검색하기 위해서 네이버 영어사전을 주로 사용하곤 한다.

보통 한문장에 모르는 단어가 있으면 검색해서 엑셀로 따로 저장해두고 두고두고 보는 식으로 단어 암기를 진행중인데

한문장에 모르는 단어가 2개 이상일 경우 단어를 검색하고 엑셀에 옮겨 작성하는게 너무 불편하다.

영단어 여러개를 한번에 작성하거나 붙여넣고 그 결과를 크롤링해서 한번에 목록 형태로 보여줄수 있다면 괜찮은 기능이 되지 않을까 싶다.

단. 내가 필요한 부분은 영어사전 검색시 필요한 것이 단어의 품사랑 뜻 정도이니 그 부분은 잘 옮겨 올 수 있다면 괜찮을 것 같다.

추가적으로 목록으로 보여준 단어의 품사 및 뜻을 한번에 복사해서 엑셀로 옮길 수 있도록 해도 좋을것 같다.

Java로 구현

영어 단어를 입력하면 네이버 영어사전에서 검색하고 다시 가져오는 코드이다.

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("영단어를 입력하세요 (여러 개의 단어를 입력하려면 쉼표(,)로 구분하세요):");
        String input = scanner.nextLine();
        String[] words = input.split("\\s+");

        // Selenium 설정
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless");  // 브라우저를 화면에 표시하지 않음
        System.setProperty("webdriver.chrome.driver", "C:\\Users\\User\\Desktop\\chromedriver.exe");
        WebDriver driver = new ChromeDriver(options);
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        for (String word : words) {
            try {
                searchAndPrintDefinition(driver, word.trim());
            } catch (Exception e) {
                System.out.println("단어 '" + word.trim() + "' 검색 중 오류가 발생했습니다.");
            }
        }

        driver.quit();
        scanner.close();
    }

    public static void searchAndPrintDefinition(WebDriver driver, String word) {
        String url = "https://endic.naver.com/search.nhn?sLn=kr&isOnlyViewEE=N&query=" + word;
        driver.get(url);

        System.out.println("url : " + url);

        // 페이지가 완전히 로드될 때까지 기다림
        WebElement element = driver.findElement(By.cssSelector(".row"));

        String html = driver.getPageSource();
        Document doc = Jsoup.parse(html);

        // 첫 번째 .row 클래스 선택
        Element row = doc.selectFirst(".row");

        if (row != null) {
            // 첫 번째 .row 클래스 아래에 있는 .mean_list 및 .mean_list.multi 클래스 선택
            Elements meanings = row.select(".mean_item");
            Elements partsOfSpeech = row.select(".word_class");

            if (meanings.size() > 0 && partsOfSpeech.size() > 0) {
                System.out.println("단어: " + word);
                System.out.println("품사: " + partsOfSpeech.get(0).text());

                // 모든 뜻 출력
                for (Element meaning : meanings) {
                    System.out.println("뜻: " + meaning.text());
                }
                System.out.println();
            } else {
                System.out.println("단어 '" + word + "'의 정보를 찾을 수 없습니다.");
            }
        } else {
            System.out.println("단어 '" + word + "'의 정보를 찾을 수 없습니다.");
        }
    }
}

Spring 으로 변환

MainController.java

package com.example.multisearch.controller;

import com.example.multisearch.model.SearchResult;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.chrome.ChromeDriver;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Controller
public class MainController {

    @GetMapping("/")
    public String showForm() {
        return "index";
    }

    @GetMapping("/search")
    public String searchWords(@RequestParam("words") String words, Model model) {
        String[] wordArray = words.split("\\n");
        List<SearchResult> results = new ArrayList<>();

        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless");
        System.setProperty("webdriver.chrome.driver", "C:\\Users\\User\\Desktop\\chromedriver.exe");
        WebDriver driver = new ChromeDriver(options);
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        for (String word : wordArray) {
            SearchResult result = new SearchResult();
            result.setWord(word);
            String definition = searchAndPrintDefinition(driver, word.trim());
            if (definition.startsWith("단어")) {
                result.setDefinition(definition);
                // Create URL for the word
                result.setUrl("https://endic.naver.com/search.nhn?sLn=kr&isOnlyViewEE=N&query=" + word.trim());
            } else {
                result.setError(definition);
            }
            results.add(result);
        }

        driver.quit();
        model.addAttribute("results", results);
        return "results";
    }


    public static String searchAndPrintDefinition(WebDriver driver, String word) {
        String url = "https://endic.naver.com/search.nhn?sLn=kr&isOnlyViewEE=N&query=" + word; // https://en.dict.naver.com/enkodict/?query=apple#/search?query=hi
        driver.get(url);

        WebElement element = driver.findElement(By.cssSelector(".row"));
        String html = driver.getPageSource();
        Document doc = Jsoup.parse(html);
        Element row = doc.selectFirst(".row");
        if (row != null) {
            Elements meanings = row.select(".mean_item");
            Elements partsOfSpeech = row.select(".word_class");
            if (meanings.size() > 0 && partsOfSpeech.size() > 0) {
                StringBuilder definition = new StringBuilder();
                definition.append("단어: ").append(word).append("\n");
                definition.append("품사: ").append(partsOfSpeech.get(0).text()).append("\n");
                for (Element meaning : meanings) {
                    definition.append("뜻: ").append(meaning.text()).append("\n");
                }
                return definition.toString();
            } else {
                return "단어 '" + word + "'의 정보를 찾을 수 없습니다.";
            }
        } else {
            return "단어 '" + word + "'의 정보를 찾을 수 없습니다.";
        }
    }
}

SearchResult.java

package com.example.multisearch.model;

public class SearchResult {
    private String word;
    private String definition;
    private String error;
    private String url;

    // 생성자
    public SearchResult() {
    }

    // Getter 및 Setter 메서드
    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }

    public String getDefinition() {
        return definition;
    }

    public void setDefinition(String definition) {
        this.definition = definition;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

MultisearchApplication.java

package com.example.multisearch;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MultisearchApplication {

	public static void main(String[] args) {
		SpringApplication.run(MultisearchApplication.class, args);
	}
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Word Search</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
        }
        h1 {
            color: #007bff;
        }
        form {
            margin-top: 20px;
        }
        textarea {
            width: 300px;
            height: 100px;
            padding: 10px;
            font-size: 16px;
            border: 1px solid #ccc;
            border-radius: 5px;
            resize: none;
        }
        button {
            padding: 10px 20px;
            font-size: 16px;
            background-color: #007bff;
            color: #fff;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
    </style>
</head>
<body>
<h1>English Dictionary</h1>
<form action="/search" method="get">
    <label for="words">Enter words to search (separated by line breaks):</label><br>
    <textarea id="words" name="words" required></textarea><br>
    <button type="submit">Search</button>
</form>
</body>
</html>

results.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Search Results</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
        }
        h2 {
            color: #007bff;
        }
        .result {
            border: 1px solid #ccc;
            border-radius: 5px;
            padding: 10px;
            margin-bottom: 20px;
        }
        .word {
            font-weight: bold;
            font-size: 18px;
            color: #333;
            margin-bottom: 5px;
        }
        .definition {
            font-size: 16px;
            color: #666;
        }
        .error {
            color: red;
            font-size: 16px;
        }
        .url {
            margin-top: 10px;
            font-size: 14px;
            color: #007bff;
        }
        hr {
            margin-top: 20px;
            margin-bottom: 20px;
            border: none;
            border-top: 1px solid #ccc;
        }
    </style>
</head>
<body>
<h2>Search Results</h2>
<div th:each="result : ${results}" class="result">
    <div class="word" th:text="'Word: ' + ${result.word}"></div>
    <div th:if="${result.error}" class="error" th:text="${result.error}"></div>
    <div th:if="${result.definition}" class="definition" th:utext="${result.definition}"></div>
    <!-- Add URL for each result -->
    <div class="url" th:if="${result.url}">
        <a th:href="${result.url}" target="_blank" th:text="${result.url}"></a>
    </div>
</div>
</body>
</html>

프로젝트 관련 추가 정보

https://github.com/Re-Note/MultiSearch

문의 사항

댓글남기기