diff --git a/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs b/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs index 973861b..912da73 100644 --- a/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs @@ -1,20 +1,23 @@ -using System; -using System.Threading.Tasks; +using AngleSharp.Dom; +using AR; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; +using OpenQA.Selenium.Chromium; +using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Support.UI; +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Web.UI.WebControls; +using System.Xml.Linq; +using UniMarc.SearchModel; +using UniMarc.마크; using WebDriverManager; using WebDriverManager.DriverConfigs.Impl; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using UniMarc.마크; -using OpenQA.Selenium.Chromium; -using UniMarc.SearchModel; -using System.Runtime.CompilerServices; -using AR; -using OpenQA.Selenium.Interactions; namespace BokBonCheck { @@ -68,64 +71,103 @@ namespace BokBonCheck { try { - // 1. 먼저 모든 전체선택 버튼을 찾아서 클릭하여 기존 선택을 모두 해제 - Console.WriteLine("기존 선택 해제 중..."); - var allSelectButtons = wait.Until(d => d.FindElements(By.CssSelector("div.library span.btnAllSelect"))); - - foreach (var button in allSelectButtons) + Console.WriteLine("도서관 선택 과정 시작..."); + + // 1. 모든 체크박스 찾기 + var element = wait.Until(d => d.FindElement(By.CssSelector(".btIco.plus"))); + if (element != null) + { + SafeClick(element); + } + + // 2. 현재 체크된 상태를 확인하고, 목표 도서관이 아닌 것들만 해제 + int uncheckedCount = 0; + int checkCount = 0; + var allCheckboxes = wait.Until(d => d.FindElements(By.CssSelector("div.library input[type='checkbox']"))); + foreach (var checkbox in allCheckboxes) { try { - // 전체선택 버튼이 "전체선택"인지 "전체해제"인지 확인하고 필요시 클릭 - var buttonText = button.Text.Trim(); - Console.WriteLine($"전체선택 버튼 텍스트: {buttonText}"); - - // 전체선택 버튼을 두 번 클릭하여 모든 선택 해제 (선택 -> 해제) - SafeClick(button); - Thread.Sleep(300); - SafeClick(button); - Thread.Sleep(300); - Console.WriteLine("전체선택 버튼 클릭 완료 (해제)"); + var checkboxValue = checkbox.GetAttribute("value"); + var isChecked = checkbox.Selected; + + Console.WriteLine($"체크박스 {checkboxValue}: {(isChecked ? "체크됨" : "체크안됨")}"); + + // 체크되어 있고, 목표 도서관이 아닌 경우에만 해제 + if (isChecked && checkboxValue != AreaCode) + { + Console.WriteLine($"{checkboxValue} 도서관 체크 해제 중..."); + SafeClick(checkbox); + Thread.Sleep(50); + uncheckedCount++; + } + else if (isChecked == false && checkboxValue == AreaCode) + { + Console.WriteLine($"{checkboxValue} 도서관 체크 중..."); + SafeClick(checkbox); + Thread.Sleep(50); + } } catch (Exception ex) { - Console.WriteLine($"전체선택 버튼 클릭 실패: {ex.Message}"); + Console.WriteLine($"체크박스 처리 중 오류: {ex.Message}"); } } - // 2. 지정된 도서관만 선택 + Console.WriteLine($"{uncheckedCount}개의 체크박스를 해제했습니다."); + + // 3. 목표 도서관 선택 확인 및 설정 if (!string.IsNullOrEmpty(AreaCode)) { - Console.WriteLine($"특정 도서관 선택 중: {AreaCode}"); - - // div.library 안에서 해당 value를 가진 체크박스 찾기 - var libraryCheckbox = wait.Until(d => d.FindElement(By.CssSelector($"div.library input[type='checkbox'][value='{AreaCode}']"))); - - // 체크박스 선택 - SafeClick(libraryCheckbox); - Thread.Sleep(500); - - // 선택 확인 - if (libraryCheckbox.Selected) + Console.WriteLine($"목표 도서관 '{AreaCode}' 선택 확인 중..."); + + try { - Console.WriteLine($"{AreaCode} 도서관 선택 완료"); + var targetCheckbox = wait.Until(d => d.FindElement(By.CssSelector($"div.library input[type='checkbox'][value='{AreaCode}']"))); + + if (!targetCheckbox.Selected) + { + Console.WriteLine($"{AreaCode} 도서관이 체크되지 않았으므로 선택합니다."); + SafeClick(targetCheckbox); + Thread.Sleep(300); + } + + // 최종 선택 상태 확인 + if (targetCheckbox.Selected) + { + Console.WriteLine($"✓ {AreaCode} 도서관 선택 완료"); + } + else + { + Console.WriteLine($"✗ {AreaCode} 도서관 선택 실패"); + return false; + } } - else + catch (Exception ex) { - Console.WriteLine($"{AreaCode} 도서관 선택 실패, 다시 시도"); - SafeClick(libraryCheckbox); - Thread.Sleep(300); + Console.WriteLine($"목표 도서관 선택 실패: {ex.Message}"); + return false; } } else { - Console.WriteLine("전체 도서관으로 검색 - 모든 도서관 선택"); - // AreaCode가 없으면 첫 번째 전체선택 버튼만 클릭하여 모든 도서관 선택 - if (allSelectButtons.Count > 0) + Console.WriteLine("AreaCode가 비어있음 - 모든 도서관 선택"); + + // 모든 체크박스를 체크 + foreach (var checkbox in allCheckboxes) { - SafeClick(allSelectButtons[0]); - Thread.Sleep(300); - Console.WriteLine("모든 도서관 선택 완료"); + try + { + if (!checkbox.Selected) + { + SafeClick(checkbox); + Thread.Sleep(100); + } + } + catch (Exception ex) + { + Console.WriteLine($"전체 선택 중 오류: {ex.Message}"); + } } } @@ -221,9 +263,53 @@ namespace BokBonCheck // 검색어 입력 try { - var searchInput = wait.Until(d => d.FindElement(By.Id("advTitle"))); - searchInput.Clear(); - searchInput.SendKeys(searchTerm); + // 검색 입력창이 상호작용 가능할 때까지 대기 + var searchInput = wait.Until(d => d.FindElement(By.Id("advTitle"))); + var dispaly = searchInput.Displayed; + var enabled = searchInput.Enabled; + + // 요소가 보이도록 스크롤 + ((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].scrollIntoView(true);", searchInput); + Thread.Sleep(300); + + // readonly 속성이 있다면 제거 + ((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].removeAttribute('readonly');", searchInput); + + // 클릭해서 포커스 맞추기 + try + { + searchInput.Click(); + Thread.Sleep(200); + } + catch { } + + // 기존 값 제거 (여러 방법 시도) + try + { + searchInput.Clear(); + } + catch + { + // Clear가 실패하면 JavaScript로 값 제거 + ((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].value = '';", searchInput); + } + + // 검색어 입력 (여러 방법 시도) + try + { + searchInput.SendKeys(searchTerm); + } + catch + { + // SendKeys가 실패하면 JavaScript로 값 설정 + ((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].value = arguments[1];", searchInput, searchTerm); + + // input 이벤트 발생시켜서 웹페이지에 변경사항 알림 + ((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].dispatchEvent(new Event('input'));", searchInput); + } + + Console.WriteLine($"검색어 '{searchTerm}' 입력 완료"); + } catch (Exception ex) { @@ -236,8 +322,27 @@ namespace BokBonCheck // 검색 버튼 클릭 try { - var searchButton = wait.Until(d => d.FindElement(By.CssSelector("a.btTxt.bg.primary"))); - searchButton.Click(); + Console.WriteLine("검색 버튼 찾기 시작..."); + + ((IJavaScriptExecutor)_driver).ExecuteScript(@" + // 검색 버튼을 여러 방법으로 찾아서 클릭 + var searchButtons = document.querySelectorAll('a.btTxt.bg.primary'); + for (var i = 0; i < searchButtons.length; i++) { + var btn = searchButtons[i]; + if (btn.innerText.trim() === '검색' || btn.innerText.includes('검색')) { + btn.click(); + console.log('검색 버튼 클릭 성공'); + break; + } + } + + // 위 방법이 실패하면 마지막 버튼 클릭 + if (searchButtons.length >= 2) { + searchButtons[searchButtons.length - 1].click(); + console.log('마지막 버튼 클릭 성공'); + } + "); + } catch (Exception ex) { @@ -247,6 +352,9 @@ namespace BokBonCheck return result; } + SearchComplete: + Console.WriteLine("검색 실행 완료, 페이지 로딩 대기 중..."); + // 페이지 변경을 감지하는 메서드 await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); @@ -279,6 +387,13 @@ namespace BokBonCheck private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) { errmessage = string.Empty; + + if(driver.Url.EndsWith("DetailSearchResult")==false) + { + errmessage = "결과페이지가아님"; + return -1; + } + try { // 1. 검색결과가 없는 경우 확인 @@ -296,15 +411,46 @@ namespace BokBonCheck // 검색결과가 있는 경우로 진행 } - // 2. 검색 결과 영역에서 직접 추출 시도 + // 2. 안산시 도서관 특화: span.count.gothic em 요소에서 직접 추출 try { - // 결과 요약 정보가 있는 영역 찾기 - var resultInfoElements = driver.FindElements(By.CssSelector(".resultInfo, .result-info, .search-result, .totalResult")); - foreach (var element in resultInfoElements) + var countElement = driver.FindElement(By.CssSelector("span.count.gothic em")); + if (countElement != null) { - var text = element.Text; - var match = Regex.Match(text, @"총\s*(\d+)\s*건", RegexOptions.IgnoreCase); + var countText = countElement.Text.Trim(); + Console.WriteLine($"count 요소 텍스트: '{countText}'"); + + // "총 0 건 " 형태에서 숫자 추출 + var match = Regex.Match(countText, @"총\s*(\d+)\s*건", RegexOptions.IgnoreCase); + if (match.Success && int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + Console.WriteLine("검색 결과: 0건"); + return 0; + } + errmessage = $"검색성공({count}권)"; + Console.WriteLine($"검색 결과: {count}건"); + return count; + } + } + } + catch (Exception ex1) + { + Console.WriteLine($"span.count.gothic em 요소 추출 실패: {ex1.Message}"); + } + + // 3. span.count 전체에서 추출 시도 + try + { + var countSpan = driver.FindElement(By.CssSelector("span.count")); + if (countSpan != null) + { + var fullText = countSpan.Text.Trim(); + Console.WriteLine($"count span 전체 텍스트: '{fullText}'"); + + var match = Regex.Match(fullText, @"총\s*(\d+)\s*건", RegexOptions.IgnoreCase); if (match.Success && int.TryParse(match.Groups[1].Value, out int count)) { if (count == 0) @@ -317,11 +463,14 @@ namespace BokBonCheck } } } - catch { } + catch (Exception ex2) + { + Console.WriteLine($"span.count 요소 추출 실패: {ex2.Message}"); + } // 3. 페이지 소스에서 정규식으로 추출 시도 var pageSource = driver.PageSource; - + // 검색 결과가 없다는 메시지 확인 if (pageSource.Contains("검색결과가 없습니다") || pageSource.Contains("검색된 자료가 없습니다") || @@ -395,9 +544,9 @@ namespace BokBonCheck { var pageSource = d.PageSource; // 검색 결과나 관련 요소가 나타나면 로드 완료 - return pageSource.Contains("검색결과") || + return pageSource.Contains("검색결과") || pageSource.Contains("총") || - pageSource.Contains("검색결과가 없습니다") || + pageSource.Contains("검색결과가 없습니다") || pageSource.Contains("건"); } catch diff --git a/unimarc/unimarc/SearchModel/BukguLibSearcher.cs b/unimarc/unimarc/SearchModel/BukguLibSearcher.cs index 01c6d8d..9822a3c 100644 --- a/unimarc/unimarc/SearchModel/BukguLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/BukguLibSearcher.cs @@ -68,22 +68,6 @@ namespace BokBonCheck { try { - // 전체 선택인 경우 - 모든 체크박스 선택 - if (string.IsNullOrEmpty(AreaCode) || AreaCode == "ALL") - { - var allCheckboxes1 = wait.Until(d => d.FindElements(By.CssSelector("input[name='libCode']"))); - foreach (var checkbox in allCheckboxes1) - { - if (!checkbox.Selected) - { - SafeClick(checkbox); - Thread.Sleep(100); - } - } - Console.WriteLine("전체 도서관 선택됨"); - return true; - } - // 특정 도서관 선택인 경우 var targetCheckboxId = $"lib{AreaCode}"; var targetCheckbox = wait.Until(d => d.FindElement(By.Id(targetCheckboxId))); diff --git a/unimarc/unimarc/마크/Check_copyWD.cs b/unimarc/unimarc/마크/Check_copyWD.cs index 3510ece..6c9b916 100644 --- a/unimarc/unimarc/마크/Check_copyWD.cs +++ b/unimarc/unimarc/마크/Check_copyWD.cs @@ -41,7 +41,7 @@ namespace WindowsFormsApp1.Mac var idx = 1; //도서검색(크롤링) _searchService = new BookSearchService(); - + _searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libMA", "문화정보도서관")); _searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libMC", "청소년도서관")); _searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libSQ", "스마트도서관")); @@ -51,9 +51,6 @@ namespace WindowsFormsApp1.Mac if (GetDemoRunAuth == true) { - - - //광주시교육청통합도서관 idx = 50; _searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MD", "중앙도서관")); @@ -315,23 +312,23 @@ namespace WindowsFormsApp1.Mac _searchService.AddSearcher(new IksanLibSearcher(idx++, "SY", "무학정원작은도서관")); // 안산시중앙도서관 - //idx = 1400; - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MA", "중앙도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MD", "감골도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MB", "관산도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "ME", "단원어린이도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "ND", "대부도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "NF", "미디어도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MH", "반월도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MX", "본오도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "ML", "부곡도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "NK", "상록수도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MF", "상록어린이도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "NC", "선부도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MC", "성포도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MS", "수암도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MR", "원고잔도서관")); - //_searchService.AddSearcher(new AnsanLibSearcher(idx++, "NJ", "월피예술도서관")); + idx = 1400; + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "MA", "중앙도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "MD", "감골도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "MB", "관산도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "ME", "단원어린이도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "ND", "대부도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "NF", "미디어도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "MH", "반월도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "MX", "본오도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "ML", "부곡도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "NK", "상록수도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "MF", "상록어린이도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "NC", "선부도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "MC", "성포도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "MS", "수암도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "MR", "원고잔도서관")); + _searchService.AddSearcher(new AnsanLibSearcher(idx++, "NJ", "월피예술도서관")); }