using System; using System.Threading.Tasks; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Support.UI; using System.Text.RegularExpressions; using WebDriverManager; using WebDriverManager.DriverConfigs.Impl; using System.IO; namespace BokBonCheck { public class KwangjuCityLibrarySearcher_choisangjun : KwangjuCityLibrarySearcher { public KwangjuCityLibrarySearcher_choisangjun() { SelectorValue = "MF"; SiteName = "광주시교육청통합도서관(분관최상준도서관)"; } } public class KwangjuCityLibrarySearcher_dagachi : KwangjuCityLibrarySearcher { public KwangjuCityLibrarySearcher_dagachi() { SelectorValue = "ME"; SiteName = "광주시교육청통합도서관(송정다가치문화도서관)"; } } public class KwangjuCityLibrarySearcher_central : KwangjuCityLibrarySearcher { public KwangjuCityLibrarySearcher_central() { SelectorValue = "MD"; SiteName = "광주시교육청통합도서관(중앙도서관)"; } } public class KwangjuCityLibrarySearcher_Student : KwangjuCityLibrarySearcher { public KwangjuCityLibrarySearcher_Student() { SelectorValue = "MC"; SiteName = "광주시교육청통합도서관(학생교육문화회관)"; } } public class KwangjuCityLibrarySearcher_Kumho : KwangjuCityLibrarySearcher { public KwangjuCityLibrarySearcher_Kumho() { SelectorValue = "MB"; SiteName = "광주시교육청통합도서관(금호평생교육관)"; } } public class KwangjuCityLibrarySearcher_Indi : KwangjuCityLibrarySearcher { public KwangjuCityLibrarySearcher_Indi() { SelectorValue = "MA"; SiteName = "광주시교육청통합도서관(학생독립운동기념회관)"; } } public abstract class KwangjuCityLibrarySearcher : ILibrarySearcher { public string SiteName { get; protected set; } = "광주시교육청통합도서관"; public string SiteUrl => "https://lib.gen.go.kr/main/site/search/bookSearch.do#simple"; protected string SelectorValue = ""; virtual protected void SelectLibrary(WebDriverWait wait) { try { // 콤보박스(select) 요소 찾기 var selectElement = wait.Until(d => d.FindElement(By.CssSelector("#manage_code"))); var select = new OpenQA.Selenium.Support.UI.SelectElement(selectElement); // value가 "MA"인 옵션 선택 select.SelectByValue(SelectorValue); } catch { // 예외 처리 (필요시 로깅 등) } } public async Task SearchAsync(string searchTerm) { var result = new BookSearchResult { SiteName = SiteName, SearchTerm = searchTerm, SearchTime = DateTime.Now }; IWebDriver driver = null; ChromeDriverService service = null; try { // ChromeDriverManager에서 설정한 드라이버 경로 사용 var driverPath = ChromeDriverManager.GetDriverPath(); if (string.IsNullOrEmpty(driverPath) || !File.Exists(driverPath)) { // 드라이버가 없으면 다시 설정 driverPath = await ChromeDriverManager.SetupChromeDriverAsync(); } // ChromeDriver 서비스 설정 service = ChromeDriverService.CreateDefaultService(Path.GetDirectoryName(driverPath), Path.GetFileName(driverPath)); service.HideCommandPromptWindow = true; var options = new ChromeOptions(); options.AddArgument("--headless"); options.AddArgument("--no-sandbox"); options.AddArgument("--disable-dev-shm-usage"); options.AddArgument("--disable-gpu"); options.AddArgument("--window-size=1920,1080"); options.AddArgument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"); options.AddArgument("--disable-blink-features=AutomationControlled"); options.AddExcludedArgument("enable-automation"); options.AddAdditionalOption("useAutomationExtension", false); // 명시적으로 서비스와 옵션을 사용하여 드라이버 생성 driver = new ChromeDriver(service, options); // 자동화 감지 방지 ((IJavaScriptExecutor)driver).ExecuteScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"); driver.Navigate().GoToUrl(SiteUrl); // 페이지 로딩 대기 var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15)); //대상도서관 선택 SelectLibrary(wait); // 검색창 찾기 IWebElement searchBox = null; try { // 여러 가능한 선택자 시도 var selectors = new[] { "input[id='search_txt']", "input[type='text']", }; foreach (var selector in selectors) { try { searchBox = wait.Until(d => { var el = d.FindElement(By.CssSelector(selector)); return (el != null && el.Displayed && el.Enabled) ? el : null; }); break; } catch { continue; } } if (searchBox == null) { throw new Exception("검색창을 찾을 수 없습니다."); } } catch (Exception ex) { throw new Exception($"검색창 찾기 실패: {ex.Message}"); } // 혹시 readonly라면 속성 제거 ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].removeAttribute('readonly')", searchBox); // 입력 전 clear searchBox.Clear(); searchBox.SendKeys(searchTerm); // 검색 버튼 클릭 IWebElement searchButton = null; try { var buttonSelectors = new[] { "input[type='submit']", }; //#frm_sch > fieldset > div > div.search_style > div > input foreach (var selector in buttonSelectors) { try { searchButton = driver.FindElement(By.CssSelector(selector)); break; } catch { continue; } } if (searchButton == null) { // Enter 키로 검색 시도 searchBox.SendKeys(Keys.Enter); } else { searchButton.Click(); } } catch (Exception ex) { // Enter 키로 검색 시도 searchBox.SendKeys(Keys.Enter); } // 검색 결과 로딩 대기 await Task.Delay(3000); // 검색 결과 수 추출 var resultCount = ExtractBookCount(driver); result.BookCount = resultCount; result.IsSuccess = true; } catch (Exception ex) { result.IsSuccess = false; result.ErrorMessage = ex.Message; result.BookCount = 0; } finally { driver?.Quit(); driver?.Dispose(); service?.Dispose(); } return result; } private int ExtractBookCount(IWebDriver driver) { try { // div.search-result 내부의 span에서 '전체 N' 텍스트 추출 var resultDiv = driver.FindElement(By.CssSelector("div.ndls_result")); var span = resultDiv.FindElement(By.XPath(".//span[contains(text(),'전체')]")); string text = span.Text; // 예: "전체 5 " var match = System.Text.RegularExpressions.Regex.Match(text, @"전체\s*(\d+)"); if (match.Success) { return int.Parse(match.Groups[1].Value); } return 0; } catch { return 0; } } } }