diff --git a/unimarc/CLAUDE.md b/unimarc/CLAUDE.md index de6683b..d825489 100644 --- a/unimarc/CLAUDE.md +++ b/unimarc/CLAUDE.md @@ -41,4 +41,116 @@ F:\(VHD) Program Files\Microsoft Visual Studio\2022\MSBuild\Current\Bin\msbuild.exe ## 프로젝트 파일명 -UniMarc.csproj \ No newline at end of file +UniMarc.csproj + +## 도서관 검색기(크롤링) 클래스 작성 가이드 + +### 개요 +도서관 웹사이트에서 도서 검색 결과를 크롤링하는 클래스들을 구현합니다. +각 도서관 사이트의 특성에 따라 HTTP 방식 또는 Selenium 방식을 선택합니다. + +### 방식 선택 기준 +- **HTTP 방식**: URL이 변경되는 사이트 (GET 파라미터로 검색) +- **Selenium 방식**: URL이 변경되지 않는 사이트 (POST 폼 제출) + +### 1. HTTP 방식 (GET 요청) +**참고 파일**: `SuncheonLibSearcher.cs` + +**특징**: +- HttpClient 사용 +- URL 파라미터로 검색 +- 빠른 실행 속도 +- HttpApiMode = true + +**주요 구현 포인트**: +```csharp +// URL 구성 +var searchUrl = $"{SiteUrl}?search={encodedSearchTerm}&libCode={AreaCode}"; + +// HTTP 헤더 추가 (500 에러 방지) +request.Headers.Add("User-Agent", "Mozilla/5.0..."); +request.Headers.Add("Accept", "text/html,application/xhtml+xml..."); + +// HTML에서 결과 수 정규식 추출 +var patterns = new[] { + @"총\s*]*class=""cred""[^>]*>(\d+)\s*건" +}; +``` + +### 2. Selenium 방식 (크롤링) +**참고 파일**: `GwangjuCityLibSearcher.cs` + +**특징**: +- Selenium WebDriver 사용 +- 폼 제출 방식 +- 복잡한 UI 상호작용 가능 +- HttpApiMode = false + +**주요 구현 포인트**: +```csharp +// 도서관 선택 (드롭다운) +var libSelect = wait.Until(d => d.FindElement(By.CssSelector("select[name='libCode']"))); +var selectElement = new SelectElement(libSelect); +selectElement.SelectByValue(AreaCode); + +// 검색어 입력 +var searchInput = wait.Until(d => d.FindElement(By.Id("bookSearchQuery"))); +searchInput.Clear(); +searchInput.SendKeys(searchTerm); + +// 검색 버튼 클릭 +var searchButton = wait.Until(d => d.FindElement(By.CssSelector("button.bookSearchBtn"))); +searchButton.Click(); + +// 페이지 로딩 대기 +await WaitForPageChange(wait); +``` + +### 3. 공통 인터페이스 구현 +모든 검색기는 `ILibrarySearcher` 인터페이스를 구현해야 합니다: + +```csharp +public interface ILibrarySearcher +{ + Task SearchAsync(string searchTerm); + Task StartDriver(bool showdriver = false); + void StopDriver(); + string SiteName { get; } + string SiteUrl { get; } + bool HttpApiMode { get; set; } + int No { get; set; } +} +``` + +### 4. Check_copyWD.cs에 등록 +새로운 검색기를 만든 후 반드시 Check_copyWD.cs의 생성자에 추가: + +```csharp +// 예시: 완도군립도서관 +idx = 1200; +_searchService.AddSearcher(new WandoLibSearcher(idx++, "MA", "완도군립도서관")); +_searchService.AddSearcher(new WandoLibSearcher(idx++, "MB", "노화공공도서관")); +``` + +### 5. 결과 추출 패턴 +HTML에서 검색 결과 수를 추출하는 일반적인 패턴들: + +```csharp +var patterns = new[] { + @"총\s*]*>(\d+)\s*건", + @"검색결과\s*총\s*(\d+)\s*건", + @"]*class=""heighlight""[^>]*>(\d+)", + @"총\s*(\d+)\s*권\(개\)" +}; +``` + +### 6. 에러 처리 +- 검색 결과 없음: `return 0` +- 추출 실패: `return -1` +- 예외 발생: BookSearchResult의 ErrorMessage에 설정 + +### 현재 구현된 검색기 목록 +1. HTTP 방식: SuncheonLibSearcher, MokpoLibSearcher, GwangsanLibSearcher, YeosuLibSearcher +2. Selenium 방식: GwangjuCityLibSearcher, GoheungLibSearcher, BukguLibSearcher 계열, JeonbukEduLibSearcher + +각 방식의 장단점을 고려하여 사이트 특성에 맞는 방식을 선택하여 구현하세요. \ No newline at end of file diff --git a/unimarc/unimarc/PUB.cs b/unimarc/unimarc/PUB.cs index 1968a39..c303f7c 100644 --- a/unimarc/unimarc/PUB.cs +++ b/unimarc/unimarc/PUB.cs @@ -4,12 +4,14 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using UniMarc.Properties; namespace UniMarc { public static class PUB { public static arUtil.Log log; + public static UserSetting setting; public static void Init() { @@ -20,6 +22,9 @@ namespace UniMarc PUB.log.SubDirectory = logsubdir; PUB.log.FileNameFormat = "{yyMMdd}"; #endregion + + setting = new UserSetting(); + setting.Load(); } } diff --git a/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs b/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs new file mode 100644 index 0000000..973861b --- /dev/null +++ b/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs @@ -0,0 +1,418 @@ +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; +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 +{ + public class AnsanLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://lib.ansan.go.kr/DetailSearch"; + public bool HttpApiMode { get; set; } = false; + + public int No { get; set; } + + private ChromiumDriver _driver; + + public AnsanLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"안산시립({areaName})"; + } + + public void StopDriver() + { + if (_driver != null) + { + _driver.Quit(); + _driver.Dispose(); + _driver = null; + } + } + + public async Task StartDriver(bool showdriver = false) + { + if (_driver == null) + { + try + { + if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download(); + _driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver); + Console.WriteLine("AnsanLibSearcher Driver 초기화 완료"); + } + catch (Exception ex) + { + Console.WriteLine($"AnsanLibSearcher Driver 초기화 실패: {ex.Message}"); + throw new InvalidOperationException($"AnsanLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex); + } + } + } + + virtual protected bool SelectLibrary(WebDriverWait wait) + { + try + { + // 1. 먼저 모든 전체선택 버튼을 찾아서 클릭하여 기존 선택을 모두 해제 + Console.WriteLine("기존 선택 해제 중..."); + var allSelectButtons = wait.Until(d => d.FindElements(By.CssSelector("div.library span.btnAllSelect"))); + + foreach (var button in allSelectButtons) + { + try + { + // 전체선택 버튼이 "전체선택"인지 "전체해제"인지 확인하고 필요시 클릭 + var buttonText = button.Text.Trim(); + Console.WriteLine($"전체선택 버튼 텍스트: {buttonText}"); + + // 전체선택 버튼을 두 번 클릭하여 모든 선택 해제 (선택 -> 해제) + SafeClick(button); + Thread.Sleep(300); + SafeClick(button); + Thread.Sleep(300); + Console.WriteLine("전체선택 버튼 클릭 완료 (해제)"); + } + catch (Exception ex) + { + Console.WriteLine($"전체선택 버튼 클릭 실패: {ex.Message}"); + } + } + + // 2. 지정된 도서관만 선택 + 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} 도서관 선택 완료"); + } + else + { + Console.WriteLine($"{AreaCode} 도서관 선택 실패, 다시 시도"); + SafeClick(libraryCheckbox); + Thread.Sleep(300); + } + } + else + { + Console.WriteLine("전체 도서관으로 검색 - 모든 도서관 선택"); + // AreaCode가 없으면 첫 번째 전체선택 버튼만 클릭하여 모든 도서관 선택 + if (allSelectButtons.Count > 0) + { + SafeClick(allSelectButtons[0]); + Thread.Sleep(300); + Console.WriteLine("모든 도서관 선택 완료"); + } + } + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"도서관 선택 실패: {ex.Message}"); + return false; + } + } + protected void SafeClick(IWebElement element) + { + // 안정적인 클릭을 위한 여러 방법 시도 + try + { + // 1. 요소가 보이도록 스크롤 후 일반 클릭 + ((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].scrollIntoView(true);", element); + Thread.Sleep(300); + element.Click(); + } + catch + { + try + { + // 2. JavaScript로 클릭 시도 + ((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].click();", element); + } + catch + { + try + { + // 3. Actions 클래스 사용 + var actions = new Actions(_driver); + actions.MoveToElement(element).Click().Perform(); + } + catch + { + try + { + // 4. 체크박스의 경우 직접 체크 상태 변경 + if (element.TagName.ToLower() == "input" && element.GetAttribute("type") == "checkbox") + { + ((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].checked = !arguments[0].checked;", element); + } + else + { + // 일반 요소는 강제 클릭 + ((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].dispatchEvent(new Event('click'));", element); + } + } + catch (Exception ex) + { + Console.WriteLine($"모든 클릭 방법 실패: {ex.Message}"); + } + } + } + } + } + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 드라이버가 없으면 자동으로 시작 + if (_driver == null) + { + await StartDriver(); + } + + var cururl = _driver.Url; + if (cururl.Equals(SiteUrl) == false) + _driver.Navigate().GoToUrl(SiteUrl); + + // 페이지 로딩 대기 + var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15)); + + // 도서관 선택 + if (SelectLibrary(wait) == false) + { + result.ErrorMessage = "도서관선택실패"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색어 입력 + try + { + var searchInput = wait.Until(d => d.FindElement(By.Id("advTitle"))); + searchInput.Clear(); + searchInput.SendKeys(searchTerm); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색어입력실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색 버튼 클릭 + try + { + var searchButton = wait.Until(d => d.FindElement(By.CssSelector("a.btTxt.bg.primary"))); + searchButton.Click(); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색버튼클릭실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 페이지 변경을 감지하는 메서드 + await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = ermsg; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = ermsg; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = ex.Message; + result.BookCount = 0; + } + + return result; + } + + private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + { + errmessage = string.Empty; + try + { + // 1. 검색결과가 없는 경우 확인 + try + { + var noResultElements = driver.FindElements(By.XPath("//*[contains(text(), '검색결과가 없습니다') or contains(text(), '검색된 자료가 없습니다')]")); + if (noResultElements.Count > 0) + { + errmessage = "검색결과없음"; + return 0; + } + } + catch + { + // 검색결과가 있는 경우로 진행 + } + + // 2. 검색 결과 영역에서 직접 추출 시도 + try + { + // 결과 요약 정보가 있는 영역 찾기 + var resultInfoElements = driver.FindElements(By.CssSelector(".resultInfo, .result-info, .search-result, .totalResult")); + foreach (var element in resultInfoElements) + { + var text = element.Text; + var match = Regex.Match(text, @"총\s*(\d+)\s*건", RegexOptions.IgnoreCase); + if (match.Success && int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + catch { } + + // 3. 페이지 소스에서 정규식으로 추출 시도 + var pageSource = driver.PageSource; + + // 검색 결과가 없다는 메시지 확인 + if (pageSource.Contains("검색결과가 없습니다") || + pageSource.Contains("검색된 자료가 없습니다") || + pageSource.Contains("자료가 없습니다") || + pageSource.Contains("총 0 건") || + pageSource.Contains("총 0건")) + { + errmessage = "검색결과없음"; + return 0; + } + + // HTML에서 다양한 패턴 찾기 (안산시 도서관 특화) + var htmlPatterns = new[] + { + @"총\s*(\d+)\s*건", + @"]*>총\s*(\d+)\s*건\s*", + @"검색결과\s*:\s*총\s*(\d+)\s*건", + @"전체\s*(\d+)\s*건", + @"검색결과\s*총\s*(\d+)\s*건", + @"검색\s*결과\s*\(\s*총\s*(\d+)\s*건\s*\)", + @"총\s*<[^>]*>(\d+)]*>\s*건" + }; + + foreach (var pattern in htmlPatterns) + { + var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + + errmessage = "결과수량을찾을수없음"; + return -1; + + } + catch (Exception ex) + { + errmessage = ex.Message; + return -1; + } + } + + // 페이지 변경을 감지하는 메서드 + public async Task WaitForPageChange(WebDriverWait wait) + { + try + { + await Task.Delay(500); + + // 페이지 로딩 상태 확인 + wait.Until(d => + { + var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState"); + return readyState.Equals("complete"); + }); + + // 검색 결과 페이지가 로드될 때까지 대기 + wait.Until(d => + { + try + { + var pageSource = d.PageSource; + // 검색 결과나 관련 요소가 나타나면 로드 완료 + return pageSource.Contains("검색결과") || + pageSource.Contains("총") || + pageSource.Contains("검색결과가 없습니다") || + pageSource.Contains("건"); + } + catch + { + return false; + } + }); + + } + catch (Exception ex) + { + // 모든 감지 방법이 실패하면 최소한의 대기 시간 적용 + await Task.Delay(3000); + Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/BukguLibSearcher.cs b/unimarc/unimarc/SearchModel/BukguLibSearcher.cs new file mode 100644 index 0000000..01c6d8d --- /dev/null +++ b/unimarc/unimarc/SearchModel/BukguLibSearcher.cs @@ -0,0 +1,438 @@ +using AR; +using AR.Dialog; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using OpenQA.Selenium.Chromium; +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 UniMarc.SearchModel; +using UniMarc.마크; +using WebDriverManager; +using WebDriverManager.DriverConfigs.Impl; + +namespace BokBonCheck +{ + public class BukguLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl { get; protected set; } = "https://lib.bukgu.gwangju.kr/main/bookSearchSmartlib.do?PID=0301"; + public bool HttpApiMode { get; set; } = false; + + public int No { get; set; } + + private ChromiumDriver _driver; + + public BukguLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"광주북구스마트도서관({areaName})"; + } + + public void StopDriver() + { + if (_driver != null) + { + _driver.Quit(); + _driver.Dispose(); + _driver = null; + } + } + + public async Task StartDriver(bool showdriver = false) + { + if (_driver == null) + { + try + { + if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download(); + _driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver); + Console.WriteLine("BukguLibSearcher Driver 초기화 완료"); + } + catch (Exception ex) + { + Console.WriteLine($"BukguLibSearcher Driver 초기화 실패: {ex.Message}"); + throw new InvalidOperationException($"BukguLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex); + } + } + } + + virtual protected bool SelectLibrary(WebDriverWait wait) + { + 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))); + + if (targetCheckbox == null) + { + Console.WriteLine($"도서관 체크박스를 찾을 수 없습니다: {targetCheckboxId}"); + return false; + } + + // 다른 체크박스들 해제 + var allCheckboxes = wait.Until(d => d.FindElements(By.CssSelector("input[name='libCode']"))); + foreach (var checkbox in allCheckboxes) + { + if (checkbox.GetAttribute("id") != targetCheckboxId && checkbox.Selected) + { + SafeClick(checkbox); + Thread.Sleep(100); + } + } + + // 대상 체크박스 선택 + if (!targetCheckbox.Selected) + { + SafeClick(targetCheckbox); + Thread.Sleep(300); + Console.WriteLine($"{AreaCode} 도서관으로 변경됨"); + } + else + { + Console.WriteLine($"{AreaCode} 도서관이 이미 선택되어 있음"); + } + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"도서관 선택 실패: {ex.Message}"); + return false; + } + } + + protected void SafeClick(IWebElement element) + { + // 안정적인 클릭을 위한 여러 방법 시도 + try + { + // 1. JavaScript로 클릭 시도 + var driver = ((IWrapsDriver)element).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", element); + } + catch + { + try + { + // 2. 요소가 보이도록 스크롤 후 클릭 + var driver = ((IWrapsDriver)element).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", element); + Thread.Sleep(500); + element.Click(); + } + catch + { + try + { + // 3. Actions 클래스 사용 + var driver = ((IWrapsDriver)element).WrappedDriver; + var actions = new OpenQA.Selenium.Interactions.Actions(driver); + actions.MoveToElement(element).Click().Perform(); + } + catch + { + // 4. 마지막 방법: JavaScript로 직접 체크 상태 변경 + var driver = ((IWrapsDriver)element).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].checked = !arguments[0].checked;", element); + } + } + } + } + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 드라이버가 없으면 자동으로 시작 + if (_driver == null) + { + await StartDriver(); + } + + var cururl = _driver.Url; + if (cururl.Equals(SiteUrl) == false) + _driver.Navigate().GoToUrl(SiteUrl); + + // 페이지 로딩 대기 + var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15)); + + // 도서관 선택 + if (SelectLibrary(wait) == false) + { + result.ErrorMessage = "도서관선택실패"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색어 입력 - 다양한 선택자로 시도 + try + { + IWebElement searchInput = null; + + + try + { + searchInput = wait.Until(d => d.FindElement(By.CssSelector("input[name='searchText']"))); + } + catch { } + + if (searchInput == null) + { + result.ErrorMessage = "검색창을 찾을 수 없음"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + searchInput.Clear(); + searchInput.SendKeys(searchTerm); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색어입력실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색 버튼 클릭 - 다양한 선택자로 시도 + try + { + IWebElement searchButton = null; + try + { + searchButton = wait.Until(d => d.FindElement(By.CssSelector("button[type='submit']"))); + } + catch { } + + + if (searchButton == null) + { + result.ErrorMessage = "검색버튼을 찾을 수 없음"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + searchButton.Click(); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색버튼클릭실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 페이지 변경을 감지하는 메서드 + await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = ermsg; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = ermsg; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = ex.Message; + result.BookCount = 0; + } + + return result; + } + + private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + { + errmessage = string.Empty; + try + { + // 1. totalCount 요소에서 직접 추출 시도 + try + { + var totalCountElement = driver.FindElement(By.CssSelector("p.totalCount")); + if (totalCountElement != null) + { + // strong 태그에서 직접 숫자 추출 시도 + try + { + var strongElement = totalCountElement.FindElement(By.TagName("strong")); + if (strongElement != null) + { + var countText = strongElement.Text.Trim(); + if (int.TryParse(countText, out int strongCount)) + { + if (strongCount == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({strongCount}권)"; + return strongCount; + } + } + } + catch { } + + // 전체 텍스트에서 정규식으로 추출 + var totalCountText = totalCountElement.Text; + var match = Regex.Match(totalCountText, @"전체\s*(\d+)\s*건", RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"totalCount 요소 검색 중 오류: {ex.Message}"); + } + + // 2. 페이지 소스에서 정규식으로 추출 시도 + var pageSource = driver.PageSource; + + // 검색 결과가 없다는 메시지 확인 + if (pageSource.Contains("검색결과가 없습니다") || + pageSource.Contains("검색된 자료가 없습니다") || + pageSource.Contains("자료가 없습니다") || + pageSource.Contains("전체 0 건")) + { + errmessage = "검색결과없음"; + return 0; + } + + // HTML에서 다양한 패턴 찾기 + var htmlPatterns = new[] + { + @"전체\s*\s*(\d+)\s*\s*건", + @"전체\s+(\d+)\s+건", + @"총\s*(\d+)\s*권", + @"총\s*(\d+)\s*건", + @"검색결과\s*:\s*(\d+)" + }; + + foreach (var pattern in htmlPatterns) + { + var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + + errmessage = "결과수량을찾을수없음"; + return -1; + + } + catch (Exception ex) + { + errmessage = ex.Message; + return -1; + } + } + + // 페이지 변경을 감지하는 메서드 + public async Task WaitForPageChange(WebDriverWait wait) + { + try + { + await Task.Delay(500); + + // 페이지 로딩 상태 확인 + wait.Until(d => + { + var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState"); + return readyState.Equals("complete"); + }); + + // 검색 결과 페이지가 로드될 때까지 대기 + wait.Until(d => + { + try + { + var pageSource = d.PageSource; + // 검색 결과나 관련 요소가 나타나면 로드 완료 + return pageSource.Contains("searchTitle") || + pageSource.Contains("totalCount") || + pageSource.Contains("검색결과") || + pageSource.Contains("전체") || + pageSource.Contains("자료가"); + } + catch + { + return false; + } + }); + + } + catch (Exception ex) + { + // 모든 감지 방법이 실패하면 최소한의 대기 시간 적용 + await Task.Delay(3000); + Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/BukguPublicLibSearcher.cs b/unimarc/unimarc/SearchModel/BukguPublicLibSearcher.cs new file mode 100644 index 0000000..8b3c2ac --- /dev/null +++ b/unimarc/unimarc/SearchModel/BukguPublicLibSearcher.cs @@ -0,0 +1,30 @@ +using AR; +using AR.Dialog; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using OpenQA.Selenium.Chromium; +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 UniMarc.SearchModel; +using UniMarc.마크; +using WebDriverManager; +using WebDriverManager.DriverConfigs.Impl; + +namespace BokBonCheck +{ + public class BukguPublicLibSearcher : BukguLibSearcher + { + + public BukguPublicLibSearcher(int no, string areaCode, string areaName) : base(no, areaCode, areaName) + { + this.SiteName = $"광주북구공공도서관({areaName})"; + base.SiteUrl = "https://lib.bukgu.gwangju.kr/main/bookSearch.do?PID=0301"; + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/BukguSlibSearcher.cs b/unimarc/unimarc/SearchModel/BukguSlibSearcher.cs new file mode 100644 index 0000000..0e001d8 --- /dev/null +++ b/unimarc/unimarc/SearchModel/BukguSlibSearcher.cs @@ -0,0 +1,30 @@ +using AR; +using AR.Dialog; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using OpenQA.Selenium.Chromium; +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 UniMarc.SearchModel; +using UniMarc.마크; +using WebDriverManager; +using WebDriverManager.DriverConfigs.Impl; + +namespace BokBonCheck +{ + public class BukguSlibSearcher : BukguLibSearcher + { + + public BukguSlibSearcher(int no, string areaCode, string areaName) : base(no, areaCode, areaName) + { + this.SiteName = $"광주북구작은도서관({areaName})"; + base.SiteUrl = "https://lib.bukgu.gwangju.kr/main/bookSearchSlib.do?PID=0301"; + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/DLSSearcher.cs b/unimarc/unimarc/SearchModel/DLSSearcher.cs index c189f1f..5681e02 100644 --- a/unimarc/unimarc/SearchModel/DLSSearcher.cs +++ b/unimarc/unimarc/SearchModel/DLSSearcher.cs @@ -18,6 +18,7 @@ namespace BokBonCheck { public class DLSSearcher : ILibrarySearcher { + public bool HttpApiMode { get; set; } = false; public string SiteName { get; protected set; } = "DLS"; public string SiteUrl => "https://dls1.edunet.net/DLS/bookMng/bookMain"; diff --git a/unimarc/unimarc/SearchModel/GoheungLibSearcher.cs b/unimarc/unimarc/SearchModel/GoheungLibSearcher.cs new file mode 100644 index 0000000..92ec876 --- /dev/null +++ b/unimarc/unimarc/SearchModel/GoheungLibSearcher.cs @@ -0,0 +1,423 @@ +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; +using System.Runtime.InteropServices; +using System.Threading; +using UniMarc.마크; +using OpenQA.Selenium.Chromium; +using UniMarc.SearchModel; +using System.Runtime.CompilerServices; +using AR; + +namespace BokBonCheck +{ + public class GoheungLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://www.ghlib.go.kr/BookSearch/detail"; + public bool HttpApiMode { get; set; } = false; + + public int No { get; set; } + + private ChromiumDriver _driver; + + public GoheungLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"고흥군립({areaName})"; + } + + public void StopDriver() + { + if (_driver != null) + { + _driver.Quit(); + _driver.Dispose(); + _driver = null; + } + } + + public async Task StartDriver(bool showdriver = false) + { + if (_driver == null) + { + try + { + if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download(); + _driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver); + Console.WriteLine("GoheungLibSearcher Driver 초기화 완료"); + } + catch (Exception ex) + { + Console.WriteLine($"GoheungLibSearcher Driver 초기화 실패: {ex.Message}"); + throw new InvalidOperationException($"GoheungLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex); + } + } + } + + virtual protected bool SelectLibrary(WebDriverWait wait) + { + try + { + // 전체 선택인 경우 + if (string.IsNullOrEmpty(AreaCode) || AreaCode == "ALL") + { + var allRadio = wait.Until(d => d.FindElement(By.Id("libALL"))); + if (!allRadio.Selected) + { + SafeClick(allRadio); + Thread.Sleep(300); + Console.WriteLine("전체도서관 선택됨"); + } + else + { + Console.WriteLine("전체도서관이 이미 선택되어 있음"); + } + return true; + } + + // 특정 도서관 선택인 경우 + var targetRadioId = $"lib{AreaCode}"; + var targetRadio = wait.Until(d => d.FindElement(By.Id(targetRadioId))); + + if (targetRadio == null) + { + Console.WriteLine($"도서관 라디오 버튼을 찾을 수 없습니다: {targetRadioId}"); + return false; + } + + // 이미 선택되어 있지 않으면 선택 + if (!targetRadio.Selected) + { + SafeClick(targetRadio); + Thread.Sleep(300); + Console.WriteLine($"{AreaCode} 도서관으로 변경됨"); + } + else + { + Console.WriteLine($"{AreaCode} 도서관이 이미 선택되어 있음"); + } + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"도서관 선택 실패: {ex.Message}"); + return false; + } + } + + protected void SafeClick(IWebElement element) + { + // 안정적인 클릭을 위한 여러 방법 시도 + try + { + // 1. JavaScript로 클릭 시도 + var driver = ((IWrapsDriver)element).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", element); + } + catch + { + try + { + // 2. 요소가 보이도록 스크롤 후 클릭 + var driver = ((IWrapsDriver)element).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", element); + Thread.Sleep(500); + element.Click(); + } + catch + { + try + { + // 3. Actions 클래스 사용 + var driver = ((IWrapsDriver)element).WrappedDriver; + var actions = new OpenQA.Selenium.Interactions.Actions(driver); + actions.MoveToElement(element).Click().Perform(); + } + catch + { + // 4. 마지막 방법: JavaScript로 직접 체크 + var driver = ((IWrapsDriver)element).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].checked = true;", element); + } + } + } + } + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 드라이버가 없으면 자동으로 시작 + if (_driver == null) + { + await StartDriver(); + } + + var cururl = _driver.Url; + if (cururl.Equals(SiteUrl) == false) + _driver.Navigate().GoToUrl(SiteUrl); + + // 페이지 로딩 대기 + var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15)); + + // 도서관 선택 + if (SelectLibrary(wait) == false) + { + result.ErrorMessage = "도서관선택실패"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 도서명 입력 + try + { + var titleInput = wait.Until(d => d.FindElement(By.Id("queryTitle"))); + titleInput.Clear(); + titleInput.SendKeys(searchTerm); + } + catch (Exception ex) + { + result.ErrorMessage = $"도서명입력실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색 버튼 클릭 + try + { + var searchButton = wait.Until(d => d.FindElement(By.CssSelector("button[type='submit']"))); + searchButton.Click(); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색버튼클릭실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 페이지 변경을 감지하는 메서드 + await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = ermsg; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = ermsg; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = ex.Message; + result.BookCount = 0; + } + + return result; + } + + private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + { + errmessage = string.Empty; + try + { + // 1. 먼저 totalCount 요소에서 직접 추출 시도 + try + { + var totalCountElement = driver.FindElement(By.CssSelector("p.totalCount")); + if (totalCountElement != null) + { + var totalCountText = totalCountElement.Text; // 예: "전체 1 건" + var match = Regex.Match(totalCountText, @"전체\s+(\d+)\s+건", RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + + // strong 태그에서 직접 숫자 추출 시도 + try + { + var strongElement = totalCountElement.FindElement(By.TagName("strong")); + if (strongElement != null && int.TryParse(strongElement.Text, out int strongCount)) + { + if(strongCount == 0) + { + errmessage = "검색결과없음"; + return 0; + } + + errmessage = $"검색성공({strongCount}권)"; + return strongCount; + } + } + catch { } + } + } + catch (Exception ex) + { + Console.WriteLine($"totalCount 요소 검색 중 오류: {ex.Message}"); + } + + // 2. 페이지 소스에서 정규식으로 추출 시도 + var pageSource = driver.PageSource; + + // 검색 결과가 없다는 메시지 확인 + if (pageSource.Contains("검색결과가 없습니다") || + pageSource.Contains("검색된 자료가 없습니다") || + pageSource.Contains("자료가 없습니다") || + pageSource.Contains("전체 0 건")) + { + errmessage = "검색결과없음"; + return 0; + } + + // HTML에서 "전체 N 건" 패턴 찾기 + var htmlPatterns = new[] + { + @"전체\s*(\d+)\s*건", + @"전체\s+(\d+)\s+건", + @"총\s*(\d+)\s*권", + @"총\s*(\d+)\s*건" + }; + + foreach (var pattern in htmlPatterns) + { + var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + + // 3. 결과 테이블이나 리스트가 있는지 확인 + try + { + var resultElements = driver.FindElements(By.CssSelector(".bookList, .searchResult, .result-list, table tbody tr")); + if (resultElements.Count > 0) + { + // 테이블 헤더나 빈 행을 제외한 실제 결과 개수 계산 + var actualCount = 0; + foreach (var element in resultElements) + { + var text = element.Text?.Trim(); + if (!string.IsNullOrEmpty(text) && + !text.Contains("도서명") && + !text.Contains("저자") && + !text.Contains("출판사")) + { + actualCount++; + } + } + + if (actualCount > 0) + { + errmessage = $"검색성공({actualCount}권)"; + return actualCount; + } + } + } + catch (Exception ex) + { + Console.WriteLine($"결과 요소 검색 중 오류: {ex.Message}"); + } + + errmessage = "결과수량을찾을수없음"; + return -1; + + } + catch (Exception ex) + { + errmessage = ex.Message; + return -1; + } + } + + // 페이지 변경을 감지하는 메서드 + public async Task WaitForPageChange(WebDriverWait wait) + { + try + { + await Task.Delay(500); + + // 페이지 로딩 상태 확인 + wait.Until(d => + { + var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState"); + return readyState.Equals("complete"); + }); + + // 검색 결과 페이지가 로드될 때까지 대기 + wait.Until(d => + { + try + { + var pageSource = d.PageSource; + // 검색 결과나 "검색결과가 없습니다" 메시지가 나타나면 로드 완료 + return pageSource.Contains("검색결과") || + pageSource.Contains("총") || + pageSource.Contains("자료가") || + pageSource.Contains("bookList") || + pageSource.Contains("searchResult"); + } + catch + { + return false; + } + }); + + } + catch (Exception ex) + { + // 모든 감지 방법이 실패하면 최소한의 대기 시간 적용 + await Task.Delay(3000); + Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/GwangjuCityLibSearcher.cs b/unimarc/unimarc/SearchModel/GwangjuCityLibSearcher.cs new file mode 100644 index 0000000..9c702dd --- /dev/null +++ b/unimarc/unimarc/SearchModel/GwangjuCityLibSearcher.cs @@ -0,0 +1,346 @@ +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; +using System.Runtime.InteropServices; +using System.Threading; +using UniMarc.마크; +using OpenQA.Selenium.Chromium; +using UniMarc.SearchModel; +using System.Runtime.CompilerServices; +using AR; + +namespace BokBonCheck +{ + public class GwangjuCityLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://citylib.gwangju.go.kr/main/bookSearch"; + public bool HttpApiMode { get; set; } = false; + + public int No { get; set; } + + private ChromiumDriver _driver; + + public GwangjuCityLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"광주시립({areaName})"; + } + + public void StopDriver() + { + if (_driver != null) + { + _driver.Quit(); + _driver.Dispose(); + _driver = null; + } + } + + public async Task StartDriver(bool showdriver = false) + { + if (_driver == null) + { + try + { + if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download(); + _driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver); + Console.WriteLine("GwangjuCityLibSearcher Driver 초기화 완료"); + } + catch (Exception ex) + { + Console.WriteLine($"GwangjuCityLibSearcher Driver 초기화 실패: {ex.Message}"); + throw new InvalidOperationException($"GwangjuCityLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex); + } + } + } + + virtual protected bool SelectLibrary(WebDriverWait wait) + { + try + { + // 도서관 선택 + if (!string.IsNullOrEmpty(AreaCode)) + { + var libSelect = wait.Until(d => d.FindElement(By.CssSelector("select[name='libCode']"))); + var selectElement = new SelectElement(libSelect); + selectElement.SelectByValue(AreaCode); + Thread.Sleep(300); + Console.WriteLine($"{AreaCode} 도서관으로 선택됨"); + } + else + { + Console.WriteLine("전체 도서관으로 검색"); + } + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"도서관 선택 실패: {ex.Message}"); + return false; + } + } + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 드라이버가 없으면 자동으로 시작 + if (_driver == null) + { + await StartDriver(); + } + + var cururl = _driver.Url; + if (cururl.Equals(SiteUrl) == false) + _driver.Navigate().GoToUrl(SiteUrl); + + // 페이지 로딩 대기 + var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15)); + + // 도서관 선택 + if (SelectLibrary(wait) == false) + { + result.ErrorMessage = "도서관선택실패"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색어 입력 + try + { + var searchInput = wait.Until(d => d.FindElement(By.Id("bookSearchQuery"))); + searchInput.Clear(); + searchInput.SendKeys(searchTerm); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색어입력실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색 버튼 클릭 + try + { + var searchButton = wait.Until(d => d.FindElement(By.CssSelector("button.bookSearchBtn"))); + searchButton.Click(); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색버튼클릭실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 페이지 변경을 감지하는 메서드 + await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = ermsg; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = ermsg; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = ex.Message; + result.BookCount = 0; + } + + return result; + } + + private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + { + errmessage = string.Empty; + try + { + // 1. 검색결과가 없는 경우 확인 + try + { + var noResultElement = driver.FindElement(By.XPath("//p[contains(text(), '조회된 도서가 없습니다')]")); + if (noResultElement != null) + { + errmessage = "검색결과없음"; + return 0; + } + } + catch + { + // 검색결과가 있는 경우로 진행 + } + + // 2. srch_info div에서 직접 추출 시도 + try + { + var srchInfoElement = driver.FindElement(By.CssSelector("div.srch_info")); + if (srchInfoElement != null) + { + // span.heighlight에서 숫자 추출 시도 + try + { + var highlightElement = srchInfoElement.FindElement(By.CssSelector("span.heighlight")); + if (highlightElement != null) + { + var countText = highlightElement.Text.Trim(); + if (int.TryParse(countText, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + catch { } + + // 전체 텍스트에서 정규식으로 추출 + var srchInfoText = srchInfoElement.Text; + var match = Regex.Match(srchInfoText, @"검색결과\s*총\s*(\d+)\s*건", RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"srch_info 요소 검색 중 오류: {ex.Message}"); + } + + // 3. 페이지 소스에서 정규식으로 추출 시도 + var pageSource = driver.PageSource; + + // 검색 결과가 없다는 메시지 확인 + if (pageSource.Contains("조회된 도서가 없습니다") || + pageSource.Contains("검색결과가 없습니다") || + pageSource.Contains("검색된 자료가 없습니다")) + { + errmessage = "검색결과없음"; + return 0; + } + + // HTML에서 다양한 패턴 찾기 + var htmlPatterns = new[] + { + @"검색결과\s*총\s*]*class=""heighlight""[^>]*>\s*(\d+)\s*\s*건", + @"검색결과\s*총\s*(\d+)\s*건", + @"]*class=""heighlight""[^>]*>\s*(\d+)\s*", + @"총\s*(\d+)\s*건" + }; + + foreach (var pattern in htmlPatterns) + { + var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + + errmessage = "결과수량을찾을수없음"; + return -1; + + } + catch (Exception ex) + { + errmessage = ex.Message; + return -1; + } + } + + // 페이지 변경을 감지하는 메서드 + public async Task WaitForPageChange(WebDriverWait wait) + { + try + { + await Task.Delay(500); + + // 페이지 로딩 상태 확인 + wait.Until(d => + { + var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState"); + return readyState.Equals("complete"); + }); + + // 검색 결과 페이지가 로드될 때까지 대기 + wait.Until(d => + { + try + { + var pageSource = d.PageSource; + // 검색 결과나 관련 요소가 나타나면 로드 완료 + return pageSource.Contains("srch_info") || + pageSource.Contains("검색결과") || + pageSource.Contains("조회된 도서가 없습니다") || + pageSource.Contains("총") || + pageSource.Contains("건"); + } + catch + { + return false; + } + }); + + } + catch (Exception ex) + { + // 모든 감지 방법이 실패하면 최소한의 대기 시간 적용 + await Task.Delay(3000); + Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/GwangsanLibSearcher.cs b/unimarc/unimarc/SearchModel/GwangsanLibSearcher.cs new file mode 100644 index 0000000..2af3ce3 --- /dev/null +++ b/unimarc/unimarc/SearchModel/GwangsanLibSearcher.cs @@ -0,0 +1,181 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using System.Text.RegularExpressions; +using System.Web; +using UniMarc.SearchModel; +using System.Text; + +namespace BokBonCheck +{ + public class GwangsanLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://lib.gwangsan.go.kr/main/bookSearch/advanced"; + public bool HttpApiMode { get; set; } = true; + + public int No { get; set; } + + private static readonly HttpClient _httpClient = new HttpClient(); + + public GwangsanLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"광주광산구({areaName})"; + } + + public async Task StartDriver(bool showdriver = false) + { + // HTTP 클라이언트 사용으로 별도 드라이버 불필요 + await Task.CompletedTask; + } + + public void StopDriver() + { + // HTTP 클라이언트 사용으로 별도 정리 불필요 + } + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 검색어 URL 인코딩 + var encodedSearchTerm = HttpUtility.UrlEncode(searchTerm, Encoding.UTF8); + + // 상세검색 URL 구성 (도서명만 검색) + var searchUrl = $"{SiteUrl}?query={encodedSearchTerm}&queryPublisher=&queryAuthor=&queryPubYearFr=&queryPubYearTo="; + + // 도서관 코드가 있으면 추가 + if (!string.IsNullOrEmpty(AreaCode)) + { + searchUrl += $"&libCodeArr={AreaCode}"; + } + + Console.WriteLine($"광주광산구 검색 URL: {searchUrl}"); + + // HTTP GET 요청 실행 + var response = await _httpClient.GetAsync(searchUrl); + response.EnsureSuccessStatusCode(); + + var htmlContent = await response.Content.ReadAsStringAsync(); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = errorMessage; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = $"검색성공({resultCount}권)"; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = $"검색 오류: {ex.Message}"; + result.BookCount = 0; + Console.WriteLine($"광주광산구 검색 오류: {ex.Message}"); + } + + return result; + } + + private int ExtractBookCount(string htmlContent, out string errorMessage) + { + errorMessage = string.Empty; + + try + { + // 검색 결과가 없는 경우 확인 + if (htmlContent.Contains("검색결과가 없습니다") || + htmlContent.Contains("검색된 자료가 없습니다") || + htmlContent.Contains("자료가 없습니다")) + { + errorMessage = "검색결과없음"; + return 0; + } + + // HTML에서 "전체 N 건" 패턴 찾기 + var patterns = new[] + { + @"전체\s*\s*(\d+)\s*\s*건", + @"전체\s+(\d+)\s+건", + @"총\s*(\d+)\s*권", + @"총\s*(\d+)\s*건" + }; + + foreach (var pattern in patterns) + { + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errorMessage = "검색결과없음"; + return 0; + } + errorMessage = $"검색성공({count}권)"; + return count; + } + } + } + + // 더 자세한 패턴으로 시도 (줄바꿈 포함) + var multilinePatterns = new[] + { + @"전체\s*\s*\r?\n?\s*(\d+)\s*\r?\n?\s*\s*건", + @"\s*\r?\n?\s*(\d+)\s*\r?\n?\s*\s*건" + }; + + foreach (var pattern in multilinePatterns) + { + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errorMessage = "검색결과없음"; + return 0; + } + errorMessage = $"검색성공({count}권)"; + return count; + } + } + } + + errorMessage = "검색결과 패턴을 찾을 수 없음"; + return -1; + } + catch (Exception ex) + { + errorMessage = $"결과 분석 오류: {ex.Message}"; + return -1; + } + } + + public Task WaitForPageChange(OpenQA.Selenium.Support.UI.WebDriverWait wait) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/ILibrarySearcher.cs b/unimarc/unimarc/SearchModel/ILibrarySearcher.cs index caf98a8..ee33108 100644 --- a/unimarc/unimarc/SearchModel/ILibrarySearcher.cs +++ b/unimarc/unimarc/SearchModel/ILibrarySearcher.cs @@ -11,6 +11,10 @@ namespace BokBonCheck public interface ILibrarySearcher { + /// + /// ũѸ ƴ HTTP ȣ ó˴ϴ + /// + bool HttpApiMode { get; set; } int No { get; set; } string SiteName { get; } string SiteUrl { get; } diff --git a/unimarc/unimarc/SearchModel/IksanLibSearcher.cs b/unimarc/unimarc/SearchModel/IksanLibSearcher.cs new file mode 100644 index 0000000..11019de --- /dev/null +++ b/unimarc/unimarc/SearchModel/IksanLibSearcher.cs @@ -0,0 +1,166 @@ +using System; +using System.Threading.Tasks; +using System.Text.RegularExpressions; +using System.Net.Http; +using System.Collections.Generic; // Added missing import +using OpenQA.Selenium.Support.UI; // Added missing import + +namespace BokBonCheck +{ + public class IksanLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://lib.iksan.go.kr/main/site/search/bookSearch.do"; + public bool HttpApiMode { get; set; } = true; + + public int No { get; set; } + + private static readonly HttpClient _httpClient = new HttpClient(); + + public IksanLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"익산시립({areaName})"; + } + + public void StopDriver() + { + // HTTP 클라이언트 사용으로 별도 정리 불필요 + } + + public async Task StartDriver(bool showdriver = false) + { + // HTTP 클라이언트 사용으로 별도 드라이버 불필요 + await Task.CompletedTask; + } + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 검색어 URL 인코딩 + var encodedSearchTerm = System.Web.HttpUtility.UrlEncode(searchTerm, System.Text.Encoding.UTF8); + + // 검색 URL 구성 - GET 방식으로 URL 파라미터 전송 + var searchUrl = $"{SiteUrl}?cmd_name=bookandnonbooksearch&search_type=detail&detail=OK&use_facet=N&all_lib=N&search_item=search_title&search_txt=&search_title={encodedSearchTerm}"; + + // 도서관 코드가 있으면 추가 + if (!string.IsNullOrEmpty(AreaCode)) + { + searchUrl += $"&manage_code={AreaCode}"; + } + + Console.WriteLine($"익산시통합도서관 검색 요청: {searchTerm}, 도서관코드: {AreaCode}"); + Console.WriteLine($"검색 URL: {searchUrl}"); + + // HTTP GET 요청 실행 + var response = await _httpClient.GetAsync(searchUrl); + response.EnsureSuccessStatusCode(); + + var htmlContent = await response.Content.ReadAsStringAsync(); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = errorMessage; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = $"검색성공({resultCount}권)"; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = ex.Message; + result.BookCount = 0; + } + + return result; + } + + private int ExtractBookCount(string htmlContent, out string errmessage) + { + errmessage = string.Empty; + try + { + // 실제 HTML 구조에 맞는 패턴으로 수정 + var htmlPatterns = new[] + { + // "상세검색결과 3개" 패턴 (실제 구조) + @"상세검색결과\s*]*>\s*(\d+)\s*\s*개", + // "전체 1개가 검색되었습니다" 패턴 (이전 패턴) + @"전체\s*]*class=""word""[^>]*>\s*(\d+)\s*\s*개가\s*검색되었습니다", + // "전체 0개가 검색되었습니다" 패턴 (0인 경우) + @"전체\s*]*class=""word""[^>]*>\s*0\s*\s*개가\s*검색되었습니다", + // 일반적인 "전체 X개가 검색되었습니다" 패턴 + @"전체\s*(\d+)\s*개가\s*검색되었습니다", + // 숫자만 찾는 패턴 (마지막 수단) + @"(\d+)\s*개가\s*검색되었습니다" + }; + + foreach (var pattern in htmlPatterns) + { + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + // 0인 경우를 먼저 확인 + if (pattern.Contains(@"\s*0\s*")) + { + errmessage = "검색결과없음"; + return 0; + } + + // 숫자 추출 + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + + // 디버깅을 위해 HTML 내용 일부 출력 + Console.WriteLine($"HTML 내용 일부: {htmlContent.Substring(0, Math.Min(1000, htmlContent.Length))}"); + + errmessage = "결과수량을찾을수없음"; + return -1; + + } + catch (Exception ex) + { + errmessage = ex.Message; + return -1; + } + } + + // 페이지 변경을 감지하는 메서드 (HTTP 방식에서는 불필요) + public async Task WaitForPageChange(WebDriverWait wait) + { + // HTTP 방식에서는 즉시 응답이 오므로 대기 불필요 + await Task.CompletedTask; + } + + } +} diff --git a/unimarc/unimarc/SearchModel/JeonbukEduLibSearcher.cs b/unimarc/unimarc/SearchModel/JeonbukEduLibSearcher.cs new file mode 100644 index 0000000..e5c53f7 --- /dev/null +++ b/unimarc/unimarc/SearchModel/JeonbukEduLibSearcher.cs @@ -0,0 +1,404 @@ +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; +using System.Runtime.InteropServices; +using System.Threading; +using UniMarc.마크; +using OpenQA.Selenium.Chromium; +using UniMarc.SearchModel; +using System.Runtime.CompilerServices; +using AR; + +namespace BokBonCheck +{ + public class JeonbukEduLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://lib.jbe.go.kr/jbe/intro/search/index.do"; + public bool HttpApiMode { get; set; } = false; + + public int No { get; set; } + + private ChromiumDriver _driver; + + public JeonbukEduLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"전북교육청({areaName})"; + } + + public void StopDriver() + { + if (_driver != null) + { + _driver.Quit(); + _driver.Dispose(); + _driver = null; + } + } + + public async Task StartDriver(bool showdriver = false) + { + if (_driver == null) + { + try + { + if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download(); + _driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver); + Console.WriteLine("JeonbukEduLibSearcher Driver 초기화 완료"); + } + catch (Exception ex) + { + Console.WriteLine($"JeonbukEduLibSearcher Driver 초기화 실패: {ex.Message}"); + throw new InvalidOperationException($"JeonbukEduLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex); + } + } + } + + virtual protected bool SelectLibrary(WebDriverWait wait) + { + try + { + // 전체 선택인 경우 - 모든 체크박스 선택 + if (string.IsNullOrEmpty(AreaCode) || AreaCode == "ALL") + { + var allCheckboxes1 = wait.Until(d => d.FindElements(By.CssSelector("input[name='libraryCodes']"))); + foreach (var checkbox in allCheckboxes1) + { + if (!checkbox.Selected) + { + SafeClick(checkbox); + Thread.Sleep(100); + } + } + Console.WriteLine("전체 도서관 선택됨"); + return true; + } + + // 특정 도서관 선택인 경우 + var targetCheckbox = wait.Until(d => d.FindElement(By.CssSelector($"input[name='libraryCodes'][value='{AreaCode}']"))); + + if (targetCheckbox == null) + { + Console.WriteLine($"도서관 체크박스를 찾을 수 없습니다: {AreaCode}"); + return false; + } + + // 다른 체크박스들 해제 + var allCheckboxes = wait.Until(d => d.FindElements(By.CssSelector("input[name='libraryCodes']"))); + foreach (var checkbox in allCheckboxes) + { + if (checkbox.GetAttribute("value") != AreaCode && checkbox.Selected) + { + SafeClick(checkbox); + Thread.Sleep(100); + } + } + + // 대상 체크박스 선택 + if (!targetCheckbox.Selected) + { + SafeClick(targetCheckbox); + Thread.Sleep(300); + Console.WriteLine($"{AreaCode} 도서관으로 변경됨"); + } + else + { + Console.WriteLine($"{AreaCode} 도서관이 이미 선택되어 있음"); + } + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"도서관 선택 실패: {ex.Message}"); + return false; + } + } + + protected void SafeClick(IWebElement element) + { + // 안정적인 클릭을 위한 여러 방법 시도 + try + { + // 1. JavaScript로 클릭 시도 + var driver = ((IWrapsDriver)element).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", element); + } + catch + { + try + { + // 2. 요소가 보이도록 스크롤 후 클릭 + var driver = ((IWrapsDriver)element).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", element); + Thread.Sleep(500); + element.Click(); + } + catch + { + try + { + // 3. Actions 클래스 사용 + var driver = ((IWrapsDriver)element).WrappedDriver; + var actions = new OpenQA.Selenium.Interactions.Actions(driver); + actions.MoveToElement(element).Click().Perform(); + } + catch + { + // 4. 마지막 방법: JavaScript로 직접 체크 상태 변경 + var driver = ((IWrapsDriver)element).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].checked = !arguments[0].checked;", element); + } + } + } + } + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 드라이버가 없으면 자동으로 시작 + if (_driver == null) + { + await StartDriver(); + } + + var cururl = _driver.Url; + if (cururl.Equals(SiteUrl) == false) + _driver.Navigate().GoToUrl(SiteUrl); + + // 페이지 로딩 대기 + var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15)); + + // 도서관 선택 + if (SelectLibrary(wait) == false) + { + result.ErrorMessage = "도서관선택실패"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색어 입력 + try + { + var searchInput = wait.Until(d => d.FindElement(By.Id("search_text"))); + searchInput.Clear(); + searchInput.SendKeys(searchTerm); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색어입력실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색 버튼 클릭 + try + { + var searchButton = wait.Until(d => d.FindElement(By.Id("do-search"))); + searchButton.Click(); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색버튼클릭실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 페이지 변경을 감지하는 메서드 + await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = ermsg; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = ermsg; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = ex.Message; + result.BookCount = 0; + } + + return result; + } + + private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + { + errmessage = string.Empty; + try + { + // 1. search-info div에서 직접 추출 시도 + try + { + var searchInfoElement = driver.FindElement(By.CssSelector("div.search-info")); + if (searchInfoElement != null) + { + var searchInfoText = searchInfoElement.Text; + + // "총 N건이 검색되었습니다" 패턴 찾기 + var match = Regex.Match(searchInfoText, @"총\s*(\d+)\s*건이\s*검색되었습니다", RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + + // 태그에서 직접 숫자 추출 시도 + try + { + var boldElements = searchInfoElement.FindElements(By.TagName("b")); + foreach (var boldElement in boldElements) + { + var boldText = boldElement.Text.Trim(); + if (int.TryParse(boldText, out int boldCount)) + { + if (boldCount == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({boldCount}권)"; + return boldCount; + } + } + } + catch { } + } + } + catch (Exception ex) + { + Console.WriteLine($"search-info 요소 검색 중 오류: {ex.Message}"); + } + + // 2. 페이지 소스에서 정규식으로 추출 시도 + var pageSource = driver.PageSource; + + // 검색 결과가 없다는 메시지 확인 + if (pageSource.Contains("0건이 검색되었습니다") || + pageSource.Contains("검색결과가 없습니다") || + pageSource.Contains("검색된 자료가 없습니다")) + { + errmessage = "검색결과없음"; + return 0; + } + + // HTML에서 다양한 패턴 찾기 + var htmlPatterns = new[] + { + @"총\s*(\d+)\s*건이\s*검색되었습니다", + @"총\s*(\d+)\s*건이\s*검색되었습니다", + @"총\s*\s*(\d+)\s*\s*건", + @"총\s*(\d+)\s*건" + }; + + foreach (var pattern in htmlPatterns) + { + var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + + errmessage = "결과수량을찾을수없음"; + return -1; + + } + catch (Exception ex) + { + errmessage = ex.Message; + return -1; + } + } + + // 페이지 변경을 감지하는 메서드 + public async Task WaitForPageChange(WebDriverWait wait) + { + try + { + await Task.Delay(500); + + // 페이지 로딩 상태 확인 + wait.Until(d => + { + var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState"); + return readyState.Equals("complete"); + }); + + // 검색 결과 페이지가 로드될 때까지 대기 + wait.Until(d => + { + try + { + var pageSource = d.PageSource; + // search-info div나 검색 결과 관련 요소가 나타나면 로드 완료 + return pageSource.Contains("search-info") || + pageSource.Contains("검색되었습니다") || + pageSource.Contains("검색결과") || + pageSource.Contains("총") || + pageSource.Contains("건이"); + } + catch + { + return false; + } + }); + + } + catch (Exception ex) + { + // 모든 감지 방법이 실패하면 최소한의 대기 시간 적용 + await Task.Delay(3000); + Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/JunnamEduJiheaNuriSearcher.cs b/unimarc/unimarc/SearchModel/JunnamEduJiheaNuriSearcher.cs new file mode 100644 index 0000000..91bf525 --- /dev/null +++ b/unimarc/unimarc/SearchModel/JunnamEduJiheaNuriSearcher.cs @@ -0,0 +1,413 @@ +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; +using System.Runtime.InteropServices; +using System.Threading; +using UniMarc.마크; +using OpenQA.Selenium.Chromium; +using UniMarc.SearchModel; +using System.Runtime.CompilerServices; +using AR; + +namespace BokBonCheck +{ + + public class JunnamEduJiheaNuriSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://jnelib.jne.go.kr/book/search_book/search.es?mid=d20101000000"; + public bool HttpApiMode { get; set; } = false; + public int No { get; set; } + + private ChromiumDriver _driver; + + public JunnamEduJiheaNuriSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"전남교육청행정자료실({areaName})"; + } + + + + public void StopDriver() + { + if (_driver != null) + { + _driver.Quit(); + _driver.Dispose(); + _driver = null; + } + } + + public async Task StartDriver(bool showdriver = false) + { + if (_driver == null) + { + try + { + if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download(); + _driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver); + Console.WriteLine("JunnamEduJiheaNuriSearcher Driver 초기화 완료"); + } + catch (Exception ex) + { + Console.WriteLine($"JunnamEduJiheaNuriSearcher Driver 초기화 실패: {ex.Message}"); + throw new InvalidOperationException($"JunnamEduJiheaNuriSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex); + } + } + } + + + + virtual protected bool SelectLibrary(WebDriverWait wait) + { + try + { + if (this.AreaCode.isEmpty()) return true; + + + + // Areacode가 "ALL"인 경우 + if (AreaCode == "ALL") + { + var allCheckbox = wait.Until(d => d.FindElement(By.Id("all_srch"))); + if (!allCheckbox.Selected) + { + SafeClick(allCheckbox); + Thread.Sleep(300); + Console.WriteLine("전체 선택으로 변경됨"); + } + else + { + Console.WriteLine("전체 선택이 이미 활성화되어 있음"); + } + return true; + } + + + // 특정 지역 선택인 경우 + var targetSelector = $"libInfo_{AreaCode}"; + var targetCheckbox = wait.Until(d => d.FindElement(By.Id(targetSelector))); + + if (targetCheckbox == null) + { + Console.WriteLine($"도서관 체크박스를 찾을 수 없습니다: {targetSelector}"); + return false; + } + + // 전체 선택이 되어 있으면 해제 + var allCheckbox2 = wait.Until(d => d.FindElement(By.Id("all_srch"))); + if (allCheckbox2.Selected) + { + SafeClick(allCheckbox2); + Thread.Sleep(300); + Console.WriteLine("전체 선택 해제됨"); + } + + // 대상 지역 선택 + if (!targetCheckbox.Selected) + { + SafeClick(targetCheckbox); + Thread.Sleep(300); + Console.WriteLine($"{AreaCode} 지역으로 변경됨"); + } + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"도서관 선택 실패: {ex.Message}"); + return false; + } + } + + protected void SafeClick(IWebElement searchBox) + { + // 안정적인 클릭을 위한 여러 방법 시도 + try + { + // 1. JavaScript로 클릭 시도 + var driver = ((IWrapsDriver)searchBox).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", searchBox); + } + catch + { + try + { + // 2. 요소가 보이도록 스크롤 후 클릭 + var driver = ((IWrapsDriver)searchBox).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", searchBox); + Thread.Sleep(500); + searchBox.Click(); + } + catch + { + try + { + // 3. Actions 클래스 사용 + var driver = ((IWrapsDriver)searchBox).WrappedDriver; + var actions = new OpenQA.Selenium.Interactions.Actions(driver); + actions.MoveToElement(searchBox).Click().Perform(); + } + catch + { + // 4. 마지막 방법: JavaScript로 직접 체크 해제 + var driver = ((IWrapsDriver)searchBox).WrappedDriver; + ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].checked = false;", searchBox); + } + } + } + } + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 드라이버가 없으면 자동으로 시작 + if (_driver == null) + { + await StartDriver(); + } + + var cururl = _driver.Url; + if (cururl.Equals(SiteUrl) == false) + _driver.Navigate().GoToUrl(SiteUrl); + + // 페이지 로딩 대기 + var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15)); + + // 도서관 선택 + if (SelectLibrary(wait) == false) + { + result.ErrorMessage = "도서관선택실패"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색 방법을 서명으로 변경 + try + { + var searchKeyWordSelect = wait.Until(d => d.FindElement(By.Id("searchKeyWord"))); + var selectElement = new SelectElement(searchKeyWordSelect); + selectElement.SelectByValue("1"); // 1 = 서명 + Thread.Sleep(300); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색방법선택실패({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색어 입력 + IWebElement searchBox = null; + try + { + searchBox = wait.Until(d => d.FindElement(By.Id("searchWordText"))); + searchBox.Clear(); + searchBox.SendKeys(searchTerm); + } + catch (Exception ex) + { + result.ErrorMessage = $"검색창없음({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 검색 버튼 클릭 + IWebElement searchButton = null; + try + { + searchButton = _driver.FindElement(By.CssSelector("button[type='submit']")); + if (searchButton != null) + searchButton.Click(); + else + { + result.ErrorMessage = $"검색버튼없음"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + } + catch (Exception ex) + { + result.ErrorMessage = $"검색버튼없음({ex.Message})"; + result.BookCount = -1; + result.IsSuccess = false; + return result; + } + + // 페이지 변경을 감지하는 메서드 + await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = ermsg; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = ermsg; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = ex.Message; + result.BookCount = 0; + } + + return result; + } + + private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + { + errmessage = string.Empty; + try + { + // 먼저 검색결과가 없는 경우 확인 + try + { + var noResultDiv = driver.FindElement(By.CssSelector(".SearchResult .resultBook")); + var noResultText = noResultDiv.Text; + if (noResultText.Contains("검색결과가 없습니다")) + { + errmessage = "검색결과없음"; + return 0; + } + } + catch + { + // 검색결과가 없는 경우 div가 없을 수도 있음 + } + + // 검색결과가 있는 경우 p.page_info에서 전체 건수 추출 + try + { + var pageInfo = driver.FindElement(By.CssSelector("p.page_info")); + var pageInfoText = pageInfo.Text; // 예: "전체 7건, 현재 페이지 1/1" + + var match = System.Text.RegularExpressions.Regex.Match(pageInfoText, @"전체\s*(\d+)건"); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int vqty)) + { + errmessage = $"검색성공({vqty}건)"; + return vqty; + } + else + { + errmessage = $"수량값오류({match.Groups[1].Value})"; + return -1; + } + } + else + { + errmessage = "수량항목없음"; + return -1; + } + } + catch (Exception ex) + { + // page_info가 없는 경우 검색결과가 없는 것으로 판단 + errmessage = "검색결과없음"; + return 0; + } + + } + catch (Exception ex) + { + errmessage = ex.Message; + return -1; + } + } + + + + // 페이지 변경을 감지하는 메서드 + public async Task WaitForPageChange(WebDriverWait wait) + { + try + { + await Task.Delay(500); + + // 페이지 로딩 상태 확인 + wait.Until(d => + { + var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState"); + return readyState.Equals("complete"); + }); + + // 검색 결과 페이지가 로드될 때까지 대기 + wait.Until(d => + { + try + { + // 검색결과가 없는 경우 확인 + try + { + var noResultDiv = d.FindElement(By.CssSelector(".SearchResult .resultBook")); + if (noResultDiv.Text.Contains("검색결과가 없습니다")) + { + return true; // 검색결과 없음 페이지 로드 완료 + } + } + catch + { + // 검색결과가 있는 경우로 진행 + } + + // 검색결과가 있는 경우 page_info 확인 + try + { + var pageInfo = d.FindElement(By.CssSelector("p.page_info")); + var pageInfoText = pageInfo.Text; + // "전체 N건" 형식이 나타나면 로드 완료 + return pageInfoText.Contains("전체") && pageInfoText.Contains("건"); + } + catch + { + return false; + } + } + catch + { + return false; + } + }); + + } + catch (Exception ex) + { + // 모든 감지 방법이 실패하면 최소한의 대기 시간 적용 + await Task.Delay(2000); + throw new Exception($"페이지 변경 감지 실패: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/JunnamEduSearcher.cs b/unimarc/unimarc/SearchModel/JunnamEduSearcher.cs index 9ba3a41..b8f34d5 100644 --- a/unimarc/unimarc/SearchModel/JunnamEduSearcher.cs +++ b/unimarc/unimarc/SearchModel/JunnamEduSearcher.cs @@ -19,10 +19,10 @@ namespace BokBonCheck public class JunnamEduSearcher : ILibrarySearcher { - public string Areacode { get; set; } = string.Empty; + protected string AreaCode { get; set; } = string.Empty; public string SiteName { get; protected set; } public string SiteUrl => "https://jnelib.jne.go.kr/book/search_book/search.es?mid=d50101000000"; - + public bool HttpApiMode { get; set; } = false; public int No { get; set; } private ChromiumDriver _driver; @@ -30,7 +30,7 @@ namespace BokBonCheck public JunnamEduSearcher(int no, string areaCode, string areaName) { this.No = no; - this.Areacode = areaCode; + this.AreaCode = areaCode; this.SiteName = $"전남교육청({areaName})"; } @@ -71,28 +71,28 @@ namespace BokBonCheck try { // Areacode가 "ALL"인 경우 - if (Areacode == "ALL") + if (AreaCode == "ALL") { - var allCheckbox = wait.Until(d => d.FindElement(By.CssSelector("#all_srch"))); - + var allCheckbox1 = wait.Until(d => d.FindElement(By.CssSelector("#all_srch"))); + // 이미 전체가 선택되어 있으면 변경하지 않음 - if (allCheckbox.Selected) + if (allCheckbox1.Selected) { Console.WriteLine("전체 선택이 이미 활성화되어 있음"); return true; } - + // 전체 선택이 안되어 있으면 활성화 - SafeClick(allCheckbox); + SafeClick(allCheckbox1); Thread.Sleep(300); Console.WriteLine("전체 선택으로 변경됨"); return true; } // 특정 지역 선택인 경우 - var targetSelector = $"#libInfo_{Areacode}"; + var targetSelector = $"#libInfo_{AreaCode}"; var targetCheckbox = wait.Until(d => d.FindElement(By.CssSelector(targetSelector))); - + if (targetCheckbox == null) { Console.WriteLine($"도서관 체크박스를 찾을 수 없습니다: {targetSelector}"); @@ -107,7 +107,7 @@ namespace BokBonCheck // 이미 원하는 지역이 선택되어 있고, 전체 선택이 해제되어 있으면 변경하지 않음 if (isTargetSelected && !isAllSelected) { - Console.WriteLine($"{Areacode} 지역이 이미 선택되어 있음"); + Console.WriteLine($"{AreaCode} 지역이 이미 선택되어 있음"); return true; } @@ -124,9 +124,9 @@ namespace BokBonCheck { SafeClick(targetCheckbox); Thread.Sleep(300); - Console.WriteLine($"{Areacode} 지역으로 변경됨"); + Console.WriteLine($"{AreaCode} 지역으로 변경됨"); } - + return true; } catch (Exception ex) @@ -191,7 +191,9 @@ namespace BokBonCheck await StartDriver(); } - _driver.Navigate().GoToUrl(SiteUrl); + var cururl = _driver.Url; + if (cururl.Equals(SiteUrl) == false) + _driver.Navigate().GoToUrl(SiteUrl); // 페이지 로딩 대기 var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15)); @@ -247,7 +249,7 @@ namespace BokBonCheck try { searchButton = _driver.FindElement(By.CssSelector("button[type='submit']")); - if(searchButton != null) + if (searchButton != null) searchButton.Click(); else { @@ -321,7 +323,7 @@ namespace BokBonCheck { var pageInfo = driver.FindElement(By.CssSelector("p.page_info")); var pageInfoText = pageInfo.Text; // 예: "전체 7건, 현재 페이지 1/1" - + var match = System.Text.RegularExpressions.Regex.Match(pageInfoText, @"전체\s*(\d+)건"); if (match.Success) { diff --git a/unimarc/unimarc/SearchModel/KwangjuCityLibrarySearcher.cs b/unimarc/unimarc/SearchModel/KwangjuCityEduLibrarySearcher.cs similarity index 79% rename from unimarc/unimarc/SearchModel/KwangjuCityLibrarySearcher.cs rename to unimarc/unimarc/SearchModel/KwangjuCityEduLibrarySearcher.cs index 03bce37..9b7716d 100644 --- a/unimarc/unimarc/SearchModel/KwangjuCityLibrarySearcher.cs +++ b/unimarc/unimarc/SearchModel/KwangjuCityEduLibrarySearcher.cs @@ -13,73 +13,23 @@ using OpenQA.Selenium.Chromium; namespace BokBonCheck { - public class KwangjuCityLibrarySearcher_choisangjun : KwangjuCityLibrarySearcher - { - public KwangjuCityLibrarySearcher_choisangjun(int no) : base(no) - { - SelectorValue = "MF"; - SiteName = "광주시교육청통합도서관(분관최상준도서관)"; - } - } - public class KwangjuCityLibrarySearcher_dagachi : KwangjuCityLibrarySearcher - { - public KwangjuCityLibrarySearcher_dagachi(int no) : base(no) - { - SelectorValue = "ME"; - SiteName = "광주시교육청통합도서관(송정다가치문화도서관)"; - } - } - public class KwangjuCityLibrarySearcher_central : KwangjuCityLibrarySearcher - { - public KwangjuCityLibrarySearcher_central(int no) : base(no) - { - SelectorValue = "MD"; - SiteName = "광주시교육청통합도서관(중앙도서관)"; - } - } - public class KwangjuCityLibrarySearcher_Student : KwangjuCityLibrarySearcher - { - public KwangjuCityLibrarySearcher_Student(int no) : base(no) - { - SelectorValue = "MC"; - SiteName = "광주시교육청통합도서관(학생교육문화회관)"; - } - } - public class KwangjuCityLibrarySearcher_Kumho : KwangjuCityLibrarySearcher - { - public KwangjuCityLibrarySearcher_Kumho(int no) : base(no) - { - SelectorValue = "MB"; - SiteName = "광주시교육청통합도서관(금호평생교육관)"; - } - } - public class KwangjuCityLibrarySearcher_Indi : KwangjuCityLibrarySearcher - { - public KwangjuCityLibrarySearcher_Indi(int no) : base(no) - { - SelectorValue = "MA"; - SiteName = "광주시교육청통합도서관(학생독립운동기념회관)"; - } - } - public abstract class KwangjuCityLibrarySearcher : ILibrarySearcher + public class KwangjuCityEduLibrarySearcher : ILibrarySearcher { public int No { get; set; } + + protected string AreaCode = ""; public string SiteName { get; protected set; } = "광주시교육청통합도서관"; public string SiteUrl => "https://lib.gen.go.kr/main/site/search/bookSearch.do#simple"; - protected string SelectorValue = ""; + public bool HttpApiMode { get; set; } = false; private ChromiumDriver _driver; - [DllImport("user32.dll")] - private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); - [DllImport("user32.dll")] - private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); - private const int SW_MINIMIZE = 6; - - public KwangjuCityLibrarySearcher(int no) + public KwangjuCityEduLibrarySearcher(int no, string areaCode, string areaName) { this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"광주시교육청통합도서관({areaName})"; } public async Task StartDriver(bool showBrowser = false) @@ -119,7 +69,7 @@ namespace BokBonCheck var select = new OpenQA.Selenium.Support.UI.SelectElement(selectElement); // value가 "MA"인 옵션 선택 - select.SelectByValue(SelectorValue); + select.SelectByValue(AreaCode); } catch { diff --git a/unimarc/unimarc/SearchModel/MokpoLibSearcher.cs b/unimarc/unimarc/SearchModel/MokpoLibSearcher.cs new file mode 100644 index 0000000..b4eada6 --- /dev/null +++ b/unimarc/unimarc/SearchModel/MokpoLibSearcher.cs @@ -0,0 +1,188 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using System.Text.RegularExpressions; +using System.Web; +using UniMarc.SearchModel; +using System.Text; + +namespace BokBonCheck +{ + public class MokpoLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://mokpolib.or.kr/dls_lt/index.php"; + public bool HttpApiMode { get; set; } = true; + + public int No { get; set; } + + private static readonly HttpClient _httpClient = new HttpClient(); + + public MokpoLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"목포시립({areaName})"; + } + + public async Task StartDriver(bool showdriver = false) + { + // HTTP 클라이언트 사용으로 별도 드라이버 불필요 + await Task.CompletedTask; + } + + public void StopDriver() + { + // HTTP 클라이언트 사용으로 별도 정리 불필요 + } + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 검색어 URL 인코딩 + var encodedSearchTerm = HttpUtility.UrlEncode(searchTerm, Encoding.UTF8); + + // 검색 URL 구성 + var searchUrl = $"{SiteUrl}?mod=wdDataSearch&act=searchResultList&manageSearch=&detailSearch="; + + // 도서관 코드가 있으면 추가 + if (!string.IsNullOrEmpty(AreaCode)) + { + searchUrl += $"&manageCode%5B{AreaCode}%5D={AreaCode}"; + } + + // 검색 방식을 제목으로 설정하고 검색어 추가 + searchUrl += $"&searchItem%5B%5D=title&searchWord%5B%5D={encodedSearchTerm}"; + + Console.WriteLine($"목포시립도서관 검색 URL: {searchUrl}"); + + // HTTP GET 요청 실행 + var response = await _httpClient.GetAsync(searchUrl); + response.EnsureSuccessStatusCode(); + + var htmlContent = await response.Content.ReadAsStringAsync(); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = errorMessage; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = $"검색성공({resultCount}권)"; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = $"검색 오류: {ex.Message}"; + result.BookCount = 0; + Console.WriteLine($"목포시립도서관 검색 오류: {ex.Message}"); + } + + return result; + } + + private int ExtractBookCount(string htmlContent, out string errorMessage) + { + errorMessage = string.Empty; + + try + { + // HTML에서 "총 N권(개)" 패턴 찾기 + var patterns = new[] + { + @"총\s*]*class=""cyan""[^>]*>(\d+)권\(개\)", + @"총\s*]*>(\d+)권\(개\)", + @"총\s*(\d+)권\(개\)", + @"총\s*]*class=""cyan""[^>]*>(\d+)", + @"총\s*]*>(\d+)" + }; + + foreach (var pattern in patterns) + { + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errorMessage = "검색결과없음"; + return 0; + } + errorMessage = $"검색성공({count}권)"; + return count; + } + } + } + + // 검색 결과가 없다는 메시지 확인 + if (htmlContent.Contains("검색결과가 없습니다") || + htmlContent.Contains("검색된 자료가 없습니다") || + htmlContent.Contains("자료가 없습니다") || + htmlContent.Contains("총 0권(개)")) + { + errorMessage = "검색결과없음"; + return 0; + } + + // 더 넓은 범위로 숫자 찾기 시도 + var generalPatterns = new[] + { + @"총\s*.*?(\d+)\s*권", + @"총\s*.*?(\d+)\s*개", + @"총\s*.*?(\d+)", + @"]*class=""cyan""[^>]*>(\d+)" + }; + + foreach (var pattern in generalPatterns) + { + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errorMessage = "검색결과없음"; + return 0; + } + errorMessage = $"검색성공({count}권)"; + return count; + } + } + } + + errorMessage = "검색결과 패턴을 찾을 수 없음"; + return -1; + } + catch (Exception ex) + { + errorMessage = $"결과 분석 오류: {ex.Message}"; + return -1; + } + } + + public Task WaitForPageChange(OpenQA.Selenium.Support.UI.WebDriverWait wait) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/NamguLibrarySearcher.cs b/unimarc/unimarc/SearchModel/NamguLibrarySearcher.cs index b35065a..b4b462c 100644 --- a/unimarc/unimarc/SearchModel/NamguLibrarySearcher.cs +++ b/unimarc/unimarc/SearchModel/NamguLibrarySearcher.cs @@ -17,198 +17,14 @@ using System.Runtime.CompilerServices; namespace BokBonCheck { - - public class NamguLibrarySearcher_Munhwa : NamguLibrarySearcher - { - public NamguLibrarySearcher_Munhwa(int no) : base(no) - { - SiteName = "남구통합도서관(문화정보도서관)"; // 문화관 검색기 - } - - protected override bool SelectLibrary(WebDriverWait wait) - { - IWebElement searchBox = null; - try - { - var selector = "#clickAll"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == true) - { - SafeClick(searchBox); - } - - selector = "#libMA"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == false) - { - SafeClick(searchBox); - } - - return true; - } - catch - { - return false; - } - } - } - public class NamguLibrarySearcher_Purungil : NamguLibrarySearcher - { - public NamguLibrarySearcher_Purungil(int no) : base(no) - { - SiteName = "남구통합도서관(푸른길도서관)"; - } - - protected override bool SelectLibrary(WebDriverWait wait) - { - IWebElement searchBox = null; - try - { - var selector = "#clickAll"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == true) - { - SafeClick(searchBox); - } - - selector = "#libMB"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == false) - { - SafeClick(searchBox); - } - - return true; - } - catch - { - return false; - } - } - } - public class NamguLibrarySearcher_Children : NamguLibrarySearcher - { - public NamguLibrarySearcher_Children(int no) : base(no) - { - SiteName = "남구통합도서관(청소년도서관)"; - } - - protected override bool SelectLibrary(WebDriverWait wait) - { - IWebElement searchBox = null; - try - { - var selector = "#clickAll"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == true) - { - SafeClick(searchBox); - - } - - selector = "#libMC"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == false) - { - //searchBox.Click(); // 체크박스 선택 - SafeClick(searchBox); - - } - return true; - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - return false; - } - } - } - public class NamguLibrarySearcher_Hyocheon : NamguLibrarySearcher - { - public NamguLibrarySearcher_Hyocheon(int no) : base(no) - { - SiteName = "남구통합도서관(효천어울림도서관)"; - } - - - protected override bool SelectLibrary(WebDriverWait wait) - { - IWebElement searchBox = null; - try - { - var selector = "#clickAll"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == true) - { - SafeClick(searchBox); - } - - - selector = "#libSW"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == false) - { - SafeClick(searchBox); - } - - return true; - } - catch - { - return false; - } - } - } - public class NamguLibrarySearcher_Smart : NamguLibrarySearcher - { - public NamguLibrarySearcher_Smart(int no) : base(no) - { - SiteName = "남구통합도서관(스마트도서관)"; - } - - protected override bool SelectLibrary(WebDriverWait wait) - { - IWebElement searchBox = null; - try - { - var selector = "#clickAll"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == true) - { - SafeClick(searchBox); - } - - - selector = "#libSQ"; - searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); - if (searchBox == null) return false; - if (searchBox.Selected == false) - { - SafeClick(searchBox); - } - - return true; - } - catch - { - return false; - } - } - } + + public class NamguLibrarySearcher : ILibrarySearcher { + protected string AreaCode = ""; public string SiteName { get; protected set; } = "남구통합도서관(전체)"; public string SiteUrl => "https://lib.namgu.gwangju.kr/main/bookSearch"; - + public bool HttpApiMode { get; set; } = false; public int No { get; set; } private ChromiumDriver _driver; @@ -219,9 +35,11 @@ namespace BokBonCheck private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); private const int SW_MINIMIZE = 6; - public NamguLibrarySearcher(int no) + public NamguLibrarySearcher(int no, string areaCode, string areaName) { this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"남구통합도서관({areaName})"; } @@ -264,15 +82,24 @@ namespace BokBonCheck var selector = "#clickAll"; searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); if (searchBox == null) return false; + if (searchBox.Selected == true) + { + SafeClick(searchBox); + } + + + selector = AreaCode; + searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector))); + if (searchBox == null) return false; if (searchBox.Selected == false) { SafeClick(searchBox); } + return true; } - catch (Exception ex) + catch { - Console.WriteLine(ex.Message); return false; } } diff --git a/unimarc/unimarc/SearchModel/SuncheonLibSearcher.cs b/unimarc/unimarc/SearchModel/SuncheonLibSearcher.cs new file mode 100644 index 0000000..f6bc42b --- /dev/null +++ b/unimarc/unimarc/SearchModel/SuncheonLibSearcher.cs @@ -0,0 +1,229 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using System.Text.RegularExpressions; +using System.Web; +using UniMarc.SearchModel; +using System.Text; + +namespace BokBonCheck +{ + public class SuncheonLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://library.suncheon.go.kr/lib/book/search/searchIndex.do"; + public bool HttpApiMode { get; set; } = true; + + public int No { get; set; } + + private static readonly HttpClient _httpClient = new HttpClient() + { + DefaultRequestHeaders = + { + { "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } + } + }; + + public SuncheonLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"순천시립({areaName})"; + } + + public async Task StartDriver(bool showdriver = false) + { + // HTTP 클라이언트 사용으로 별도 드라이버 불필요 + await Task.CompletedTask; + } + + public void StopDriver() + { + // HTTP 클라이언트 사용으로 별도 정리 불필요 + } + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 검색어 URL 인코딩 + var encodedSearchTerm = HttpUtility.UrlEncode(searchTerm, Encoding.UTF8); + + // 검색 URL 구성 + var searchUrl = $"{SiteUrl}?menuCd=L001001001&alpha=&vcindex=¤tPageNo=1&nPageSize=10&searchType=title&search={encodedSearchTerm}&mediaCode="; + + // 도서관 코드가 있으면 추가 + if (!string.IsNullOrEmpty(AreaCode)) + { + if (AreaCode == "mini") + { + // 작은도서관의 경우 모든 코드를 포함 + searchUrl += "&manageCd=AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,BB,BC,BD,BF,BG,BH,BI,BJ,BM,BQ,BS,BW,CA,CB,CC,CD,CE,CF,CG,CH,CI,CJ,CK,CL,CM,CN,CO,CP,CQ,DA,DB,DC,DD,DE,DF,DG,DH,DI,DJ,DK,DL,DS,GD"; + } + else + { + searchUrl += $"&manageCd={AreaCode}"; + } + } + + Console.WriteLine($"순천시립도서관 검색 URL: {searchUrl}"); + + // HTTP GET 요청 실행 (추가 헤더 포함) + using (var request = new HttpRequestMessage(HttpMethod.Get, searchUrl)) + { + // 브라우저와 유사한 헤더 추가 + request.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); + request.Headers.Add("Accept-Language", "ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3"); + request.Headers.Add("Accept-Encoding", "gzip, deflate, br"); + request.Headers.Add("Connection", "keep-alive"); + request.Headers.Add("Upgrade-Insecure-Requests", "1"); + + var response = await _httpClient.SendAsync(request); + + if (!response.IsSuccessStatusCode) + { + var errorContent = await response.Content.ReadAsStringAsync(); + throw new HttpRequestException($"HTTP {(int)response.StatusCode} {response.StatusCode}: {errorContent}"); + } + + var htmlContent = await response.Content.ReadAsStringAsync(); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = errorMessage; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = $"검색성공({resultCount}권)"; + } + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = $"검색 오류: {ex.Message}"; + result.BookCount = 0; + Console.WriteLine($"순천시립도서관 검색 오류: {ex.Message}"); + } + + return result; + } + + private int ExtractBookCount(string htmlContent, out string errorMessage) + { + errorMessage = string.Empty; + + try + { + // HTML에서 "총 N건" 패턴 찾기 + var patterns = new[] + { + @"총\s*]*class=""cred""[^>]*>(\d+)\s*건", + @"총\s*]*>(\d+)\s*건", + @"총\s*(\d+)\s*건", + @"]*class=""cred""[^>]*>(\d+)" + }; + + foreach (var pattern in patterns) + { + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errorMessage = "검색결과없음"; + return 0; + } + errorMessage = $"검색성공({count}권)"; + return count; + } + } + } + + // 검색 결과가 없다는 메시지 확인 + if (htmlContent.Contains("검색결과가 없습니다") || + htmlContent.Contains("검색된 자료가 없습니다") || + htmlContent.Contains("자료가 없습니다") || + htmlContent.Contains("총 0건")) + { + errorMessage = "검색결과없음"; + return 0; + } + + // resultCon 영역에서 더 자세히 검색 + var resultConPattern = @"]*class=""resultCon[^""]*""[^>]*>.*?총\s*]*>(\d+)\s*건.*?"; + var resultConMatch = Regex.Match(htmlContent, resultConPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); + if (resultConMatch.Success) + { + if (int.TryParse(resultConMatch.Groups[1].Value, out int count)) + { + if (count == 0) + { + errorMessage = "검색결과없음"; + return 0; + } + errorMessage = $"검색성공({count}권)"; + return count; + } + } + + // 더 넓은 범위로 숫자 찾기 시도 + var generalPatterns = new[] + { + @"검색한\s*결과\s*총\s*]*>(\d+)", + @"검색결과를\s*찾았습니다[^>]*>(\d+)", + @"]*class=""cred""[^>]*>(\d+)" + }; + + foreach (var pattern in generalPatterns) + { + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errorMessage = "검색결과없음"; + return 0; + } + errorMessage = $"검색성공({count}권)"; + return count; + } + } + } + + errorMessage = "검색결과 패턴을 찾을 수 없음"; + return -1; + } + catch (Exception ex) + { + errorMessage = $"결과 분석 오류: {ex.Message}"; + return -1; + } + } + + public Task WaitForPageChange(OpenQA.Selenium.Support.UI.WebDriverWait wait) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/WandoLibSearcher.cs b/unimarc/unimarc/SearchModel/WandoLibSearcher.cs new file mode 100644 index 0000000..e246cbe --- /dev/null +++ b/unimarc/unimarc/SearchModel/WandoLibSearcher.cs @@ -0,0 +1,167 @@ +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; +using System.Runtime.InteropServices; +using System.Threading; +using UniMarc.마크; +using OpenQA.Selenium.Chromium; +using UniMarc.SearchModel; +using System.Runtime.CompilerServices; +using AR; +using System.Net.Http; + +namespace BokBonCheck +{ + public class WandoLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://wandolib.kr/dls_le/index.php"; + public bool HttpApiMode { get; set; } = true; + + public int No { get; set; } + + private ChromiumDriver _driver; + private static readonly HttpClient _httpClient = new HttpClient(); + + public WandoLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"완도군립({areaName})"; + } + + public void StopDriver() + { + // HTTP 클라이언트 사용으로 별도 정리 불필요 + } + + public async Task StartDriver(bool showdriver = false) + { + // HTTP 클라이언트 사용으로 별도 드라이버 불필요 + await Task.CompletedTask; + } + + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 검색어 URL 인코딩 + var encodedSearchTerm = System.Web.HttpUtility.UrlEncode(searchTerm, System.Text.Encoding.UTF8); + + // 검색 URL 구성 + var searchUrl = $"{SiteUrl}?mod=wdDataSearch&act=searchIList&item=title&word={encodedSearchTerm}"; + + // 도서관 코드가 있으면 추가 + if (!string.IsNullOrEmpty(AreaCode)) + { + searchUrl += $"&manageCode%5B{AreaCode}%5D={AreaCode}"; + } + + Console.WriteLine($"완도군립도서관 검색 URL: {searchUrl}"); + + // HTTP GET 요청 실행 + var response = await _httpClient.GetAsync(searchUrl); + response.EnsureSuccessStatusCode(); + + var htmlContent = await response.Content.ReadAsStringAsync(); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = errorMessage; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = $"검색성공({resultCount}권)"; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = ex.Message; + result.BookCount = 0; + } + + return result; + } + + private int ExtractBookCount(string htmlContent, out string errmessage) + { + errmessage = string.Empty; + try + { + // 검색 결과가 없다는 메시지 확인 + if (htmlContent.Contains("0권(개)") || htmlContent.Contains("검색결과가 없습니다")) + { + errmessage = "검색결과없음"; + return 0; + } + + // HTML에서 다양한 패턴 찾기 + var htmlPatterns = new[] + { + @"총\s*]*class=""cyan""[^>]*>\s*(\d+)\s*권\(개\)", + @"총\s*(\d+)\s*권\(개\)", + @"]*class=""cyan""[^>]*>\s*(\d+)\s*권", + @"(\d+)\s*권\(개\)" + }; + + foreach (var pattern in htmlPatterns) + { + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errmessage = "검색결과없음"; + return 0; + } + errmessage = $"검색성공({count}권)"; + return count; + } + } + } + + errmessage = "결과수량을찾을수없음"; + return -1; + + } + catch (Exception ex) + { + errmessage = ex.Message; + return -1; + } + } + + // 페이지 변경을 감지하는 메서드 (HTTP 방식에서는 불필요) + public async Task WaitForPageChange(WebDriverWait wait) + { + // HTTP 방식에서는 즉시 응답이 오므로 대기 불필요 + await Task.CompletedTask; + } + } +} diff --git a/unimarc/unimarc/SearchModel/YeosuLibSearcher.cs b/unimarc/unimarc/SearchModel/YeosuLibSearcher.cs new file mode 100644 index 0000000..ede6ee1 --- /dev/null +++ b/unimarc/unimarc/SearchModel/YeosuLibSearcher.cs @@ -0,0 +1,162 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using System.Text.RegularExpressions; +using System.Web; +using UniMarc.SearchModel; +using System.Text; +using OpenQA.Selenium.Support.UI; + +namespace BokBonCheck +{ + + public class YeosuLibSearcher : ILibrarySearcher + { + protected string AreaCode { get; set; } = string.Empty; + public string SiteName { get; protected set; } + public string SiteUrl => "https://yslib.yeosu.go.kr/dls_kapi/index.php"; + + public int No { get; set; } + public bool HttpApiMode { get; set; } = true; + + private static readonly HttpClient _httpClient = new HttpClient(); + + public YeosuLibSearcher(int no, string areaCode, string areaName) + { + this.No = no; + this.AreaCode = areaCode; + this.SiteName = $"여수시립({areaName})"; + } + + + + public async Task StartDriver(bool showdriver = false) + { + // HTTP 클라이언트 사용으로 별도 드라이버 불필요 + await Task.CompletedTask; + } + + public void StopDriver() + { + // HTTP 클라이언트 사용으로 별도 정리 불필요 + } + + + + + public async Task SearchAsync(string searchTerm) + { + var result = new BookSearchResult + { + SiteName = SiteName, + SearchTerm = searchTerm, + SearchTime = DateTime.Now + }; + + try + { + // 검색어 URL 인코딩 + var encodedSearchTerm = HttpUtility.UrlEncode(searchTerm, Encoding.UTF8); + + // 검색 URL 구성 + var searchUrl = $"{SiteUrl}?act=searchResultList&mod=wdDataSearch"; + + // 지역코드가 있으면 추가 (전체인 경우 facetManageCode 생략) + if (!string.IsNullOrEmpty(AreaCode)) + { + searchUrl += $"&facetManageCode={AreaCode}"; + } + + searchUrl += $"&searchWord={encodedSearchTerm}&author=&keyword=&publisher=&pubYearS=&pubYearE=&dataSort=rk+desc"; + + Console.WriteLine($"여수시립도서관 검색 URL: {searchUrl}"); + + // HTTP GET 요청 실행 + var response = await _httpClient.GetAsync(searchUrl); + response.EnsureSuccessStatusCode(); + + var htmlContent = await response.Content.ReadAsStringAsync(); + + // 검색 결과 수 추출 + var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = errorMessage; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = $"검색성공({resultCount}권)"; + } + + } + catch (Exception ex) + { + result.IsSuccess = false; + result.ErrorMessage = $"검색 오류: {ex.Message}"; + result.BookCount = 0; + Console.WriteLine($"여수시립도서관 검색 오류: {ex.Message}"); + } + + return result; + } + + private int ExtractBookCount(string htmlContent, out string errorMessage) + { + errorMessage = string.Empty; + + try + { + // HTML에서 "총 N권(개)" 패턴 찾기 + var pattern = @"총\s*]*class=""cyan""[^>]*>(\d+)권\(개\)"; + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); + + if (match.Success) + { + if (int.TryParse(match.Groups[1].Value, out int count)) + { + errorMessage = $"검색성공({count}권)"; + return count; + } + else + { + errorMessage = $"수량값오류({match.Groups[1].Value})"; + return -1; + } + } + else + { + // 패턴을 찾지 못한 경우 다른 패턴으로 시도 + var alternatePattern = @"총\s*]*>(\d+)권\(개\)"; + var alternateMatch = Regex.Match(htmlContent, alternatePattern, RegexOptions.IgnoreCase); + + if (alternateMatch.Success) + { + if (int.TryParse(alternateMatch.Groups[1].Value, out int count)) + { + errorMessage = $"검색성공({count}권)"; + return count; + } + } + + errorMessage = "검색결과 패턴을 찾을 수 없음"; + return -1; + } + } + catch (Exception ex) + { + errorMessage = $"결과 분석 오류: {ex.Message}"; + return -1; + } + } + + public Task WaitForPageChange(WebDriverWait wait) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/Settings.cs b/unimarc/unimarc/Settings.cs index aee0734..39882a8 100644 --- a/unimarc/unimarc/Settings.cs +++ b/unimarc/unimarc/Settings.cs @@ -1,6 +1,22 @@ -namespace UniMarc.Properties { - - +using System.Security.Permissions; + +namespace UniMarc.Properties { + + + public class UserSetting : AR.Setting + { + public string LastSearchTarget { get; set; } + public string LastSearchTargetDLS { get; set; } + public override void AfterLoad() + { + + } + + public override void AfterSave() + { + + } + } // 이 클래스를 사용하여 설정 클래스에 대한 특정 이벤트를 처리할 수 있습니다. // SettingChanging 이벤트는 설정 값이 변경되기 전에 발생합니다. // PropertyChanged 이벤트는 설정 값이 변경된 후에 발생합니다. diff --git a/unimarc/unimarc/UniMarc.csproj b/unimarc/unimarc/UniMarc.csproj index 1884e89..cb1dd63 100644 --- a/unimarc/unimarc/UniMarc.csproj +++ b/unimarc/unimarc/UniMarc.csproj @@ -225,13 +225,27 @@ Reference.svcmap + + + + Form + + + + - + + + + + + + diff --git a/unimarc/unimarc/UniMarc.csproj.user b/unimarc/unimarc/UniMarc.csproj.user index be8df31..a66c462 100644 --- a/unimarc/unimarc/UniMarc.csproj.user +++ b/unimarc/unimarc/UniMarc.csproj.user @@ -1,4 +1,4 @@ - + E:\UniMarcApplicationUpdateFiles\|ftp://ftpgloria%401.215.250.130/|ftp://ftpgloria%401.215.250.130:50005/|sftp://ftpgloria%401.215.250.130/|ftp://ftpgloria%401.215.250.130/unimarc/ diff --git a/unimarc/unimarc/마크/Check_Copy_Sub_List.Designer.cs b/unimarc/unimarc/마크/Check_Copy_Sub_List.Designer.cs index 8644ae2..deca090 100644 --- a/unimarc/unimarc/마크/Check_Copy_Sub_List.Designer.cs +++ b/unimarc/unimarc/마크/Check_Copy_Sub_List.Designer.cs @@ -68,7 +68,7 @@ namespace UniMarc.마크 this.dataGridView1.Name = "dataGridView1"; this.dataGridView1.ReadOnly = true; this.dataGridView1.RowTemplate.Height = 23; - this.dataGridView1.Size = new System.Drawing.Size(436, 186); + this.dataGridView1.Size = new System.Drawing.Size(464, 583); this.dataGridView1.TabIndex = 1; this.dataGridView1.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellDoubleClick); this.dataGridView1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.dataGridView1_KeyDown); @@ -96,7 +96,7 @@ namespace UniMarc.마크 this.panel1.Dock = System.Windows.Forms.DockStyle.Top; this.panel1.Location = new System.Drawing.Point(0, 0); this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(436, 32); + this.panel1.Size = new System.Drawing.Size(464, 32); this.panel1.TabIndex = 2; // // btn_Close @@ -144,14 +144,14 @@ namespace UniMarc.마크 this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; this.panel2.Location = new System.Drawing.Point(0, 32); this.panel2.Name = "panel2"; - this.panel2.Size = new System.Drawing.Size(436, 186); + this.panel2.Size = new System.Drawing.Size(464, 583); this.panel2.TabIndex = 3; // // Check_Copy_Sub_List // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(436, 218); + this.ClientSize = new System.Drawing.Size(464, 615); this.Controls.Add(this.panel2); this.Controls.Add(this.panel1); this.Name = "Check_Copy_Sub_List"; diff --git a/unimarc/unimarc/마크/Check_Copy_Sub_List.cs b/unimarc/unimarc/마크/Check_Copy_Sub_List.cs index 3983018..5ecd796 100644 --- a/unimarc/unimarc/마크/Check_Copy_Sub_List.cs +++ b/unimarc/unimarc/마크/Check_Copy_Sub_List.cs @@ -133,7 +133,6 @@ namespace UniMarc.마크 { var cc2 = cc as Check_copyWD; cc2.btn_SearchList.Text = list_name; - cc2.SearchCount.Value = cc2.dv1.Rows.Count - 1; } diff --git a/unimarc/unimarc/마크/Check_Copy_Sub_Search.Designer.cs b/unimarc/unimarc/마크/Check_Copy_Sub_Search.Designer.cs index a7c194e..b89cb8c 100644 --- a/unimarc/unimarc/마크/Check_Copy_Sub_Search.Designer.cs +++ b/unimarc/unimarc/마크/Check_Copy_Sub_Search.Designer.cs @@ -67,7 +67,7 @@ namespace UniMarc.마크 this.dataGridView1.Name = "dataGridView1"; this.dataGridView1.ReadOnly = true; this.dataGridView1.RowTemplate.Height = 23; - this.dataGridView1.Size = new System.Drawing.Size(514, 261); + this.dataGridView1.Size = new System.Drawing.Size(514, 606); this.dataGridView1.TabIndex = 0; this.dataGridView1.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellDoubleClick); this.dataGridView1.RowPostPaint += new System.Windows.Forms.DataGridViewRowPostPaintEventHandler(this.dataGridView1_RowPostPaint); @@ -119,7 +119,7 @@ namespace UniMarc.마크 // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(514, 261); + this.ClientSize = new System.Drawing.Size(514, 606); this.Controls.Add(this.dataGridView1); this.Name = "Check_Copy_Sub_Search"; this.Text = "도서관 검색"; diff --git a/unimarc/unimarc/마크/Check_copyWD.Designer.cs b/unimarc/unimarc/마크/Check_copyWD.Designer.cs index 1794bf3..bcc1403 100644 --- a/unimarc/unimarc/마크/Check_copyWD.Designer.cs +++ b/unimarc/unimarc/마크/Check_copyWD.Designer.cs @@ -42,9 +42,7 @@ this.btn_SearchList = new System.Windows.Forms.Button(); this.btn_Start = new System.Windows.Forms.Button(); this.btn_Stop = new System.Windows.Forms.Button(); - this.SearchCount = new System.Windows.Forms.NumericUpDown(); this.btn_Close = new System.Windows.Forms.Button(); - this.label2 = new System.Windows.Forms.Label(); this.tb_SearchTarget = new System.Windows.Forms.ComboBox(); this.btSearchLibrary = new System.Windows.Forms.Label(); this.panel2 = new System.Windows.Forms.Panel(); @@ -61,16 +59,15 @@ this.panel6 = new System.Windows.Forms.Panel(); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.lblStatus = new System.Windows.Forms.ToolStripLabel(); + this.lbSite = new System.Windows.Forms.ToolStripStatusLabel(); this.panel4 = new System.Windows.Forms.Panel(); this.chk_RemoveBrit = new System.Windows.Forms.CheckBox(); this.btn_ResultEmpty = new System.Windows.Forms.Button(); this.btn_GridReset = new System.Windows.Forms.Button(); this.btn_OpenMemo = new System.Windows.Forms.Button(); this.chk_spChar = new System.Windows.Forms.CheckBox(); - this.lbSite = new System.Windows.Forms.ToolStripStatusLabel(); this.panel1.SuspendLayout(); this.groupBox1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.SearchCount)).BeginInit(); this.panel2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.dv1)).BeginInit(); this.panel3.SuspendLayout(); @@ -228,19 +225,6 @@ this.btn_Stop.UseVisualStyleBackColor = true; this.btn_Stop.Click += new System.EventHandler(this.btn_Stop_Click); // - // SearchCount - // - this.SearchCount.Location = new System.Drawing.Point(491, 6); - this.SearchCount.Maximum = new decimal(new int[] { - 10000, - 0, - 0, - 0}); - this.SearchCount.Name = "SearchCount"; - this.SearchCount.Size = new System.Drawing.Size(54, 21); - this.SearchCount.TabIndex = 2; - this.SearchCount.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; - // // btn_Close // this.btn_Close.Location = new System.Drawing.Point(548, 4); @@ -251,23 +235,15 @@ this.btn_Close.UseVisualStyleBackColor = true; this.btn_Close.Click += new System.EventHandler(this.btn_Close_Click); // - // label2 - // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(444, 10); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(45, 12); - this.label2.TabIndex = 0; - this.label2.Text = "검색 수"; - // // tb_SearchTarget // this.tb_SearchTarget.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; this.tb_SearchTarget.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + this.tb_SearchTarget.Enabled = false; this.tb_SearchTarget.ImeMode = System.Windows.Forms.ImeMode.Hangul; this.tb_SearchTarget.Location = new System.Drawing.Point(66, 6); this.tb_SearchTarget.Name = "tb_SearchTarget"; - this.tb_SearchTarget.Size = new System.Drawing.Size(243, 20); + this.tb_SearchTarget.Size = new System.Drawing.Size(390, 20); this.tb_SearchTarget.TabIndex = 1; this.tb_SearchTarget.SelectedIndexChanged += new System.EventHandler(this.tb_SearchTarget_SelectedIndexChanged); this.tb_SearchTarget.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tb_SearchTarget_KeyDown); @@ -288,11 +264,9 @@ this.panel2.Controls.Add(this.btn_SiteDenote); this.panel2.Controls.Add(this.lbl_PW); this.panel2.Controls.Add(this.lbl_ID); - this.panel2.Controls.Add(this.SearchCount); this.panel2.Controls.Add(this.tb_SearchTarget); this.panel2.Controls.Add(this.btSearchLibrary); this.panel2.Controls.Add(this.btn_Close); - this.panel2.Controls.Add(this.label2); this.panel2.Dock = System.Windows.Forms.DockStyle.Top; this.panel2.Location = new System.Drawing.Point(0, 0); this.panel2.Name = "panel2"; @@ -301,7 +275,7 @@ // // btn_SiteDenote // - this.btn_SiteDenote.Location = new System.Drawing.Point(317, 5); + this.btn_SiteDenote.Location = new System.Drawing.Point(462, 5); this.btn_SiteDenote.Name = "btn_SiteDenote"; this.btn_SiteDenote.Size = new System.Drawing.Size(77, 23); this.btn_SiteDenote.TabIndex = 4; @@ -432,6 +406,12 @@ this.lblStatus.Size = new System.Drawing.Size(27, 20); this.lblStatus.Text = "WD"; // + // lbSite + // + this.lbSite.Name = "lbSite"; + this.lbSite.Size = new System.Drawing.Size(55, 17); + this.lbSite.Text = "-- Site --"; + // // panel4 // this.panel4.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; @@ -497,12 +477,6 @@ this.chk_spChar.Text = "특수문자 제거"; this.chk_spChar.UseVisualStyleBackColor = true; // - // lbSite - // - this.lbSite.Name = "lbSite"; - this.lbSite.Size = new System.Drawing.Size(55, 17); - this.lbSite.Text = "-- Site --"; - // // Check_copyWD // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F); @@ -510,14 +484,13 @@ this.ClientSize = new System.Drawing.Size(637, 738); this.Controls.Add(this.panel3); this.Name = "Check_copyWD"; - this.Text = "복본조사(WebDriver)"; + this.Text = "복본조사(New)"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Check_copy_FormClosing); this.Load += new System.EventHandler(this.Check_copy_Load); this.panel1.ResumeLayout(false); this.panel1.PerformLayout(); this.groupBox1.ResumeLayout(false); this.groupBox1.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.SearchCount)).EndInit(); this.panel2.ResumeLayout(false); this.panel2.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.dv1)).EndInit(); @@ -536,7 +509,6 @@ private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Label btSearchLibrary; - private System.Windows.Forms.Label label2; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Button btn_Close; private System.Windows.Forms.Button btn_Stop; @@ -550,7 +522,6 @@ private System.Windows.Forms.Panel panel4; private System.Windows.Forms.CheckBox chk_spChar; private System.Windows.Forms.CheckBox chk_RemoveBrit; - public System.Windows.Forms.NumericUpDown SearchCount; private System.Windows.Forms.RadioButton rb_isNumber; private System.Windows.Forms.RadioButton rb_isHave; private System.Windows.Forms.Button btn_GridReset; diff --git a/unimarc/unimarc/마크/Check_copyWD.cs b/unimarc/unimarc/마크/Check_copyWD.cs index ce29f68..3510ece 100644 --- a/unimarc/unimarc/마크/Check_copyWD.cs +++ b/unimarc/unimarc/마크/Check_copyWD.cs @@ -10,6 +10,7 @@ using BokBonCheck; using AR; using UniMarc.SearchModel; using System.Linq; +using UniMarc; namespace WindowsFormsApp1.Mac { @@ -22,6 +23,15 @@ namespace WindowsFormsApp1.Mac public string SearcherNo; private bool isStop = false; Main main; + + bool GetDemoRunAuth + { + get + { + return (DateTime.Now <= new DateTime(2025, 09, 30)); + + } + } public Check_copyWD(Main _main) { InitializeComponent(); @@ -32,56 +42,307 @@ namespace WindowsFormsApp1.Mac //도서검색(크롤링) _searchService = new BookSearchService(); - _searchService.AddSearcher(new NamguLibrarySearcher_Munhwa(idx++)); - _searchService.AddSearcher(new NamguLibrarySearcher_Children(idx++)); - _searchService.AddSearcher(new NamguLibrarySearcher_Smart(idx++)); - _searchService.AddSearcher(new NamguLibrarySearcher_Purungil(idx++)); - _searchService.AddSearcher(new NamguLibrarySearcher_Hyocheon(idx++)); + _searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libMA", "문화정보도서관")); + _searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libMC", "청소년도서관")); + _searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libSQ", "스마트도서관")); + _searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libMB", "푸른길도서관")); + _searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libSW", "효천어울림도서관")); - //광주시청도서광 - idx = 30; - _searchService.AddSearcher(new KwangjuCityLibrarySearcher_central(idx++)); - _searchService.AddSearcher(new KwangjuCityLibrarySearcher_choisangjun(idx++)); - _searchService.AddSearcher(new KwangjuCityLibrarySearcher_dagachi(idx++)); - _searchService.AddSearcher(new KwangjuCityLibrarySearcher_Indi(idx++)); - _searchService.AddSearcher(new KwangjuCityLibrarySearcher_Student(idx++)); - _searchService.AddSearcher(new KwangjuCityLibrarySearcher_Kumho(idx++)); - //전남교육청통합도서관 - idx = 50; - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "ALL", "전체")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146010", "목포")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146007", "나주")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146006", "남평")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146025", "광양")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146008", "담양")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146005", "구례")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146012", "보성")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146013", "벌교")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146023", "화순")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146019", "장흥")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146022", "해남")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146017", "영암")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146011", "무안")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146021", "함평")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146016", "영광")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146018", "장성")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146020", "진도")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146003", "곡성")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146059", "학생교육문화회관")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146042", "광양평생")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146002", "고흥평생")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00346002", "교육연수원")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00324008", "교육연구정보원")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00346030", "전라남도국제교육원")); - _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146180", "순천만생태문화교육원")); + if (GetDemoRunAuth == true) + { + + //광주시교육청통합도서관 + idx = 50; + _searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MD", "중앙도서관")); + _searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MF", "분관최상준도서관")); + _searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "ME", "송정다가치문화도서관")); + _searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MA", "학생독립운동기념회관")); + _searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MC", "학생교육문화회관")); + _searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MB", "금호평생교육관")); + + //전남교육청통합도서관 + idx = 100; + //_searchService.AddSearcher(new JunnamEduSearcher(idx++, "ALL", "전체")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146010", "목포")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146007", "나주")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146006", "남평")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146025", "광양")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146008", "담양")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146005", "구례")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146012", "보성")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146013", "벌교")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146023", "화순")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146019", "장흥")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146022", "해남")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146017", "영암")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146011", "무안")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146021", "함평")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146016", "영광")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146018", "장성")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146020", "진도")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146003", "곡성")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146059", "학생교육문화회관")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146042", "광양평생")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146002", "고흥평생")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00346002", "교육연수원")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00324008", "교육연구정보원")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00346030", "전라남도국제교육원")); + _searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146180", "순천만생태문화교육원")); + + //전남교육청행정자료실 + idx = 150; + _searchService.AddSearcher(new JunnamEduJiheaNuriSearcher(idx++, "00324010", "지혜누리")); + + //여수시립 + idx = 200; + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "MG", "여수이순신도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "MA", "여수시립쌍봉도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "MB", "여수시립현암도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "MC", "여수시립환경도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "MD", "여수시립돌산도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "ME", "여수시립소라도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "MF", "여수시립율촌도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "PA", "거문도은빛바다도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "PB", "치매안심센터작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "PC", "청솔글누리작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "PD", "동부도시보건작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "PE", "화양열린작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "PF", "여문늘벗작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "PH", "국동작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SA", "아주타운아파트작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SB", "책이랑나랑작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SC", "현천작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SE", "꿈꾸는영어전문작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SF", "학마을작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SG", "웅천지웰작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SH", "한려작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SJ", "주은금고작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SK", "광림작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SM", "민들레작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SO", "원앙작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SP", "푸른정원작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SR", "로얄골드빌작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SS", "꿈을키우는작은도서관")); + //_searchService.AddSearcher(new YeosuLibSearcher(idx++, "ST", "KLAS MANAGEMENT")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SY", "신기부영작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "SZ", "국동365열린도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "TA", "지웰2차 작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "TB", "이편한 작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "TC", "채움늘 작은도서관")); + _searchService.AddSearcher(new YeosuLibSearcher(idx++, "TD", "웅천글꽃 작은도서관")); + + //고흥군립 + idx = 300; + _searchService.AddSearcher(new GoheungLibSearcher(idx++, "MA", "고흥군립중앙도서관")); + _searchService.AddSearcher(new GoheungLibSearcher(idx++, "MB", "고흥군립남부도서관")); + _searchService.AddSearcher(new GoheungLibSearcher(idx++, "MC", "고흥군립북부도서관")); + _searchService.AddSearcher(new GoheungLibSearcher(idx++, "MD", "과역작은도서관")); + _searchService.AddSearcher(new GoheungLibSearcher(idx++, "ME", "나로한빛 작은도서관")); + _searchService.AddSearcher(new GoheungLibSearcher(idx++, "MF", "도화푸른꿈 작은도서관")); + _searchService.AddSearcher(new GoheungLibSearcher(idx++, "MG", "포두작은도서관")); + + //광주북구 통합도서관 + idx = 400; + _searchService.AddSearcher(new BukguLibSearcher(idx++, "101", "스마트도서관1호점")); + _searchService.AddSearcher(new BukguLibSearcher(idx++, "102", "스마트도서관2호점")); + _searchService.AddSearcher(new BukguLibSearcher(idx++, "105", "스마트도서관3호점")); + + //광주북구 작은도서관 + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "729038", "꿈나무작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129094", "동림동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "729046", "두암1동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129069", "두암2동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129068", "두암3동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129056", "매곡동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129074", "문흥2동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129054", "삼각동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "729068", "시화문화마을작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129057", "신안동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129220", "양산동주민작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129085", "오치1동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129070", "오치2동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129055", "용봉작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129064", "운암1동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129071", "운암2동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129058", "운암3동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "729175", "임동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "729085", "중앙동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129075", "중흥1동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "729155", "징검다리작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "129065", "풍향동작은도서관")); + _searchService.AddSearcher(new BukguSlibSearcher(idx++, "729154", "햇살마루작은도서관")); + + //광주북구 공공도서관 + _searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "124010", "일곡도서관")); + _searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "129131", "운암도서관")); + _searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "129225", "양산도서관")); + _searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "129226", "중흥도서관")); + _searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "129227", "신용도서관")); + + //전북교육청도서관 + idx = 700; + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MV", "고창도서관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MB", "군산학생교육문화관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MC", "군산학생교육문화관 대야분관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MH", "김제학생교육문화관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MJ", "김제학생교육문화관 금산분관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MF", "남원학생교육문화관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MG", "남원학생교육문화관운봉분관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MR", "무주도서관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MK", "부안학생교육문화관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MU", "순창도서관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MP", "완주도서관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MD", "익산학생교육문화관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_ME", "익산학생교육문화관 함열분관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MT", "임실도서관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MS", "장수도서관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MA", "전주학생교육문화관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MQ", "진안도서관")); + _searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MN", "정읍학생복지회관")); + + //광주광산구 통합도서관 + idx = 800; + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "JM", "장덕도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "MB", "이야기꽃도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "MA", "첨단도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "BR", "신가도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "WN", "운남어린이도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "MC", "스마트도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CA", "첨단빛누리작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CB", "사랑나눔작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CC", "혜윰마루작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CD", "책놀터작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CE", "우산동작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CF", "더불어락작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CG", "임곡꿈쟁이작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CH", "유노윤호HUG작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CI", "늘품작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CJ", "왁자지껄수다놀이터작은도서관")); + _searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CK", "송정다누리작은도서관")); + + //목포시립도서관 + idx = 900; + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MA", "목포시립도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MX", "목포어울림도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MJ", "목포어린이도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MQ", "목포영어도서관")); + + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "ME", "꿈나무작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MB", "꿈돌이작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MS", "노을작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MW", "늘푸른작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MH", "무지개작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MF", "반딧불작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MP", "삼학작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MV", "샛별작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MO", "소나무작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MN", "숲속작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MK", "연꽃작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MD", "옹달샘작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MC", "은하수작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MM", "청개구리작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MI", "초롱초롱작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "ML", "푸른솔작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MU", "푸른숲작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MR", "행복마을작은도서관")); + _searchService.AddSearcher(new MokpoLibSearcher(idx++, "MT", "호돌이작은도서관")); + + //순천시립도서관 + idx = 1000; + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MA", "삼산도서관")); + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MB", "그림책도서관")); + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "BR", "연향도서관")); + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MH", "기적의도서관")); + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MC", "조례호수도서관")); + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MJ", "신대도서관")); + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MK", "어울림도서관")); + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MD", "해룡농어촌도서관")); + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MG", "풍덕글마루도서관")); + _searchService.AddSearcher(new SuncheonLibSearcher(idx++, "mini", "작은도서관")); + + //광주시립도서관 + idx = 1100; + _searchService.AddSearcher(new GwangjuCityLibSearcher(idx++, "124003", "무등도서관")); + _searchService.AddSearcher(new GwangjuCityLibSearcher(idx++, "124004", "사직도서관")); + _searchService.AddSearcher(new GwangjuCityLibSearcher(idx++, "124008", "산수도서관")); + _searchService.AddSearcher(new GwangjuCityLibSearcher(idx++, "129228", "디지털정보도서관")); + + // 완도군립도서관 + idx = 1200; + _searchService.AddSearcher(new WandoLibSearcher(idx++, "MA", "완도군립도서관")); + _searchService.AddSearcher(new WandoLibSearcher(idx++, "MB", "노화공공도서관")); + _searchService.AddSearcher(new WandoLibSearcher(idx++, "MC", "금일공공도서관")); + _searchService.AddSearcher(new WandoLibSearcher(idx++, "MD", "보길윤선도작은도서관")); + _searchService.AddSearcher(new WandoLibSearcher(idx++, "ME", "군외동백숲작은도서관")); + _searchService.AddSearcher(new WandoLibSearcher(idx++, "MF", "도담도담작은도서관")); + + // 익산시통합도서관 + idx = 1300; + _searchService.AddSearcher(new IksanLibSearcher(idx++, "MO", "모현도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "BR", "영등도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "MA", "마동도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "BU", "부송도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "MC", "유천도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "HW", "황등도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "MB", "금마도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SX", "모인여행숲도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SW", "수도산그림책숲도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "UQ", "스마트도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SI", "글마루작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SD", "깊은샘작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SE", "꿈꾸는뜰작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SO", "꿈드림작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SK", "낭산작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SB", "동산작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SJ", "모현뜰작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SH", "배산작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SC", "봄나루작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SF", "부송작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SA", "삼성동어린이도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SQ", "어양작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SP", "어울림작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SG", "예솔작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SL", "왕궁작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SN", "울림작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SM", "행복세상작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SR", "그루터기작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SS", "늘봄작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SU", "망성아담작은도서관")); + _searchService.AddSearcher(new IksanLibSearcher(idx++, "SV", "함라등대작은도서관")); + _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", "월피예술도서관")); + + } + this.tb_SearchTarget.Items.Clear(); - // this.tb_SearchTarget.Items.Add("-- 검색대상을 선택하세요 --"); - foreach (var item in _searchService.GetAvailableSites().OrderBy(t=>t)) + // this.tb_SearchTarget.Items.Add("-- 검색대상을 선택하세요 --"); + foreach (var item in _searchService.GetAvailableSites().OrderBy(t => t)) tb_SearchTarget.Items.Add(item); - + if (PUB.setting.LastSearchTarget.isEmpty() == false) + tb_SearchTarget.Text = PUB.setting.LastSearchTarget; + + this.Text = $"복본조사(New:{_searchService.GetAvailableSites().Count})"; } private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) @@ -97,6 +358,7 @@ namespace WindowsFormsApp1.Mac //크롤링 InitializeChromeDriver(); + tb_SearchTarget.Enabled = true; } @@ -220,10 +482,9 @@ namespace WindowsFormsApp1.Mac { Skill_Grid sg = new Skill_Grid(); sg.Excel_to_DataGridView(sender, e); - - SearchCount.Value = dv1.Rows.Count; } + async private void btn_Start_Click(object sender, EventArgs e) { var searcher = this.GetSearcher(); @@ -233,14 +494,9 @@ namespace WindowsFormsApp1.Mac return; } - SearchCount.Value = dv1.Rows.Count; //250619 - RowCount = 0; //250619 - dv1.CurrentCell = dv1.Rows[0].Cells[0]; - int SCount = Convert.ToInt32(SearchCount.Value); - - if (DateTime.Now > new DateTime(2025, 09, 30)) + if (GetDemoRunAuth == false) { UTIL.MsgE("테스트 기간이 종료되었습니다\n개발자 문의 하세요"); return; @@ -254,10 +510,12 @@ namespace WindowsFormsApp1.Mac try { + PUB.setting.LastSearchTarget = this.tb_SearchTarget.Text.Trim(); + PUB.setting.Save(); this._isSearching = true; btn_Start.Enabled = false; - btn_Stop.Enabled = false; + btn_Stop.Enabled = true; await searcher.StartDriver(this.chkShowBrowser.Checked); byte searchopt = 0; //전체검색 if (radTargetEmpty.Checked) searchopt = 1; @@ -278,7 +536,7 @@ namespace WindowsFormsApp1.Mac if (this._isSearching == false) { drow.Cells["dvc_remark"].Value = "Cancel"; - continue; + break; } dv1.CurrentCell = drow.Cells[0]; @@ -340,7 +598,7 @@ namespace WindowsFormsApp1.Mac } } - if (retry == false && chkRetryErrData.Checked) + if (_isSearching == true && retry == false && chkRetryErrData.Checked) { retry = true; radTargetErr.Checked = true; @@ -371,11 +629,8 @@ namespace WindowsFormsApp1.Mac btn_Start.Enabled = true; this._isSearching = false; } - - } - int RowCount = 0; /// @@ -521,7 +776,7 @@ namespace WindowsFormsApp1.Mac dv1.Rows[a].DefaultCellStyle.ForeColor = Color.Black; dv1.Rows[a].DefaultCellStyle.BackColor = Color.White; dv1.Rows[a].Cells["Count"].Value = ""; - RowCount = 0; + dv1.Rows[a].Cells["dvc_remark"].Value = ""; } } @@ -540,7 +795,7 @@ namespace WindowsFormsApp1.Mac { if (this.tb_SearchTarget.SelectedIndex < 0) return null; return this._searchService.Get(tb_SearchTarget.Text); - + } private void btn_SiteDenote_Click(object sender, EventArgs e) @@ -567,10 +822,12 @@ namespace WindowsFormsApp1.Mac var searcher = this._searchService.Get(this.tb_SearchTarget.Text); if (searcher == null) { + chkShowBrowser.Enabled = true; lbSite.Text = "-- site --"; return; } - this.lbSite.Text = searcher.SiteUrl; + chkShowBrowser.Enabled = !searcher.HttpApiMode; + this.lbSite.Text = searcher.SiteUrl; } } } diff --git a/unimarc/unimarc/마크/DLS_Copy.cs b/unimarc/unimarc/마크/DLS_Copy.cs index 159ea67..ee9b8c3 100644 --- a/unimarc/unimarc/마크/DLS_Copy.cs +++ b/unimarc/unimarc/마크/DLS_Copy.cs @@ -9,6 +9,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; +using UniMarc; using UniMarc.SearchModel; namespace WindowsFormsApp1.Mac @@ -179,6 +180,8 @@ namespace WindowsFormsApp1.Mac btn_Search.Enabled = true; + PUB.setting.LastSearchTargetDLS = tb_SearchClient.Text.Trim(); + PUB.setting.Save(); //로그인 _searcher = new DLSSearcher(50);