From 216311b558abb5360d01af66ff9e19928b52fd2d Mon Sep 17 00:00:00 2001 From: chiDT Date: Mon, 15 Sep 2025 23:39:04 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B2=80=EC=83=89=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=97=86=EC=9D=8C=20HTML=20=EC=B6=94=EC=B6=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EB=8F=84=EC=84=9C=EA=B4=80=EB=B3=84=20?= =?UTF-8?q?=EC=A7=80=EC=97=B0=EC=8B=9C=EA=B0=84=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 모든 도서관 검색기에서 검색결과 없음시 구체적인 HTML 조각 추출 - BookSearchResult에 Resulthtml 속성 추가하여 매칭된 HTML 컨텍스트 저장 - Helper_LibraryDelaySettings.cs 추가로 도서관별 검색 지연시간 XML 저장 - Check_copyWD.cs에 dvc_resulthtml 컬럼 표시 및 지연시간 저장 UI 구현 - 15개 SearchModel 파일에서 htmlContent 1000자 자르기를 의미있는 메시지로 교체 - HTTP 검색기들에 한글 인코딩 문제 해결을 위한 헤더 개선 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- unimarc/CLAUDE.md | 3 + unimarc/unimarc.sln | 5 + .../unimarc/Helper_LibraryDelaySettings.cs | 170 ++++++++++++++ unimarc/unimarc/Program.cs | 7 +- unimarc/unimarc/Properties/AssemblyInfo.cs | 4 +- .../unimarc/SearchModel/AnsanLibSearcher.cs | 216 ++++++++---------- .../unimarc/SearchModel/BookSearchService.cs | 2 + .../unimarc/SearchModel/BukguLibSearcher.cs | 152 +++++++----- .../SearchModel/ChosunTechLibSearcher.cs | 83 ++++++- .../SearchModel/ChosunUnivLibSearcher.cs | 82 ++++++- .../unimarc/SearchModel/GochangLibSearcher.cs | 146 ++++++++---- .../unimarc/SearchModel/GoheungLibSearcher.cs | 168 +++++++------- .../SearchModel/GwangjuCityLibSearcher.cs | 163 +++++++------ .../SearchModel/GwangjuDongguLibSearcher.cs | 184 +++++++++++++-- .../SearchModel/GwangjuSeoguLibSearcher.cs | 145 +++++++++++- .../SearchModel/GwangsanLibSearcher.cs | 81 ++++++- .../SearchModel/GyeongnamLibSearcher.cs | 138 ++++++----- .../unimarc/SearchModel/IksanLibSearcher.cs | 79 ++++++- .../SearchModel/JeonbukEduLibSearcher.cs | 139 ++++++----- .../SearchModel/JunnamEduJiheaNuriSearcher.cs | 89 +++++++- .../unimarc/SearchModel/JunnamEduSearcher.cs | 109 ++++++++- unimarc/unimarc/SearchModel/KcmLibSearcher.cs | 81 ++++++- .../KwangjuCityEduLibrarySearcher.cs | 124 +++++++++- .../unimarc/SearchModel/MokpoLibSearcher.cs | 81 ++++++- .../unimarc/SearchModel/MuanLibSearcher.cs | 168 +++++++++----- .../SearchModel/NamguLibrarySearcher.cs | 91 +++++++- .../SearchModel/SuncheonLibSearcher.cs | 83 ++++++- .../unimarc/SearchModel/WandoLibSearcher.cs | 77 ++++++- .../unimarc/SearchModel/YeosuLibSearcher.cs | 92 +++++++- unimarc/unimarc/Skill.cs | 176 ++++++++------ unimarc/unimarc/UniMarc.csproj | 1 + unimarc/unimarc/마크/Check_copyWD.Designer.cs | 179 ++++++++++----- unimarc/unimarc/마크/Check_copyWD.cs | 99 ++++++++ unimarc/unimarc/마크/Check_copyWD.resx | 7 +- 34 files changed, 2641 insertions(+), 783 deletions(-) create mode 100644 unimarc/unimarc/Helper_LibraryDelaySettings.cs diff --git a/unimarc/CLAUDE.md b/unimarc/CLAUDE.md index d825489..76777b1 100644 --- a/unimarc/CLAUDE.md +++ b/unimarc/CLAUDE.md @@ -10,6 +10,9 @@ - **데이터베이스**: MySQL - **주요기능**: 마크 작성, 복본조사, DLS 연동, 도서 정보 관리 +## 데이터베이스 정보 +- Server=1.215.250.130;Port=3306;Database=unimarc;uid=root;pwd=Admin21234; + ## 코딩 컨벤션 - 파일명: PascalCase (예: DLS_Copy.cs) - 클래스명: PascalCase diff --git a/unimarc/unimarc.sln b/unimarc/unimarc.sln index 8840ad2..713cd26 100644 --- a/unimarc/unimarc.sln +++ b/unimarc/unimarc.sln @@ -9,6 +9,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Factory_Client", "Factory_C EndProject Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "UniMarcSetup", "UniMarcSetup\UniMarcSetup.vdproj", "{B0A88F76-DC68-44F9-90B4-CD94625CC1F4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "솔루션 항목", "솔루션 항목", "{2A3A057F-5D22-31FD-628C-DF5EF75AEF1E}" + ProjectSection(SolutionItems) = preProject + CLAUDE.md = CLAUDE.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/unimarc/unimarc/Helper_LibraryDelaySettings.cs b/unimarc/unimarc/Helper_LibraryDelaySettings.cs new file mode 100644 index 0000000..e4de9bc --- /dev/null +++ b/unimarc/unimarc/Helper_LibraryDelaySettings.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using System.Text; + +namespace UniMarc +{ + /// + /// 도서관별 지연시간 설정을 관리하는 클래스 + /// + public class Helper_LibraryDelaySettings + { + private static readonly string SettingsFileName = "LibraryDelaySettings.xml"; + private static readonly string SettingsFilePath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "UniMarc", + SettingsFileName + ); + + private Dictionary _libraryDelaySettings; + + public Helper_LibraryDelaySettings() + { + _libraryDelaySettings = new Dictionary(); + LoadSettings(); + } + + /// + /// 도서관별 지연시간 설정을 로드 + /// + private void LoadSettings() + { + try + { + if (File.Exists(SettingsFilePath)) + { + var xmlDoc = new XmlDocument(); + xmlDoc.Load(SettingsFilePath); + + var libraryNodes = xmlDoc.SelectNodes("//LibraryDelaySettings/Library"); + if (libraryNodes != null) + { + foreach (XmlNode node in libraryNodes) + { + var name = node.Attributes?["Name"]?.Value; + var delayStr = node.Attributes?["Delay"]?.Value; + + if (!string.IsNullOrEmpty(name) && int.TryParse(delayStr, out int delay)) + { + _libraryDelaySettings[name] = delay; + } + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"도서관 지연시간 설정 로드 오류: {ex.Message}"); + _libraryDelaySettings = new Dictionary(); + } + } + + /// + /// 도서관별 지연시간 설정을 저장 + /// + private void SaveSettings() + { + try + { + // 디렉토리 생성 + var directory = Path.GetDirectoryName(SettingsFilePath); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + // XML 파일로 저장 + var xmlDoc = new XmlDocument(); + var declaration = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null); + xmlDoc.AppendChild(declaration); + + var root = xmlDoc.CreateElement("LibraryDelaySettings"); + xmlDoc.AppendChild(root); + + foreach (var kvp in _libraryDelaySettings) + { + var libraryElement = xmlDoc.CreateElement("Library"); + libraryElement.SetAttribute("Name", kvp.Key); + libraryElement.SetAttribute("Delay", kvp.Value.ToString()); + root.AppendChild(libraryElement); + } + + xmlDoc.Save(SettingsFilePath); + } + catch (Exception ex) + { + Console.WriteLine($"도서관 지연시간 설정 저장 오류: {ex.Message}"); + } + } + + /// + /// 특정 도서관의 지연시간 설정을 가져옴 + /// + /// 도서관 이름 + /// 지연시간(초), 설정이 없으면 0 + public int GetDelay(string libraryName) + { + if (string.IsNullOrEmpty(libraryName)) + return 0; + + return _libraryDelaySettings.TryGetValue(libraryName, out int delay) ? delay : 0; + } + + /// + /// 특정 도서관의 지연시간 설정을 저장 + /// + /// 도서관 이름 + /// 지연시간(초) + public void SetDelay(string libraryName, int delaySeconds) + { + if (string.IsNullOrEmpty(libraryName)) + return; + + if (delaySeconds < 0) + delaySeconds = 0; + + _libraryDelaySettings[libraryName] = delaySeconds; + SaveSettings(); + + Console.WriteLine($"도서관 지연시간 설정 저장: {libraryName} = {delaySeconds}초"); + } + + /// + /// 모든 도서관 지연시간 설정을 가져옴 + /// + /// 도서관명-지연시간 딕셔너리 + public Dictionary GetAllSettings() + { + return new Dictionary(_libraryDelaySettings); + } + + /// + /// 특정 도서관의 지연시간 설정을 제거 + /// + /// 도서관 이름 + public void RemoveDelay(string libraryName) + { + if (string.IsNullOrEmpty(libraryName)) + return; + + if (_libraryDelaySettings.ContainsKey(libraryName)) + { + _libraryDelaySettings.Remove(libraryName); + SaveSettings(); + Console.WriteLine($"도서관 지연시간 설정 제거: {libraryName}"); + } + } + + /// + /// 모든 지연시간 설정을 초기화 + /// + public void ClearAllSettings() + { + _libraryDelaySettings.Clear(); + SaveSettings(); + Console.WriteLine("모든 도서관 지연시간 설정 초기화"); + } + } +} \ No newline at end of file diff --git a/unimarc/unimarc/Program.cs b/unimarc/unimarc/Program.cs index b501d02..354aae5 100644 --- a/unimarc/unimarc/Program.cs +++ b/unimarc/unimarc/Program.cs @@ -30,14 +30,9 @@ namespace WindowsFormsApp1 Application.Run(new Main()); } - - - - - static void DB_InitSetting() { - UniMarc.Properties.Settings.Default.IP = ConvertIP(UniMarc.Properties.Settings.Default.IP); + UniMarc.Properties.Settings.Default.IP = ConvertIP(UniMarc.Properties.Settings.Default.IP); UniMarc.Properties.Settings.Default.Port = Convert2to10(UniMarc.Properties.Settings.Default.Port); UniMarc.Properties.Settings.Default.Uid = ConvertAscii(UniMarc.Properties.Settings.Default.Uid); UniMarc.Properties.Settings.Default.pwd = ConvertAscii(UniMarc.Properties.Settings.Default.pwd); diff --git a/unimarc/unimarc/Properties/AssemblyInfo.cs b/unimarc/unimarc/Properties/AssemblyInfo.cs index d620c5a..7b74652 100644 --- a/unimarc/unimarc/Properties/AssemblyInfo.cs +++ b/unimarc/unimarc/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를 // 기본값으로 할 수 있습니다. // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2025.09.03.2030")] -[assembly: AssemblyFileVersion("2025.09.03.2030")] +[assembly: AssemblyVersion("2025.09.16.0000")] +[assembly: AssemblyFileVersion("2025.09.16.0000")] diff --git a/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs b/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs index 395368d..bb5b7e3 100644 --- a/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/AnsanLibSearcher.cs @@ -397,8 +397,10 @@ namespace BokBonCheck // 페이지 변경을 감지하는 메서드 await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); - // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + // SearchAsync에서 PageSource 가져오기 + var htmlContent = _driver.PageSource; + var resultCount = ExtractBookCount(htmlContent, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -423,128 +425,40 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; - - if (driver.Url.EndsWith("DetailSearchResult") == false) - { - errmessage = "결과페이지가아님"; - return -1; - } - + resulthtml = string.Empty; try { - // 1. 검색결과가 없는 경우 확인 - try + // 검색 결과가 없다는 메시지 확인 + var noResultPatterns = new[] { - var noResultElements = driver.FindElements(By.XPath("//*[contains(text(), '검색결과가 없습니다') or contains(text(), '검색된 자료가 없습니다')]")); - if (noResultElements.Count > 0) + @"검색결과가 없습니다", + @"검색된 자료가 없습니다", + @"자료가 없습니다", + @"총 0 건", + @"총 0건" + }; + + foreach (var pattern in noResultPatterns) + { + if (htmlContent.Contains(pattern)) { errmessage = "검색결과없음"; - return 0; - } - } - catch - { - // 검색결과가 있는 경우로 진행 - } - - - RetryPoint: - bool retry = false; - // 2. 안산시 도서관 특화: span.count.gothic em 요소에서 직접 추출 - try - { - - - var countElement = driver.FindElement(By.CssSelector("span.count.gothic em")); - if (countElement != null) - { - // Text 대신 InnerHtml이나 GetAttribute 사용해보기 - var countText = countElement.Text.Trim(); - var innerHTML = countElement.GetAttribute("innerHTML"); - var outerHTML = countElement.GetAttribute("outerHTML"); - - Console.WriteLine($"count 요소 .Text: '{countText}'"); - Console.WriteLine($"count 요소 innerHTML: '{innerHTML}'"); - Console.WriteLine($"count 요소 outerHTML: '{outerHTML}'"); - - // innerHTML이나 outerHTML에서 텍스트 추출 시도 - string textToUse = !string.IsNullOrEmpty(countText) ? countText : innerHTML; - if (string.IsNullOrEmpty(textToUse)) + var match = System.Text.RegularExpressions.Regex.Match(htmlContent, pattern); + if (match.Success) { - textToUse = outerHTML; - } - - Console.WriteLine($"추출할 텍스트: '{textToUse}'"); - - // "총 0 건 " 형태에서 숫자 추출 - var match = Regex.Match(textToUse, @"총\s*(\d+)\s*건", RegexOptions.IgnoreCase); - if (match.Success && int.TryParse(match.Groups[1].Value, out int count)) - { - if (count == 0) - { - errmessage = "검색결과없음"; - Console.WriteLine("검색 결과: 0건"); - return 0; - } - errmessage = $"검색성공({count}권)"; - Console.WriteLine($"검색 결과: {count}건"); - return count; + resulthtml = ExtractResultContext(htmlContent, match); } else { - Console.WriteLine($"정규식 매칭 실패: '{textToUse}'"); + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; } + return 0; } } - catch (Exception ex1) - { - Console.WriteLine($"span.count.gothic em 요소 추출 실패: {ex1.Message}"); - } - - // 3. span.count 전체에서 추출 시도 - try - { - var countSpan = driver.FindElement(By.CssSelector("span.count")); - if (countSpan != null) - { - var fullText = countSpan.Text.Trim(); - Console.WriteLine($"count span 전체 텍스트: '{fullText}'"); - - var match = Regex.Match(fullText, @"총\s*(\d+)\s*건", RegexOptions.IgnoreCase); - if (match.Success && int.TryParse(match.Groups[1].Value, out int count)) - { - if (count == 0) - { - errmessage = "검색결과없음"; - return 0; - } - errmessage = $"검색성공({count}권)"; - return count; - } - } - } - catch (Exception ex2) - { - Console.WriteLine($"span.count 요소 추출 실패: {ex2.Message}"); - } - - // 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[] @@ -560,11 +474,14 @@ namespace BokBonCheck foreach (var pattern in htmlPatterns) { - var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) { + // 매칭된 부분과 상위 태그 추출하여 resulthtml에 저장 + resulthtml = ExtractResultContext(htmlContent, match); + if (count == 0) { errmessage = "검색결과없음"; @@ -576,28 +493,81 @@ namespace BokBonCheck } } - if (retry == false) - { - Console.WriteLine( "결과를 찾을 수 없어 재시도"); - retry = true; - Task.Delay(1000); - goto RetryPoint; - } - else - { - errmessage = "결과수량을찾을수없음"; - return -1; - } - + errmessage = "결과수량을찾을수없음"; + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + return -1; } catch (Exception ex) { errmessage = ex.Message; + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } + // 페이지 변경을 감지하는 메서드 public async Task WaitForPageChange(WebDriverWait wait) { diff --git a/unimarc/unimarc/SearchModel/BookSearchService.cs b/unimarc/unimarc/SearchModel/BookSearchService.cs index 94634b7..4d85522 100644 --- a/unimarc/unimarc/SearchModel/BookSearchService.cs +++ b/unimarc/unimarc/SearchModel/BookSearchService.cs @@ -13,6 +13,8 @@ namespace BokBonCheck public DateTime SearchTime { get; set; } public string ErrorMessage { get; set; } public bool IsSuccess { get; set; } + + public string Resulthtml { get; set; } } public class BookSearchService diff --git a/unimarc/unimarc/SearchModel/BukguLibSearcher.cs b/unimarc/unimarc/SearchModel/BukguLibSearcher.cs index 76d7430..43a3da0 100644 --- a/unimarc/unimarc/SearchModel/BukguLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/BukguLibSearcher.cs @@ -244,8 +244,9 @@ namespace BokBonCheck // 페이지 변경을 감지하는 메서드 await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); - // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + var htmlContent = _driver.PageSource; + var resultCount = ExtractBookCount(htmlContent, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -270,73 +271,39 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; + try { - // 1. totalCount 요소에서 직접 추출 시도 - try + // 검색 결과가 없다는 메시지 확인 + var noResultPatterns = new[] { - 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 { } + @"검색결과가 없습니다", + @"검색된 자료가 없습니다", + @"자료가 없습니다", + @"전체 0 건" + }; - // 전체 텍스트에서 정규식으로 추출 - var totalCountText = totalCountElement.Text; - var match = Regex.Match(totalCountText, @"전체\s*(\d+)\s*건", RegexOptions.IgnoreCase); + foreach (var pattern in noResultPatterns) + { + if (htmlContent.Contains(pattern)) + { + errmessage = "검색결과없음"; + var match = System.Text.RegularExpressions.Regex.Match(htmlContent, pattern); if (match.Success) { - if (int.TryParse(match.Groups[1].Value, out int count)) - { - if (count == 0) - { - errmessage = "검색결과없음"; - return 0; - } - errmessage = $"검색성공({count}권)"; - return count; - } + resulthtml = ExtractResultContext(htmlContent, match); } + else + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } + return 0; } } - 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[] @@ -350,11 +317,14 @@ namespace BokBonCheck foreach (var pattern in htmlPatterns) { - var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) { + // 매칭된 부분과 상위 태그 추출하여 resulthtml에 저장 + resulthtml = ExtractResultContext(htmlContent, match); + if (count == 0) { errmessage = "검색결과없음"; @@ -367,16 +337,80 @@ namespace BokBonCheck } errmessage = "결과수량을찾을수없음"; + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; return -1; } catch (Exception ex) { errmessage = ex.Message; + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } + // 페이지 변경을 감지하는 메서드 public async Task WaitForPageChange(WebDriverWait wait) { diff --git a/unimarc/unimarc/SearchModel/ChosunTechLibSearcher.cs b/unimarc/unimarc/SearchModel/ChosunTechLibSearcher.cs index 8d1c14f..73d04a0 100644 --- a/unimarc/unimarc/SearchModel/ChosunTechLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/ChosunTechLibSearcher.cs @@ -69,7 +69,7 @@ namespace BokBonCheck // 브라우저와 유사한 헤더 추가 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("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -84,7 +84,8 @@ namespace BokBonCheck var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -112,19 +113,21 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errorMessage) + private int ExtractBookCount(string htmlContent, out string errorMessage, out string resulthtml) { errorMessage = string.Empty; + resulthtml = string.Empty; try { // 1. 검색 결과가 없는 경우 확인 if (htmlContent.Contains("검색결과가 없습니다") || htmlContent.Contains("검색된 자료가 없습니다") || - htmlContent.Contains("자료가 없습니다") || + htmlContent.Contains("자룼가 없습니다") || htmlContent.Contains("0 Results:")) { errorMessage = "검색결과없음"; + resulthtml = "검색결과없음"; return 0; } @@ -147,8 +150,11 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; Console.WriteLine($"조선이공대학교도서관 검색 결과: {count}건"); return count; @@ -173,22 +179,27 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; Console.WriteLine($"조선이공대학교도서관 검색 결과: {count}건"); return count; } } } - + + // 패턴을 찾지 못한 경우 errorMessage = "검색결과 패턴을 찾을 수 없음"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; Console.WriteLine("조선이공대학교도서관 검색결과 패턴을 찾을 수 없음"); return -1; } catch (Exception ex) { errorMessage = $"결과 분석 오류: {ex.Message}"; + resulthtml = "결과 분석 오류: " + ex.Message; return -1; } } @@ -197,5 +208,67 @@ namespace BokBonCheck { throw new NotImplementedException(); } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/ChosunUnivLibSearcher.cs b/unimarc/unimarc/SearchModel/ChosunUnivLibSearcher.cs index d9c7536..2f27496 100644 --- a/unimarc/unimarc/SearchModel/ChosunUnivLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/ChosunUnivLibSearcher.cs @@ -69,7 +69,7 @@ namespace BokBonCheck // 브라우저와 유사한 헤더 추가 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("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -84,7 +84,8 @@ namespace BokBonCheck var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -112,9 +113,10 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errorMessage) + private int ExtractBookCount(string htmlContent, out string errorMessage, out string resulthtml) { errorMessage = string.Empty; + resulthtml = string.Empty; try { @@ -125,6 +127,7 @@ namespace BokBonCheck htmlContent.Contains("총 0건")) { errorMessage = "검색결과없음"; + resulthtml = "검색결과없음"; return 0; } @@ -147,8 +150,11 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; Console.WriteLine($"조선대학교중앙도서관 검색 결과: {count}건"); return count; @@ -173,22 +179,28 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; Console.WriteLine($"조선대학교중앙도서관 검색 결과: {count}건"); return count; } } } - + + // 패턴을 찾지 못한 경우 errorMessage = "검색결과 패턴을 찾을 수 없음"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; Console.WriteLine("조선대학교중앙도서관 검색결과 패턴을 찾을 수 없음"); return -1; } catch (Exception ex) { errorMessage = $"결과 분석 오류: {ex.Message}"; + resulthtml = "결과 분석 오류: " + ex.Message; return -1; } } @@ -197,5 +209,67 @@ namespace BokBonCheck { throw new NotImplementedException(); } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/GochangLibSearcher.cs b/unimarc/unimarc/SearchModel/GochangLibSearcher.cs index 90fb781..6be35f1 100644 --- a/unimarc/unimarc/SearchModel/GochangLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/GochangLibSearcher.cs @@ -148,7 +148,10 @@ namespace BokBonCheck await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + // SearchAsync에서 PageSource 가져오기 + var htmlContent = _driver.PageSource; + var resultCount = ExtractBookCount(htmlContent, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -173,60 +176,54 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; + try { - try + var noResultPatterns = new[] { - var noResultElement = driver.FindElement(By.XPath("//*[contains(text(), '검색된 도서가 없습니다') or contains(text(), '검색결과가 없습니다')]")); - if (noResultElement != null) + @"검색된 도서가 없습니다", + @"검색결과가 없습니다", + @"검색된 자료가 없습니다" + }; + + foreach (var pattern in noResultPatterns) + { + if (htmlContent.Contains(pattern)) { errmessage = "검색결과없음"; - return 0; - } - } - catch - { - } - - try - { - var resultElement = driver.FindElement(By.CssSelector(".subTapContWrap.on p")); - if (resultElement != null) - { - var resultText = resultElement.Text; - var match = Regex.Match(resultText, @"총\s*(\d+)건", RegexOptions.IgnoreCase); - if (match.Success) + // 검색결과 없음 메시지를 포함한 HTML 조각 추출 + var index = htmlContent.IndexOf(pattern); + if (index >= 0) { - if (int.TryParse(match.Groups[1].Value, out int count)) + var startIndex = Math.Max(0, index - 100); + var endIndex = Math.Min(htmlContent.Length, index + pattern.Length + 100); + resulthtml = htmlContent.Substring(startIndex, endIndex - startIndex); + + // 상위 태그 찾기 시도 + try { - if (count == 0) + var match = System.Text.RegularExpressions.Regex.Match(htmlContent, pattern); + if (match.Success) { - errmessage = "검색결과없음"; - return 0; + resulthtml = ExtractResultContext(htmlContent, match); } - errmessage = $"검색성공({count}권)"; - return count; + } + catch + { + // 실패시 기본 추출 결과 사용 } } + else + { + resulthtml = pattern; + } + return 0; } } - catch (Exception ex) - { - Console.WriteLine($"결과 요소 검색 중 오류: {ex.Message}"); - } - - var pageSource = driver.PageSource; - - if (pageSource.Contains("검색된 도서가 없습니다") || - pageSource.Contains("검색결과가 없습니다") || - pageSource.Contains("검색된 자료가 없습니다")) - { - errmessage = "검색결과없음"; - return 0; - } var htmlPatterns = new[] { @@ -237,11 +234,14 @@ namespace BokBonCheck foreach (var pattern in htmlPatterns) { - var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) { + // 매칭된 부분과 상위 태그 추출하여 resulthtml에 저장 + resulthtml = ExtractResultContext(htmlContent, match); + if (count == 0) { errmessage = "검색결과없음"; @@ -254,16 +254,80 @@ namespace BokBonCheck } errmessage = "결과수량을찾을수없음"; + resulthtml = "결과수량을찾을수없음"; return -1; } catch (Exception ex) { errmessage = ex.Message; + resulthtml = "ExtractBookCount 오류: " + ex.Message; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } + public async Task WaitForPageChange(WebDriverWait wait) { try diff --git a/unimarc/unimarc/SearchModel/GoheungLibSearcher.cs b/unimarc/unimarc/SearchModel/GoheungLibSearcher.cs index fde295f..f6e734d 100644 --- a/unimarc/unimarc/SearchModel/GoheungLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/GoheungLibSearcher.cs @@ -218,8 +218,9 @@ namespace BokBonCheck // 페이지 변경을 감지하는 메서드 await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); - // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + var htmlContent = _driver.PageSource; + var resultCount = ExtractBookCount(htmlContent, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -244,67 +245,21 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = 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 건")) + if (htmlContent.Contains("검색결과가 없습니다") || + htmlContent.Contains("검색된 자료가 없습니다") || + htmlContent.Contains("자료가 없습니다") || + htmlContent.Contains("전체 0 건")) { errmessage = "검색결과없음"; + resulthtml = "검색결과가 없습니다"; return 0; } @@ -319,11 +274,14 @@ namespace BokBonCheck foreach (var pattern in htmlPatterns) { - var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) { + // 매칭된 부분과 상위 태그 추출하여 resulthtml에 저장 + resulthtml = ExtractResultContext(htmlContent, match); + if (count == 0) { errmessage = "검색결과없음"; @@ -335,49 +293,81 @@ namespace BokBonCheck } } - // 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 = "결과수량을찾을수없음"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } catch (Exception ex) { errmessage = ex.Message; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } + // 페이지 변경을 감지하는 메서드 public async Task WaitForPageChange(WebDriverWait wait) { diff --git a/unimarc/unimarc/SearchModel/GwangjuCityLibSearcher.cs b/unimarc/unimarc/SearchModel/GwangjuCityLibSearcher.cs index b419b4e..15db808 100644 --- a/unimarc/unimarc/SearchModel/GwangjuCityLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/GwangjuCityLibSearcher.cs @@ -155,8 +155,9 @@ namespace BokBonCheck // 페이지 변경을 감지하는 메서드 await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); - // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + var htmlContent = _driver.PageSource; + var resultCount = ExtractBookCount(htmlContent, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -181,87 +182,38 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; + try { - // 1. 검색결과가 없는 경우 확인 - try + // 검색 결과가 없다는 메시지 확인 + var noResultPatterns = new[] { - var noResultElement = driver.FindElement(By.XPath("//p[contains(text(), '조회된 도서가 없습니다')]")); - if (noResultElement != null) + @"조회된 도서가 없습니다", + @"검색결과가 없습니다", + @"검색된 자료가 없습니다" + }; + + foreach (var pattern in noResultPatterns) + { + if (htmlContent.Contains(pattern)) { 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); + var match = System.Text.RegularExpressions.Regex.Match(htmlContent, pattern); if (match.Success) { - if (int.TryParse(match.Groups[1].Value, out int count)) - { - if (count == 0) - { - errmessage = "검색결과없음"; - return 0; - } - errmessage = $"검색성공({count}권)"; - return count; - } + resulthtml = ExtractResultContext(htmlContent, match); } + else + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } + return 0; } } - 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[] @@ -274,11 +226,14 @@ namespace BokBonCheck foreach (var pattern in htmlPatterns) { - var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) { + // 매칭된 부분과 상위 태그 추출하여 resulthtml에 저장 + resulthtml = ExtractResultContext(htmlContent, match); + if (count == 0) { errmessage = "검색결과없음"; @@ -291,16 +246,80 @@ namespace BokBonCheck } errmessage = "결과수량을찾을수없음"; + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; return -1; } catch (Exception ex) { errmessage = ex.Message; + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } + // 페이지 변경을 감지하는 메서드 public async Task WaitForPageChange(WebDriverWait wait) { diff --git a/unimarc/unimarc/SearchModel/GwangjuDongguLibSearcher.cs b/unimarc/unimarc/SearchModel/GwangjuDongguLibSearcher.cs index 5f16854..6d8142d 100644 --- a/unimarc/unimarc/SearchModel/GwangjuDongguLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/GwangjuDongguLibSearcher.cs @@ -81,10 +81,11 @@ namespace BokBonCheck // 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("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + request.Headers.Add("Accept-Language", "ko-KR,ko;q=0.9,en;q=0.1"); + request.Headers.Add("Accept-Charset", "UTF-8,*;q=0.1"); + // Accept-Encoding 제거 - 압축으로 인한 인코딩 문제 방지 request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -96,10 +97,50 @@ namespace BokBonCheck throw new HttpRequestException($"HTTP {(int)response.StatusCode} {response.StatusCode}: {errorContent}"); } - var htmlContent = await response.Content.ReadAsStringAsync(); + // 인코딩 문제 해결: 서버 응답의 Content-Type 확인 후 적절한 인코딩 사용 + string htmlContent; + var contentType = response.Content.Headers.ContentType?.ToString() ?? ""; + Console.WriteLine($"광주동구 Content-Type: {contentType}"); + + if (contentType.Contains("charset=")) + { + // 서버에서 지정한 charset 사용 + htmlContent = await response.Content.ReadAsStringAsync(); + } + else + { + // charset이 명시되지 않은 경우 수동 처리 + var responseBytes = await response.Content.ReadAsByteArrayAsync(); + + // UTF-8 먼저 시도 + htmlContent = Encoding.UTF8.GetString(responseBytes); + + // 한글이 없거나 깨진 경우 EUC-KR 시도 + if (!ContainsKorean(htmlContent) || htmlContent.Contains("�")) + { + try + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + var euckrEncoding = Encoding.GetEncoding("EUC-KR"); + var euckrContent = euckrEncoding.GetString(responseBytes); + if (ContainsKorean(euckrContent)) + { + htmlContent = euckrContent; + Console.WriteLine("광주동구: EUC-KR 인코딩 사용"); + } + } + catch (Exception encEx) + { + Console.WriteLine($"광주동구 EUC-KR 변환 오류: {encEx.Message}"); + } + } + } // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + + // ResultHtml에 분석용 HTML 저장 (디버깅용으로 일부만) + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -126,10 +167,11 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errorMessage) + private int ExtractBookCount(string htmlContent, out string errorMessage,out string resulthtml) { errorMessage = string.Empty; - + resulthtml=string.Empty; + try { // 1. 검색 결과가 없는 경우 확인 @@ -141,18 +183,23 @@ namespace BokBonCheck return 0; } - // 2. 검색 결과 수량 추출:

전체 2

+ // 2. 검색 결과 수량 추출 - 더 포괄적인 패턴 사용 var patterns = new[] { - @"]*class=""totalCount""[^>]*>전체\s*\s*(\d+)\s*\s*건

", - @"전체\s*\s*(\d+)\s*\s*건", - @"\s*(\d+)\s*\s*건", - @"총\s*(\d+)\s*건" + // 기본 패턴들 + @"전체\s*(?:\*\*)?(\d+)(?:\*\*)?\s*건", // 전체 **N** 건 또는 전체 N 건 + @"]*>.*?전체\s*(?:\*\*)?(\d+)(?:\*\*)?\s*건.*?", // h태그 안의 전체 N 건 + @"]*class=""totalCount""[^>]*>전체\s*\s*(\d+)\s*\s*건

", // 원래 패턴 + @"전체\s*\s*(\d+)\s*\s*건", // strong 태그 + @"\s*(\d+)\s*\s*건", // strong만 + @"총\s*(\d+)\s*건", // 총 N 건 + @"검색결과\s*:\s*(\d+)\s*건", // 검색결과: N 건 + @"(\d+)\s*건의\s*검색결과", // N 건의 검색결과 }; foreach (var pattern in patterns) { - var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) @@ -160,25 +207,31 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; // 매칭된 부분만 저장 return 0; } + + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); + errorMessage = $"검색성공({count}권)"; - Console.WriteLine($"광주동구 검색 결과: {count}건"); + Console.WriteLine($"광주동구 검색 결과: {count}건 (패턴: {pattern})"); return count; } } } - // 3. 더 자세한 패턴으로 시도 (줄바꿈 포함) + // 3. 멀티라인 패턴으로 재시도 var multilinePatterns = new[] { - @"전체\s*\s*\r?\n?\s*(\d+)\s*\r?\n?\s*\s*건", - @"\s*\r?\n?\s*(\d+)\s*\r?\n?\s*\s*건" + @"전체\s*(?:\*\*)?[\r\n\s]*(\d+)[\r\n\s]*(?:\*\*)?\s*건", + @"전체\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); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) @@ -186,15 +239,24 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; // 매칭된 부분만 저장 return 0; } + + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); + errorMessage = $"검색성공({count}권)"; - Console.WriteLine($"광주동구 검색 결과: {count}건"); + Console.WriteLine($"광주동구 검색 결과: {count}건 (멀티라인 패턴)"); return count; } } } - + + // 4. 패턴을 찾지 못한 경우 - 디버깅을 위한 HTML 내용 일부 출력 + resulthtml = "검색결과 패턴을 찾을 수 없음"; + Console.WriteLine($"광주동구 HTML 샘플: {resulthtml}"); + errorMessage = "검색결과 패턴을 찾을 수 없음"; Console.WriteLine("광주동구 검색결과 패턴을 찾을 수 없음"); return -1; @@ -202,6 +264,8 @@ namespace BokBonCheck catch (Exception ex) { errorMessage = $"결과 분석 오류: {ex.Message}"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; // 오류 시 HTML 일부 저장 + Console.WriteLine($"광주동구 결과 분석 오류: {ex.Message}"); return -1; } } @@ -210,5 +274,83 @@ namespace BokBonCheck { throw new NotImplementedException(); } + + /// + /// 문자열에 한글이 포함되어 있는지 확인 + /// + private bool ContainsKorean(string text) + { + if (string.IsNullOrEmpty(text)) + return false; + + foreach (char c in text) + { + if (c >= 0xAC00 && c <= 0xD7A3) // 한글 유니코드 범위 + return true; + } + return false; + } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/GwangjuSeoguLibSearcher.cs b/unimarc/unimarc/SearchModel/GwangjuSeoguLibSearcher.cs index c0ea2bc..37b673b 100644 --- a/unimarc/unimarc/SearchModel/GwangjuSeoguLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/GwangjuSeoguLibSearcher.cs @@ -337,7 +337,8 @@ namespace BokBonCheck await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); // 검색 결과 수 추출 (페이징 포함) - var (resultCount, ermsg) = await ExtractBookCountWithPaging(_driver, searchTerm); + var (resultCount, ermsg, resultHtml) = await ExtractBookCountWithPaging(_driver, searchTerm); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -362,13 +363,15 @@ namespace BokBonCheck return result; } - private async Task<(int count, string message)> ExtractBookCountWithPaging(IWebDriver driver, string searchTerm) + private async Task<(int count, string message, string resultHtml)> ExtractBookCountWithPaging(IWebDriver driver, string searchTerm) { string errmessage = string.Empty; int totalCount = 0; try { + var htmlContent = driver.PageSource; + string resultHtml = string.Empty; // 첫 번째 페이지에서 테이블 row 수 확인 var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); @@ -381,7 +384,28 @@ namespace BokBonCheck if (firstPageRows.Count == 0) { errmessage = "검색결과없음"; - return (0, errmessage); + // "검색결과가 없습니다"와 같은 메시지를 찾아 context 추출 시도 + var noResultPatterns = new[] + { + @"검색결과가 없습니다", + @"검색된 자료가 없습니다", + @"자료가 없습니다" + }; + + foreach (var pattern in noResultPatterns) + { + if (htmlContent.Contains(pattern)) + { + var match = System.Text.RegularExpressions.Regex.Match(htmlContent, pattern); + if (match.Success) + { + resultHtml = ExtractResultContext(htmlContent, match); + return (0, errmessage, resultHtml); + } + } + } + resultHtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + return (0, errmessage, resultHtml); } totalCount += firstPageRows.Count; @@ -390,7 +414,28 @@ namespace BokBonCheck catch { errmessage = "검색결과없음"; - return (0, errmessage); + // "검색결과가 없습니다"와 같은 메시지를 찾아 context 추출 시도 + var noResultPatterns = new[] + { + @"검색결과가 없습니다", + @"검색된 자료가 없습니다", + @"자료가 없습니다" + }; + + foreach (var pattern in noResultPatterns) + { + if (htmlContent.Contains(pattern)) + { + var match = System.Text.RegularExpressions.Regex.Match(htmlContent, pattern); + if (match.Success) + { + resultHtml = ExtractResultContext(htmlContent, match); + return (0, errmessage, resultHtml); + } + } + } + resultHtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + return (0, errmessage, resultHtml); } // 페이징이 있는지 확인하고 각 페이지 방문 @@ -436,18 +481,42 @@ namespace BokBonCheck if (totalCount == 0) { errmessage = "검색결과없음"; - return (0, errmessage); + // "검색결과가 없습니다"와 같은 메시지를 찾아 context 추출 시도 + var noResultPatterns = new[] + { + @"검색결과가 없습니다", + @"검색된 자료가 없습니다", + @"자료가 없습니다", + @"전체\s*0\s*건" + }; + + foreach (var pattern in noResultPatterns) + { + if (htmlContent.Contains(pattern)) + { + var match = System.Text.RegularExpressions.Regex.Match(htmlContent, pattern); + if (match.Success) + { + resultHtml = ExtractResultContext(htmlContent, match); + return (0, errmessage, resultHtml); + } + } + } + resultHtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + return (0, errmessage, resultHtml); } + resultHtml = $"검색성공 - 총 {totalCount}건"; errmessage = $"검색성공({totalCount}권)"; Console.WriteLine($"전체 검색 결과: {totalCount}건"); - return (totalCount, errmessage); + return (totalCount, errmessage, resultHtml); } catch (Exception ex) { errmessage = ex.Message; - return (-1, errmessage); + string resultHtml = "오류 발생"; + return (-1, errmessage, resultHtml); } } @@ -492,5 +561,67 @@ namespace BokBonCheck Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}"); } } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, System.Text.RegularExpressions.Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = System.Text.RegularExpressions.Regex.Matches(searchText, tagPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = System.Text.RegularExpressions.Regex.Match(tagMatch.Value, @"<(\w+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = System.Text.RegularExpressions.Regex.Match(searchText, closeTagPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/GwangsanLibSearcher.cs b/unimarc/unimarc/SearchModel/GwangsanLibSearcher.cs index d32cd53..baf1a66 100644 --- a/unimarc/unimarc/SearchModel/GwangsanLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/GwangsanLibSearcher.cs @@ -74,7 +74,7 @@ namespace BokBonCheck // 브라우저와 유사한 헤더 추가 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("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -84,7 +84,8 @@ namespace BokBonCheck var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -112,9 +113,10 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errorMessage) + private int ExtractBookCount(string htmlContent, out string errorMessage, out string resulthtml) { errorMessage = string.Empty; + resulthtml = string.Empty; try { @@ -124,6 +126,7 @@ namespace BokBonCheck htmlContent.Contains("자료가 없습니다")) { errorMessage = "검색결과없음"; + resulthtml = "검색결과없음"; return 0; } @@ -146,8 +149,11 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; return count; } @@ -171,20 +177,25 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; return count; } } } - + + // 패턴을 찾지 못한 경우 + resulthtml = "검색결과 패턴을 찾을 수 없음"; errorMessage = "검색결과 패턴을 찾을 수 없음"; return -1; } catch (Exception ex) { errorMessage = $"결과 분석 오류: {ex.Message}"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } @@ -193,5 +204,67 @@ namespace BokBonCheck { throw new NotImplementedException(); } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/GyeongnamLibSearcher.cs b/unimarc/unimarc/SearchModel/GyeongnamLibSearcher.cs index 1cce63c..7a66ffa 100644 --- a/unimarc/unimarc/SearchModel/GyeongnamLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/GyeongnamLibSearcher.cs @@ -107,8 +107,9 @@ namespace BokBonCheck // JavaScript 렌더링 대기 await Task.Delay(3000); - // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + var htmlContent = _driver.PageSource; + var resultCount = ExtractBookCount(htmlContent, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -133,71 +134,21 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; try { - // JavaScript 실행 후 실제 렌더링된 DOM에서 결과 추출 - var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); - - // 1. 검색결과가 없는 경우 확인 - try - { - var noResultElements = driver.FindElements(By.XPath("//*[contains(text(), '검색결과가 없습니다') or contains(text(), '검색된 자료가 없습니다')]")); - if (noResultElements.Count > 0) - { - errmessage = "검색결과없음"; - return 0; - } - } - catch - { - // 검색결과가 있는 경우로 진행 - } - - // 2. total_area에서 결과 수량 추출 (JavaScript 렌더링 후) - try - { - var totalAreaElement = wait.Until(d => d.FindElement(By.CssSelector("div.total_area p.total span"))); - if (totalAreaElement != null) - { - var countText = totalAreaElement.Text.Trim(); - Console.WriteLine($"total_area 텍스트: '{countText}'"); - - // "총 3건" 형태에서 숫자 추출 - var match = Regex.Match(countText, @"총\s*(\d+)\s*건", RegexOptions.IgnoreCase); - if (match.Success && int.TryParse(match.Groups[1].Value, out int count)) - { - if (count == 0) - { - errmessage = "검색결과없음"; - Console.WriteLine("검색 결과: 0건"); - return 0; - } - errmessage = $"검색성공({count}권)"; - Console.WriteLine($"검색 결과: {count}건"); - return count; - } - } - } - catch (Exception ex1) - { - Console.WriteLine($"total_area 요소 추출 실패: {ex1.Message}"); - } - - // 3. 페이지 소스에서 렌더링된 결과 추출 - var pageSource = driver.PageSource; - Console.WriteLine("페이지 소스에서 결과 추출 시도"); - // 검색 결과가 없다는 메시지 확인 - if (pageSource.Contains("검색결과가 없습니다") || - pageSource.Contains("검색된 자료가 없습니다") || - pageSource.Contains("자료가 없습니다") || - pageSource.Contains("총 0건")) + if (htmlContent.Contains("검색결과가 없습니다") || + htmlContent.Contains("검색된 자료가 없습니다") || + htmlContent.Contains("자료가 없습니다") || + htmlContent.Contains("총 0건")) { errmessage = "검색결과없음"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return 0; } @@ -212,11 +163,14 @@ namespace BokBonCheck foreach (var pattern in htmlPatterns) { - var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) { + // 매칭된 부분과 상위 태그 추출하여 resulthtml에 저장 + resulthtml = ExtractResultContext(htmlContent, match); + if (count == 0) { errmessage = "검색결과없음"; @@ -229,16 +183,80 @@ namespace BokBonCheck } errmessage = "결과수량을찾을수없음"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } catch (Exception ex) { errmessage = ex.Message; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } + // 완전한 페이지 로딩 대기 메서드 private async Task WaitForCompletePageLoad(WebDriverWait wait) { diff --git a/unimarc/unimarc/SearchModel/IksanLibSearcher.cs b/unimarc/unimarc/SearchModel/IksanLibSearcher.cs index 69750ee..43521f4 100644 --- a/unimarc/unimarc/SearchModel/IksanLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/IksanLibSearcher.cs @@ -74,7 +74,7 @@ namespace BokBonCheck // 브라우저와 유사한 헤더 추가 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("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -84,7 +84,8 @@ namespace BokBonCheck var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -110,9 +111,10 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; try { // 실제 HTML 구조에 맞는 패턴으로 수정 @@ -139,6 +141,7 @@ namespace BokBonCheck if (pattern.Contains(@"\s*0\s*")) { errmessage = "검색결과없음"; + resulthtml = match.Value; return 0; } @@ -148,17 +151,21 @@ namespace BokBonCheck if (count == 0) { errmessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errmessage = $"검색성공({count}권)"; return count; } } } - // 디버깅을 위해 HTML 내용 일부 출력 + // 패턴을 찾지 못한 경우 + resulthtml = "검색결과 패턴을 찾을 수 없음"; Console.WriteLine($"HTML 내용 일부: {htmlContent.Substring(0, Math.Min(1000, htmlContent.Length))}"); - + errmessage = "결과수량을찾을수없음"; return -1; @@ -166,6 +173,7 @@ namespace BokBonCheck catch (Exception ex) { errmessage = ex.Message; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } @@ -177,5 +185,66 @@ namespace BokBonCheck await Task.CompletedTask; } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } diff --git a/unimarc/unimarc/SearchModel/JeonbukEduLibSearcher.cs b/unimarc/unimarc/SearchModel/JeonbukEduLibSearcher.cs index 71f5208..7136450 100644 --- a/unimarc/unimarc/SearchModel/JeonbukEduLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/JeonbukEduLibSearcher.cs @@ -227,8 +227,9 @@ namespace BokBonCheck // 페이지 변경을 감지하는 메서드 await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); - // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + var htmlContent = _driver.PageSource; + var resultCount = ExtractBookCount(htmlContent, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -253,71 +254,20 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = 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("검색된 자료가 없습니다")) + if (htmlContent.Contains("0건이 검색되었습니다") || + htmlContent.Contains("검색결과가 없습니다") || + htmlContent.Contains("검색된 자료가 없습니다")) { errmessage = "검색결과없음"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return 0; } @@ -332,11 +282,14 @@ namespace BokBonCheck foreach (var pattern in htmlPatterns) { - var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) { + // 매칭된 부분과 상위 태그 추출하여 resulthtml에 저장 + resulthtml = ExtractResultContext(htmlContent, match); + if (count == 0) { errmessage = "검색결과없음"; @@ -349,16 +302,80 @@ namespace BokBonCheck } errmessage = "결과수량을찾을수없음"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } catch (Exception ex) { errmessage = ex.Message; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } + // 페이지 변경을 감지하는 메서드 public async Task WaitForPageChange(WebDriverWait wait) { diff --git a/unimarc/unimarc/SearchModel/JunnamEduJiheaNuriSearcher.cs b/unimarc/unimarc/SearchModel/JunnamEduJiheaNuriSearcher.cs index 22f3904..98025d3 100644 --- a/unimarc/unimarc/SearchModel/JunnamEduJiheaNuriSearcher.cs +++ b/unimarc/unimarc/SearchModel/JunnamEduJiheaNuriSearcher.cs @@ -260,7 +260,8 @@ namespace BokBonCheck await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -285,11 +286,13 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; try { + var htmlContent = driver.PageSource; // 먼저 검색결과가 없는 경우 확인 try { @@ -298,6 +301,7 @@ namespace BokBonCheck if (noResultText.Contains("검색결과가 없습니다")) { errmessage = "검색결과없음"; + resulthtml = noResultText; return 0; } } @@ -318,17 +322,36 @@ namespace BokBonCheck if (int.TryParse(match.Groups[1].Value, out int vqty)) { errmessage = $"검색성공({vqty}건)"; + resulthtml = ExtractResultContext(htmlContent, match); return vqty; } else { errmessage = $"수량값오류({match.Groups[1].Value})"; + resulthtml = match.Value; return -1; } } else { errmessage = "수량항목없음"; + // 매칭된 부분이 없는 경우, 기본적으로 pageInfoText 부분 추출 시도 + try + { + var dummyMatch = System.Text.RegularExpressions.Regex.Match(pageInfoText, @"전체.*"); + if (dummyMatch.Success) + { + resulthtml = ExtractResultContext(htmlContent, dummyMatch); + } + else + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } + } + catch + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } return -1; } } @@ -336,6 +359,7 @@ namespace BokBonCheck { // page_info가 없는 경우 검색결과가 없는 것으로 판단 errmessage = "검색결과없음"; + resulthtml = "검색결과 없음: " + ex.Message; return 0; } @@ -343,11 +367,72 @@ namespace BokBonCheck catch (Exception ex) { errmessage = ex.Message; + resulthtml = "ExtractBookCount 오류: " + ex.Message; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } // 페이지 변경을 감지하는 메서드 public async Task WaitForPageChange(WebDriverWait wait) diff --git a/unimarc/unimarc/SearchModel/JunnamEduSearcher.cs b/unimarc/unimarc/SearchModel/JunnamEduSearcher.cs index e440bbc..29b8bc0 100644 --- a/unimarc/unimarc/SearchModel/JunnamEduSearcher.cs +++ b/unimarc/unimarc/SearchModel/JunnamEduSearcher.cs @@ -272,7 +272,8 @@ namespace BokBonCheck await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -297,11 +298,13 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; try { + var htmlContent = driver.PageSource; // 먼저 검색결과가 없는 경우 확인 try { @@ -310,6 +313,7 @@ namespace BokBonCheck if (noResultText.Contains("검색결과가 없습니다")) { errmessage = "검색결과없음"; + resulthtml = noResultText; return 0; } } @@ -330,17 +334,36 @@ namespace BokBonCheck if (int.TryParse(match.Groups[1].Value, out int vqty)) { errmessage = $"검색성공({vqty}건)"; + resulthtml = ExtractResultContext(htmlContent, match); return vqty; } else { errmessage = $"수량값오류({match.Groups[1].Value})"; + resulthtml = match.Value; return -1; } } else { errmessage = "수량항목없음"; + // 매칭된 부분이 없는 경우, 기본적으로 pageInfoText 부분 추출 시도 + try + { + var dummyMatch = System.Text.RegularExpressions.Regex.Match(pageInfoText, @"전체.*"); + if (dummyMatch.Success) + { + resulthtml = ExtractResultContext(htmlContent, dummyMatch); + } + else + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } + } + catch + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } return -1; } } @@ -348,6 +371,27 @@ namespace BokBonCheck { // page_info가 없는 경우 검색결과가 없는 것으로 판단 errmessage = "검색결과없음"; + // "검색결과가 없습니다"와 같은 메시지를 찾아 context 추출 시도 + var noResultPatterns = new[] + { + @"검색결과가 없습니다", + @"검색된 자료가 없습니다", + @"자료가 없습니다" + }; + + foreach (var pattern in noResultPatterns) + { + if (htmlContent.Contains(pattern)) + { + var match = System.Text.RegularExpressions.Regex.Match(htmlContent, pattern); + if (match.Success) + { + resulthtml = ExtractResultContext(htmlContent, match); + return 0; + } + } + } + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; return 0; } @@ -355,11 +399,72 @@ namespace BokBonCheck catch (Exception ex) { errmessage = ex.Message; + resulthtml = "ExtractBookCount 오류: " + ex.Message; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } // 페이지 변경을 감지하는 메서드 public async Task WaitForPageChange(WebDriverWait wait) diff --git a/unimarc/unimarc/SearchModel/KcmLibSearcher.cs b/unimarc/unimarc/SearchModel/KcmLibSearcher.cs index 654c050..da854fb 100644 --- a/unimarc/unimarc/SearchModel/KcmLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/KcmLibSearcher.cs @@ -85,7 +85,7 @@ namespace BokBonCheck // 브라우저와 유사한 헤더 추가 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("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -100,7 +100,8 @@ namespace BokBonCheck var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -128,9 +129,10 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errorMessage) + private int ExtractBookCount(string htmlContent, out string errorMessage, out string resulthtml) { errorMessage = string.Empty; + resulthtml = string.Empty; try { @@ -141,6 +143,7 @@ namespace BokBonCheck htmlContent.Contains("총 0 건이 검색되었습니다")) { errorMessage = "검색결과없음"; + resulthtml = "검색결과없음"; return 0; } @@ -163,8 +166,11 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; Console.WriteLine($"KCM자료검색시스템 검색 결과: {count}건"); return count; @@ -189,15 +195,19 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; Console.WriteLine($"KCM자료검색시스템 검색 결과: {count}건"); return count; } } } - + + // 패턴을 찾지 못한 경우 + resulthtml = "검색결과 패턴을 찾을 수 없음"; errorMessage = "검색결과 패턴을 찾을 수 없음"; Console.WriteLine("KCM자료검색시스템 검색결과 패턴을 찾을 수 없음"); return -1; @@ -205,6 +215,7 @@ namespace BokBonCheck catch (Exception ex) { errorMessage = $"결과 분석 오류: {ex.Message}"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } @@ -213,5 +224,67 @@ namespace BokBonCheck { throw new NotImplementedException(); } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/KwangjuCityEduLibrarySearcher.cs b/unimarc/unimarc/SearchModel/KwangjuCityEduLibrarySearcher.cs index 777bb8f..72c1594 100644 --- a/unimarc/unimarc/SearchModel/KwangjuCityEduLibrarySearcher.cs +++ b/unimarc/unimarc/SearchModel/KwangjuCityEduLibrarySearcher.cs @@ -193,10 +193,21 @@ namespace BokBonCheck await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(30))); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver); + var resultCount = ExtractBookCount(_driver, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; - result.BookCount = resultCount; - result.IsSuccess = true; + if (resultCount == -1) + { + result.BookCount = 0; + result.IsSuccess = false; + result.ErrorMessage = errorMessage; + } + else + { + result.BookCount = resultCount; + result.IsSuccess = true; + result.ErrorMessage = errorMessage; + } } catch (Exception ex) { @@ -211,10 +222,14 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver) + private int ExtractBookCount(IWebDriver driver, out string errorMessage, out string resulthtml) { + errorMessage = string.Empty; + resulthtml = string.Empty; try { + var htmlContent = driver.PageSource; + // div.search-result 내부의 span에서 '전체 N' 텍스트 추출 var resultDiv = driver.FindElement(By.CssSelector("div.ndls_result")); var span = resultDiv.FindElement(By.XPath(".//span[contains(text(),'전체')]")); @@ -222,13 +237,106 @@ namespace BokBonCheck var match = System.Text.RegularExpressions.Regex.Match(text, @"전체\s*(\d+)"); if (match.Success) { - return int.Parse(match.Groups[1].Value); + if (int.TryParse(match.Groups[1].Value, out int count)) + { + if (count == 0) + { + errorMessage = "검색결과없음"; + resulthtml = text; + return 0; + } + errorMessage = $"검색성공({count}건)"; + resulthtml = ExtractResultContext(htmlContent, match); + return count; + } } - return 0; + errorMessage = "수량항목없음"; + // 매칭된 부분이 없는 경우, 기본적으로 text 부분 추출 시도 + try + { + var dummyMatch = System.Text.RegularExpressions.Regex.Match(text, @"전체.*"); + if (dummyMatch.Success) + { + resulthtml = ExtractResultContext(htmlContent, dummyMatch); + } + else + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } + } + catch + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } + return -1; } - catch + catch (Exception ex) { - return 0; + errorMessage = ex.Message; + resulthtml = "오류발생"; + return -1; + } + } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 } } diff --git a/unimarc/unimarc/SearchModel/MokpoLibSearcher.cs b/unimarc/unimarc/SearchModel/MokpoLibSearcher.cs index e1b6497..adf8cce 100644 --- a/unimarc/unimarc/SearchModel/MokpoLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/MokpoLibSearcher.cs @@ -77,7 +77,7 @@ namespace BokBonCheck // 브라우저와 유사한 헤더 추가 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("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -92,7 +92,8 @@ namespace BokBonCheck var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -120,9 +121,10 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errorMessage) + private int ExtractBookCount(string htmlContent, out string errorMessage, out string resulthtml) { errorMessage = string.Empty; + resulthtml = string.Empty; try { @@ -146,8 +148,11 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; return count; } @@ -161,6 +166,7 @@ namespace BokBonCheck htmlContent.Contains("총 0권(개)")) { errorMessage = "검색결과없음"; + resulthtml = "검색결과없음"; return 0; } @@ -183,20 +189,25 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; return count; } } } - + + // 패턴을 찾지 못한 경우 + resulthtml = "검색결과 패턴을 찾을 수 없음"; errorMessage = "검색결과 패턴을 찾을 수 없음"; return -1; } catch (Exception ex) { errorMessage = $"결과 분석 오류: {ex.Message}"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } @@ -205,5 +216,67 @@ namespace BokBonCheck { throw new NotImplementedException(); } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/MuanLibSearcher.cs b/unimarc/unimarc/SearchModel/MuanLibSearcher.cs index 8b81f1f..099f100 100644 --- a/unimarc/unimarc/SearchModel/MuanLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/MuanLibSearcher.cs @@ -196,9 +196,9 @@ namespace BokBonCheck // 페이지 변경을 감지하는 메서드 await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); - - // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + var htmlContent = _driver.PageSource; + var resultCount = ExtractBookCount(htmlContent, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -223,71 +223,56 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; try { - // 검색결과 페이지 대기 - var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); - - // 1. 검색결과가 없는 경우 확인 - try + // 검색 결과가 없다는 메시지 확인 + var noResultPatterns = new[] { - var noResultElements = driver.FindElements(By.XPath("//*[contains(text(), '검색결과가 없습니다') or contains(text(), '검색된 자료가 없습니다')]")); - if (noResultElements.Count > 0) + @"검색결과가 없습니다", + @"검색된 자료가 없습니다", + @"자료가 없습니다", + @"전체 0 건" + }; + + foreach (var pattern in noResultPatterns) + { + if (htmlContent.Contains(pattern)) { errmessage = "검색결과없음"; + // 검색결과 없음 메시지를 포함한 HTML 조각 추출 + var index = htmlContent.IndexOf(pattern); + if (index >= 0) + { + var startIndex = Math.Max(0, index - 100); + var endIndex = Math.Min(htmlContent.Length, index + pattern.Length + 100); + resulthtml = htmlContent.Substring(startIndex, endIndex - startIndex); + + // 상위 태그 찾기 시도 + try + { + var match = System.Text.RegularExpressions.Regex.Match(htmlContent, pattern); + if (match.Success) + { + resulthtml = ExtractResultContext(htmlContent, match); + } + } + catch + { + // 실패시 기본 추출 결과 사용 + } + } + else + { + resulthtml = pattern; + } return 0; } } - catch - { - // 검색결과가 있는 경우로 진행 - } - - // 2. totalCount에서 결과 수량 추출 - try - { - var totalCountElement = wait.Until(d => d.FindElement(By.CssSelector("p.totalCount strong"))); - if (totalCountElement != null) - { - var countText = totalCountElement.Text.Trim(); - Console.WriteLine($"totalCount 텍스트: '{countText}'"); - - if (int.TryParse(countText, out int count)) - { - if (count == 0) - { - errmessage = "검색결과없음"; - Console.WriteLine("검색 결과: 0건"); - return 0; - } - errmessage = $"검색성공({count}권)"; - Console.WriteLine($"검색 결과: {count}건"); - return count; - } - } - } - catch (Exception ex1) - { - Console.WriteLine($"totalCount 요소 추출 실패: {ex1.Message}"); - } - - // 3. 페이지 소스에서 결과 추출 - var pageSource = driver.PageSource; - Console.WriteLine("페이지 소스에서 결과 추출 시도"); - - // 검색 결과가 없다는 메시지 확인 - if (pageSource.Contains("검색결과가 없습니다") || - pageSource.Contains("검색된 자료가 없습니다") || - pageSource.Contains("자료가 없습니다") || - pageSource.Contains("전체 0 건")) - { - errmessage = "검색결과없음"; - return 0; - } // HTML에서 다양한 패턴 찾기 var htmlPatterns = new[] @@ -299,11 +284,14 @@ namespace BokBonCheck foreach (var pattern in htmlPatterns) { - var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase); + var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int count)) { + // 매칭된 부분과 상위 태그 추출하여 resulthtml에 저장 + resulthtml = ExtractResultContext(htmlContent, match); + if (count == 0) { errmessage = "검색결과없음"; @@ -316,16 +304,80 @@ namespace BokBonCheck } errmessage = "결과수량을찾을수없음"; + resulthtml = "결과수량을찾을수없음"; return -1; } catch (Exception ex) { errmessage = ex.Message; + resulthtml = "ExtractBookCount 오류: " + ex.Message; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } + // 완전한 페이지 로딩 대기 메서드 private async Task WaitForCompletePageLoad(WebDriverWait wait) { diff --git a/unimarc/unimarc/SearchModel/NamguLibrarySearcher.cs b/unimarc/unimarc/SearchModel/NamguLibrarySearcher.cs index 80e3a90..abd2ec3 100644 --- a/unimarc/unimarc/SearchModel/NamguLibrarySearcher.cs +++ b/unimarc/unimarc/SearchModel/NamguLibrarySearcher.cs @@ -269,7 +269,8 @@ namespace BokBonCheck await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15))); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg); + var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { result.BookCount = 0; @@ -294,11 +295,14 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage) + private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; try { + var htmlContent = driver.PageSource; + // div.search-result 내부의 span에서 '전체 N' 텍스트 추출 var resultDiv = driver.FindElement(By.CssSelector("div.search-result")); @@ -306,6 +310,7 @@ namespace BokBonCheck if (bodytext.Contains("검색결과가 없습니다")) { errmessage = "검색결과없음"; + resulthtml = bodytext; return 0; } @@ -315,6 +320,7 @@ namespace BokBonCheck if (searchTerm.Contains(searchtitle) == false) { errmessage = $"검색어불일치({searchtitle}/{searchTerm})"; + resulthtml = searchtitle; return -1; } var span = resultDiv.FindElement(By.XPath(".//span[contains(text(),'전체')]")); @@ -325,10 +331,13 @@ namespace BokBonCheck if (int.TryParse(match.Groups[1].Value, out int vqty) == false) { errmessage = $"수량값오류({match.Groups[1].Value})"; + resulthtml = match.Value; return -1; } else { + errmessage = $"검색성공({vqty}건)"; + resulthtml = ExtractResultContext(htmlContent, match); searchTerm = string.Empty; return vqty; } @@ -336,6 +345,23 @@ namespace BokBonCheck else { errmessage = "수량항목없음"; + // 매칭된 부분이 없는 경우, 기본적으로 text 부분 추출 시도 + try + { + var dummyMatch = System.Text.RegularExpressions.Regex.Match(text, @"전체.*"); + if (dummyMatch.Success) + { + resulthtml = ExtractResultContext(htmlContent, dummyMatch); + } + else + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } + } + catch + { + resulthtml = htmlContent.Length > 500 ? htmlContent.Substring(0, 500) : htmlContent; + } return -1; } @@ -343,11 +369,72 @@ namespace BokBonCheck catch (Exception ex) { errmessage = ex.Message; + resulthtml = "ExtractBookCount 오류: " + ex.Message; return -1; } } + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } // 페이지 변경을 감지하는 메서드 public async Task WaitForPageChange(WebDriverWait wait) diff --git a/unimarc/unimarc/SearchModel/SuncheonLibSearcher.cs b/unimarc/unimarc/SearchModel/SuncheonLibSearcher.cs index 91d571a..2297b98 100644 --- a/unimarc/unimarc/SearchModel/SuncheonLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/SuncheonLibSearcher.cs @@ -82,7 +82,7 @@ namespace BokBonCheck // 브라우저와 유사한 헤더 추가 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("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -97,7 +97,8 @@ namespace BokBonCheck var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -125,9 +126,10 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errorMessage) + private int ExtractBookCount(string htmlContent, out string errorMessage, out string resulthtml) { errorMessage = string.Empty; + resulthtml = string.Empty; try { @@ -150,8 +152,11 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; return count; } @@ -165,6 +170,7 @@ namespace BokBonCheck htmlContent.Contains("총 0건")) { errorMessage = "검색결과없음"; + resulthtml = "검색결과없음"; return 0; } @@ -178,8 +184,10 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = resultConMatch.Value; return 0; } + resulthtml = ExtractResultContext(htmlContent, resultConMatch); errorMessage = $"검색성공({count}권)"; return count; } @@ -203,20 +211,25 @@ namespace BokBonCheck if (count == 0) { errorMessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; return count; } } } - + + // 패턴을 찾지 못한 경우 + resulthtml = "검색결과 패턴을 찾을 수 없음"; errorMessage = "검색결과 패턴을 찾을 수 없음"; return -1; } catch (Exception ex) { errorMessage = $"결과 분석 오류: {ex.Message}"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } @@ -225,5 +238,67 @@ namespace BokBonCheck { throw new NotImplementedException(); } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } \ No newline at end of file diff --git a/unimarc/unimarc/SearchModel/WandoLibSearcher.cs b/unimarc/unimarc/SearchModel/WandoLibSearcher.cs index 5d79869..5d7487b 100644 --- a/unimarc/unimarc/SearchModel/WandoLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/WandoLibSearcher.cs @@ -86,7 +86,7 @@ namespace BokBonCheck // 브라우저와 유사한 헤더 추가 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("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -96,7 +96,8 @@ namespace BokBonCheck var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -122,15 +123,17 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errmessage) + private int ExtractBookCount(string htmlContent, out string errmessage, out string resulthtml) { errmessage = string.Empty; + resulthtml = string.Empty; try { // 검색 결과가 없다는 메시지 확인 if (htmlContent.Contains("0권(개)") || htmlContent.Contains("검색결과가 없습니다")) { errmessage = "검색결과없음"; + resulthtml = "검색결과없음"; return 0; } @@ -153,14 +156,19 @@ namespace BokBonCheck if (count == 0) { errmessage = "검색결과없음"; + resulthtml = match.Value; return 0; } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errmessage = $"검색성공({count}권)"; return count; } } } + // 패턴을 찾지 못한 경우 + resulthtml = "검색결과 패턴을 찾을 수 없음"; errmessage = "결과수량을찾을수없음"; return -1; @@ -168,6 +176,7 @@ namespace BokBonCheck catch (Exception ex) { errmessage = ex.Message; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } @@ -178,5 +187,67 @@ namespace BokBonCheck // HTTP 방식에서는 즉시 응답이 오므로 대기 불필요 await Task.CompletedTask; } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } diff --git a/unimarc/unimarc/SearchModel/YeosuLibSearcher.cs b/unimarc/unimarc/SearchModel/YeosuLibSearcher.cs index 956aa95..95599cc 100644 --- a/unimarc/unimarc/SearchModel/YeosuLibSearcher.cs +++ b/unimarc/unimarc/SearchModel/YeosuLibSearcher.cs @@ -82,7 +82,7 @@ namespace BokBonCheck // 브라우저와 유사한 헤더 추가 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("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); @@ -92,7 +92,8 @@ namespace BokBonCheck var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 - var resultCount = ExtractBookCount(htmlContent, out string errorMessage); + var resultCount = ExtractBookCount(htmlContent, out string errorMessage, out string resultHtml); + result.Resulthtml = resultHtml; if (resultCount == -1) { @@ -120,9 +121,10 @@ namespace BokBonCheck return result; } - private int ExtractBookCount(string htmlContent, out string errorMessage) + private int ExtractBookCount(string htmlContent, out string errorMessage, out string resulthtml) { errorMessage = string.Empty; + resulthtml = string.Empty; try { @@ -134,12 +136,21 @@ namespace BokBonCheck { if (int.TryParse(match.Groups[1].Value, out int count)) { + if (count == 0) + { + errorMessage = "검색결과없음"; + resulthtml = match.Value; + return 0; + } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, match); errorMessage = $"검색성공({count}권)"; return count; } else { errorMessage = $"수량값오류({match.Groups[1].Value})"; + resulthtml = match.Value; return -1; } } @@ -153,11 +164,21 @@ namespace BokBonCheck { if (int.TryParse(alternateMatch.Groups[1].Value, out int count)) { + if (count == 0) + { + errorMessage = "검색결과없음"; + resulthtml = alternateMatch.Value; + return 0; + } + // 매칭된 부분과 그 상위 태그를 찾아서 저장 + resulthtml = ExtractResultContext(htmlContent, alternateMatch); errorMessage = $"검색성공({count}권)"; return count; } } - + + // 패턴을 찾지 못한 경우 + resulthtml = "검색결과 패턴을 찾을 수 없음"; errorMessage = "검색결과 패턴을 찾을 수 없음"; return -1; } @@ -165,6 +186,7 @@ namespace BokBonCheck catch (Exception ex) { errorMessage = $"결과 분석 오류: {ex.Message}"; + resulthtml = "검색결과 패턴을 찾을 수 없음"; return -1; } } @@ -173,5 +195,67 @@ namespace BokBonCheck { throw new NotImplementedException(); } + + /// + /// 매칭된 결과와 그 상위 태그를 추출 + /// + private string ExtractResultContext(string htmlContent, Match match) + { + try + { + var matchIndex = match.Index; + var matchLength = match.Length; + + // 매칭된 위치 앞쪽에서 상위 태그 시작 찾기 + var startSearchIndex = Math.Max(0, matchIndex - 200); // 매칭 위치 200자 전부터 검색 + var searchText = htmlContent.Substring(startSearchIndex, matchIndex - startSearchIndex + matchLength + Math.Min(200, htmlContent.Length - matchIndex - matchLength)); + + // 상위 태그 패턴들 (div, p, h1-h6, span 등) + var tagPatterns = new[] { @"<(div|p|h[1-6]|span|section|article)[^>]*>", @"<[^>]+>" }; + + string resultContext = match.Value; // 기본값은 매칭된 부분만 + + foreach (var tagPattern in tagPatterns) + { + // 매칭된 부분 앞에서 가장 가까운 태그 시작 찾기 + var tagMatches = Regex.Matches(searchText, tagPattern, RegexOptions.IgnoreCase); + + for (int i = tagMatches.Count - 1; i >= 0; i--) + { + var tagMatch = tagMatches[i]; + if (tagMatch.Index < (matchIndex - startSearchIndex)) + { + // 태그 이름 추출 + var tagName = Regex.Match(tagMatch.Value, @"<(\w+)", RegexOptions.IgnoreCase).Groups[1].Value; + + // 닫는 태그 찾기 + var closeTagPattern = $@"]*>"; + var closeMatch = Regex.Match(searchText, closeTagPattern, RegexOptions.IgnoreCase); + + if (closeMatch.Success && closeMatch.Index > (matchIndex - startSearchIndex)) + { + // 상위 태그와 그 내용을 포함하여 반환 + var startIdx = tagMatch.Index; + var endIdx = closeMatch.Index + closeMatch.Length; + resultContext = searchText.Substring(startIdx, Math.Min(endIdx - startIdx, 500)); // 최대 500자 + return resultContext; + } + } + } + } + + // 상위 태그를 찾지 못한 경우, 매칭 전후 50자씩 포함 + var contextStart = Math.Max(0, matchIndex - 50); + var contextEnd = Math.Min(htmlContent.Length, matchIndex + matchLength + 50); + resultContext = htmlContent.Substring(contextStart, contextEnd - contextStart); + + return resultContext; + } + catch (Exception ex) + { + Console.WriteLine($"ExtractResultContext 오류: {ex.Message}"); + return match.Value; // 오류 시 매칭된 부분만 반환 + } + } } } \ No newline at end of file diff --git a/unimarc/unimarc/Skill.cs b/unimarc/unimarc/Skill.cs index ef9f92d..373c040 100644 --- a/unimarc/unimarc/Skill.cs +++ b/unimarc/unimarc/Skill.cs @@ -63,7 +63,7 @@ namespace WindowsFormsApp1 else dataGridView.Sort(dataGridView.Columns[col], System.ComponentModel.ListSortDirection.Ascending); - + } /// /// * Row헤더에 체크박스를 넣는 기능* @@ -99,7 +99,7 @@ namespace WindowsFormsApp1 } private void datagridview_checkBox_Click(object sender, EventArgs e) { - foreach(DataGridViewRow r in ((DataGridView)sender).Rows) + foreach (DataGridViewRow r in ((DataGridView)sender).Rows) { r.Cells["colCheck"].Value = ((CheckBox)sender).Checked; } @@ -119,16 +119,23 @@ namespace WindowsFormsApp1 char[] columnSplitter = { '\t' }; //get the text from clipboard - IDataObject dataInClipboard = Clipboard.GetDataObject(); - string stringInClipboard = (string)dataInClipboard.GetData(DataFormats.Text); + if (Clipboard.ContainsText() == false) return; + + string stringInClipboard = null; + + if (e.Alt) + stringInClipboard = Clipboard.GetText(TextDataFormat.UnicodeText); + else + stringInClipboard = Clipboard.GetText();// (string)objdata; + //split it into lines //20230209 \r텝 기능과 \n 줄넘김 기능을 같이 공백 제거 처리해버려 공백칸을 활용해야 함에도 제거하는 현상 발생. //텝 공백 문자열 동시에 사용하여 분류 // stringInClipboard= stringInClipboard.Replace("\r", ""); if (stringInClipboard == null) return; - ListrowsInClipboard = stringInClipboard.Split(rowSpliteter, StringSplitOptions.None).ToList(); - rowsInClipboard.RemoveAt(rowsInClipboard.Count-1); + List rowsInClipboard = stringInClipboard.Split(rowSpliteter, StringSplitOptions.None).ToList(); + rowsInClipboard.RemoveAt(rowsInClipboard.Count - 1); //get the row and column of selected cell in dataGridView1 int r = ((DataGridView)sender).SelectedCells[0].RowIndex; int c = ((DataGridView)sender).SelectedCells[0].ColumnIndex; @@ -209,7 +216,7 @@ namespace WindowsFormsApp1 private Rectangle dragBoxFromMouseDown; private int rowIndexFromMouseDown; private int rowIndexOfItemUnderMouseToDrop; - + public void MouseMove(object sender, MouseEventArgs e) { DataGridView dataGridView = sender as DataGridView; @@ -292,7 +299,8 @@ namespace WindowsFormsApp1 string[] db_data = db_res1.Split('|'); string[] db_pur = db_res2.Split('|'); - if (db_res1.Length < 3) { + if (db_res1.Length < 3) + { MessageBox.Show("DB호출 에러!", "Error"); return "False"; } @@ -300,20 +308,26 @@ namespace WindowsFormsApp1 string fax = string.Empty; string emchk = string.Empty; - if (db_pur.Length > 3) { - for(int a= 0; a < db_pur.Length; a++) + if (db_pur.Length > 3) + { + for (int a = 0; a < db_pur.Length; a++) { - if (a % 3 == 0) { // 전화번호 - if (db_pur[a] != "") { + if (a % 3 == 0) + { // 전화번호 + if (db_pur[a] != "") + { tel = db_pur[a]; } } - if (a % 3 == 1) { // 팩스 - if (db_pur[a] != "") { + if (a % 3 == 1) + { // 팩스 + if (db_pur[a] != "") + { fax = db_pur[a]; } } - if (a % 3 == 2) { // 팩스 이메일 체크 + if (a % 3 == 2) + { // 팩스 이메일 체크 emchk = db_pur[a]; } } @@ -401,7 +415,7 @@ namespace WindowsFormsApp1 #region 주문일자 / 보낸곳 (4행) rng = ws.Range["A4", "C4"]; rng.MergeCells = true; - rng.Value2 = "주문일자 : "+DateTime.Now.ToString("yyyy-MM-dd H:m:ss"); + rng.Value2 = "주문일자 : " + DateTime.Now.ToString("yyyy-MM-dd H:m:ss"); rng.HorizontalAlignment = Excel.XlHAlign.xlHAlignLeft; rng.Font.Bold = true; @@ -495,7 +509,7 @@ namespace WindowsFormsApp1 #region 추신 endcount++; - string 발송처 = "D"+endcount.ToString(); + string 발송처 = "D" + endcount.ToString(); rng = ws.Range["A" + endcount, "C" + endcount]; rng.MergeCells = true; @@ -551,7 +565,7 @@ namespace WindowsFormsApp1 rng2.Font.Bold = true; //////// - rng = ws.Range[발송처, "D"+endcount]; + rng = ws.Range[발송처, "D" + endcount]; rng.MergeCells = true; rng.Value2 = "발 송 처"; rng.Font.Bold = true; @@ -580,10 +594,10 @@ namespace WindowsFormsApp1 application.Interactive = true; application.Quit(); - + return FileName; } - catch(Exception e) + catch (Exception e) { MessageBox.Show(e.ToString()); return "False"; @@ -630,9 +644,9 @@ namespace WindowsFormsApp1 private string Excel_Sub(string[] data) { string[] length = { - "1", "2", "3", "4", "5", - "6", "7", "8", "9", "10", - "11", "12", "13", "14", "15", + "1", "2", "3", "4", "5", + "6", "7", "8", "9", "10", + "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26" }; @@ -646,8 +660,8 @@ namespace WindowsFormsApp1 string count = data.Length.ToString(); string res = string.Empty; - - for(int a = 0; a < length.Length; a++) + + for (int a = 0; a < length.Length; a++) { if (length[a] == count) { @@ -658,7 +672,7 @@ namespace WindowsFormsApp1 } #endregion } - public class Helper_Print + public class Helper_Print { /// /// 행의 갯수 @@ -788,7 +802,7 @@ namespace WindowsFormsApp1 /// /// [0] 발신번호 / [1] 수신번호 /// / [2] 수신자 회사명 / [3 ]수신자명 - public string Send_BaroFax(string file_name, string[] fax_param ) + public string Send_BaroFax(string file_name, string[] fax_param) { BaroService_FAXSoapClient fAXSoapClient = new BaroService_FAXSoapClient(); @@ -824,7 +838,7 @@ namespace WindowsFormsApp1 BaroService_FAXSoapClient fAXSoapClient = new BaroService_FAXSoapClient(); // 수신자회사명, 수신번호, 전송일시, 전송결과, 전송페이지수, 성공페이지수, 전송파일명 - string[] MsgBox_Array = { + string[] MsgBox_Array = { fAXSoapClient.GetFaxMessage(CERTKEY, CorpNum, sendkey).ReceiveCorp, fAXSoapClient.GetFaxMessage(CERTKEY, CorpNum, sendkey).ReceiverNum, fAXSoapClient.GetFaxMessage(CERTKEY, CorpNum, sendkey).SendDT, @@ -1190,9 +1204,9 @@ namespace WindowsFormsApp1 public bool IsConnected { get; set; } private string ipAddr = string.Empty; - private string Port = string.Empty; + private string Port = string.Empty; private string userId = string.Empty; - private string Pwd = string.Empty; + private string Pwd = string.Empty; public FTP() { } @@ -1219,7 +1233,7 @@ namespace WindowsFormsApp1 using (ftpRequest.GetResponse()) { } this.IsConnected = true; } - catch(Exception ex) + catch (Exception ex) { this.LastException = ex; System.Reflection.MemberInfo info = System.Reflection.MethodInfo.GetCurrentMethod(); @@ -1288,7 +1302,7 @@ namespace WindowsFormsApp1 buff = null; } } - catch(Exception ex) + catch (Exception ex) { MessageBox.Show(ex.ToString()); this.LastException = ex; @@ -1392,14 +1406,14 @@ namespace WindowsFormsApp1 if (reader != null) reader.Close(); - foreach(string file in result.ToString().Split('\n')) + foreach (string file in result.ToString().Split('\n')) { resultList.Add(file); } } return resultList; } - catch(Exception ex) + catch (Exception ex) { this.LastException = ex; @@ -1420,12 +1434,12 @@ namespace WindowsFormsApp1 try { - foreach(string tmpFolder in arrDir) + foreach (string tmpFolder in arrDir) { try { if (tmpFolder == string.Empty) continue; - + currentDir += @"/" + tmpFolder; string url = string.Format(@"FTP://{0}:{1}/{2}", this.ipAddr, this.Port, currentDir); @@ -1453,9 +1467,11 @@ namespace WindowsFormsApp1 private void checkDir(string localFullPathFile) { FileInfo finfo = new FileInfo(localFullPathFile); - if (!finfo.Exists) { + if (!finfo.Exists) + { DirectoryInfo dInfo = new DirectoryInfo(finfo.DirectoryName); - if (!dInfo.Exists) { + if (!dInfo.Exists) + { dInfo.Create(); } } @@ -1552,30 +1568,35 @@ namespace WindowsFormsApp1 int tDown = 0; for (int a = 0; a < array_text.Count; a++) { - // if (array_text[a] == "") continue; + // if (array_text[a] == "") continue; num.Add(array_text[a].Substring(0, 3)); - if (array_text[a][5] == '▼') { + if (array_text[a][5] == '▼') + { array_text[a] = array_text[a].Remove(0, 3); } - else { + else + { array_text[a] = array_text[a].Remove(0, 5); } 가변길이 += array_text[a] + "\n"; int textLength = 0; - if (EncodingType == "UTF-8") { + if (EncodingType == "UTF-8") + { textLength = Encoding.UTF8.GetBytes(array_text[a]).Length - WordCheck(array_text[a], "▲") - WordCheck(array_text[a], "▼"); } - else if (EncodingType == "UniCode") { + else if (EncodingType == "UniCode") + { textLength = Encoding.Unicode.GetBytes(array_text[a]).Length - WordCheck(array_text[a], "▲") - WordCheck(array_text[a], "▼"); } - else { // ANSI + else + { // ANSI textLength = Encoding.Default.GetBytes(array_text[a]).Length - WordCheck(array_text[a], "▲") - WordCheck(array_text[a], "▼"); @@ -1587,12 +1608,13 @@ namespace WindowsFormsApp1 for (int a = 0; a < array_text.Count; a++) { - if (a == 0) { //total.Add("0"); + if (a == 0) + { //total.Add("0"); tTotal.Add(0); } else { - // total.Add(total[a - 1] + count[a - 1]); + // total.Add(total[a - 1] + count[a - 1]); tTotal.Add(tTotal[a - 1] + tCount[a - 1]); } //else if (a == 1) @@ -1609,7 +1631,7 @@ namespace WindowsFormsApp1 // else c = Convert.ToInt32(Encoding.Default.GetBytes(array_text[a - 2]).Length.ToString()) - WordCheck(array_text[a - 2], "▲") - WordCheck(array_text[a - 2], "▼"); // int res = b + c; // total.Add(res.ToString()); - + } string[] str_num = num.ToArray(); @@ -1626,7 +1648,7 @@ namespace WindowsFormsApp1 // else if (total[a].Length == 2) { total[a] = total[a].Insert(0, "000"); } // else if (total[a].Length == 1) { total[a] = total[a].Insert(0, "0000"); } // 디렉토리 += str_num[a] + count[a] + total[a] + "\n"; - 디렉토리 += str_num[a] + tCount[a].ToString().PadLeft(4, '0') + tTotal[a].ToString().PadLeft(5, '0'); + 디렉토리 += str_num[a] + tCount[a].ToString().PadLeft(4, '0') + tTotal[a].ToString().PadLeft(5, '0'); } string[] 리더부 = { "00000","n", "a", "m", " ", @@ -1642,11 +1664,11 @@ namespace WindowsFormsApp1 string dp = 가변길이 + 디렉토리; int recode = 0; - if (EncodingType == "UTF-8") recode = Encoding.UTF8.GetBytes(dp).Length- WordCheck(dp, "▲") - WordCheck(dp, "▼") - WordCheck(dp, "↔"); + if (EncodingType == "UTF-8") recode = Encoding.UTF8.GetBytes(dp).Length - WordCheck(dp, "▲") - WordCheck(dp, "▼") - WordCheck(dp, "↔"); else if (EncodingType == "UniCode") recode = Encoding.Unicode.GetBytes(dp).Length - WordCheck(dp, "▲") - WordCheck(dp, "▼") - WordCheck(dp, "↔"); - else recode = Encoding.Default.GetBytes(dp).Length- WordCheck(dp, "▲") - WordCheck(dp, "▼") - WordCheck(dp, "↔"); - - + else recode = Encoding.Default.GetBytes(dp).Length - WordCheck(dp, "▲") - WordCheck(dp, "▼") - WordCheck(dp, "↔"); + + 리더부[0] = insert_Zero(recode + 24, 5); int data_addr = 24 + Encoding.Default.GetBytes(디렉토리).Length - WordCheck(디렉토리, "▲"); @@ -1693,7 +1715,7 @@ namespace WindowsFormsApp1 return result; } #endregion - + /// /// 추가하고 싶은 태그를 뷰형태의 마크에 추가하는 함수. @@ -1706,7 +1728,7 @@ namespace WindowsFormsApp1 if (Tag.Length < 3) return ""; int TargetTagNum = Convert.ToInt32(Tag.Substring(0, 3)); - + string[] SplitView = TypeView.Split('\n'); List View = new List(SplitView); @@ -1731,9 +1753,9 @@ namespace WindowsFormsApp1 /// 추가할 태그 (태그명\t지시기호\t태그내용) /// 뷰형태의 마크 /// - public string AddTagInMarc(int pTargetTagNum,string pAddTag, string pTargetData)//TagTarget Num 을 찾아서 있을경우는 해당 Tag 데이터를 전송, 없을경우는 신규로 해야함. + public string AddTagInMarc(int pTargetTagNum, string pAddTag, string pTargetData)//TagTarget Num 을 찾아서 있을경우는 해당 Tag 데이터를 전송, 없을경우는 신규로 해야함. { - + if (pAddTag.Length < 3) return ""; string tRet = pTargetData; // ex ) 020 : ~~~ 에 XXXX 내용줄 뒤에 추가 @@ -1827,7 +1849,7 @@ namespace WindowsFormsApp1 } } } - } + } else {// 기존 태그 변경 int endIdx = SplitView[a].IndexOf("▼", startIdx + 1); @@ -1906,7 +1928,7 @@ namespace WindowsFormsApp1 /// 마크 데이터 /// 추출할 함수(배열) /// - public string[] Take_Tag(string marc, string[] search,bool pSearchTag = false) + public string[] Take_Tag(string marc, string[] search, bool pSearchTag = false) { string[] ary = marc.Split(''); string[] tag = res_dir(ary[0].Substring(24)); @@ -1958,7 +1980,7 @@ namespace WindowsFormsApp1 //memo = result[b]; start += 2; int end = -1; - if (tmp.Length > 1) end=tmp.IndexOf("", start); + if (tmp.Length > 1) end = tmp.IndexOf("", start); if (memo == result[b]) break; @@ -2188,7 +2210,8 @@ namespace WindowsFormsApp1 /// EventArgs public void Int_Comma(object sender, EventArgs e) { - if (((TextBox)sender).Text != "") { + if (((TextBox)sender).Text != "") + { string text; text = ((TextBox)sender).Text.Replace(",", ""); ((TextBox)sender).Text = String.Format("{0:#,###}", Convert.ToInt32(text)); @@ -2204,7 +2227,7 @@ namespace WindowsFormsApp1 public bool isContainHangul(string value) { char[] charArr = value.ToCharArray(); - foreach(char c in charArr) + foreach (char c in charArr) { if (char.GetUnicodeCategory(c) == System.Globalization.UnicodeCategory.OtherLetter) return true; @@ -2221,7 +2244,7 @@ namespace WindowsFormsApp1 public bool CheckString(string value, string chkString) { int index = value.IndexOf(chkString); - if (index < 0) + if (index < 0) return false; return true; @@ -2264,10 +2287,10 @@ namespace WindowsFormsApp1 public class API { - public string CheckString(string pText,string pStr) + public string CheckString(string pText, string pStr) { string tRet = pText; - Regex reg = new Regex(@"([\"+pStr+"]+)" + @"[가-힣]+");//+ @"([\>]+)");//new Regex(@"([\<]+)"+ @"[ㄱ-ㅎ가-힣]+"+@"([\>]+)"); + Regex reg = new Regex(@"([\" + pStr + "]+)" + @"[가-힣]+");//+ @"([\>]+)");//new Regex(@"([\<]+)"+ @"[ㄱ-ㅎ가-힣]+"+@"([\>]+)"); MatchCollection tMatch = reg.Matches(tRet); for (int i = 0; i < tMatch.Count; i++) { @@ -2326,8 +2349,9 @@ namespace WindowsFormsApp1 xml = CheckString(xml, "〈"); doc.LoadXml(xml); } - catch (Exception ex){ - return ""; + catch (Exception ex) + { + return ""; } var json = JsonConvert.SerializeXmlNode(doc); @@ -2520,19 +2544,23 @@ namespace WindowsFormsApp1 { if (length == 1) { - try { + try + { tmp_data.Add(docs[Param[b]]["#text"]); } - catch (KeyNotFoundException e) { + catch (KeyNotFoundException e) + { tmp_data.Add(""); } } else { - try { + try + { tmp_data.Add(docs[a][Param[b]]["#text"]); } - catch (KeyNotFoundException e) { + catch (KeyNotFoundException e) + { tmp_data.Add(""); } } @@ -2649,8 +2677,8 @@ namespace WindowsFormsApp1 return dialogResult; } } - public class PrintLine - { + public class PrintLine + { string num { get; set; } string count { get; set; } string list_name { get; set; } @@ -2963,7 +2991,7 @@ namespace WindowsFormsApp1 { string version = "0"; var fn = Application.StartupPath + "\\update.inf"; - if(System.IO.File.Exists(fn)) + if (System.IO.File.Exists(fn)) { StreamReader sr = new StreamReader(fn); while (!sr.EndOfStream) @@ -2976,7 +3004,7 @@ namespace WindowsFormsApp1 } } } - + return version; } } diff --git a/unimarc/unimarc/UniMarc.csproj b/unimarc/unimarc/UniMarc.csproj index 7185567..727ccc0 100644 --- a/unimarc/unimarc/UniMarc.csproj +++ b/unimarc/unimarc/UniMarc.csproj @@ -224,6 +224,7 @@ True Reference.svcmap + diff --git a/unimarc/unimarc/마크/Check_copyWD.Designer.cs b/unimarc/unimarc/마크/Check_copyWD.Designer.cs index bcc1403..9abb84f 100644 --- a/unimarc/unimarc/마크/Check_copyWD.Designer.cs +++ b/unimarc/unimarc/마크/Check_copyWD.Designer.cs @@ -28,7 +28,8 @@ /// private void InitializeComponent() { - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); this.panel1 = new System.Windows.Forms.Panel(); this.chkShowBrowser = new System.Windows.Forms.CheckBox(); this.chkRetryErrData = new System.Windows.Forms.CheckBox(); @@ -50,10 +51,9 @@ this.lbl_PW = new System.Windows.Forms.Label(); this.lbl_ID = new System.Windows.Forms.Label(); this.dv1 = new System.Windows.Forms.DataGridView(); - this.book_name = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.book_comp = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Count = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dvc_remark = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); + this.유니코드문자로붙여넝ㅎ기ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.일반문자로붙여넣기ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.btn_ApplyFilter = new System.Windows.Forms.Button(); this.panel3 = new System.Windows.Forms.Panel(); this.panel6 = new System.Windows.Forms.Panel(); @@ -66,19 +66,30 @@ 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.book_name = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.book_comp = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Count = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dvc_remark = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dvc_resulthtml = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.nudAddDelay = new System.Windows.Forms.NumericUpDown(); + this.label1 = new System.Windows.Forms.Label(); this.panel1.SuspendLayout(); this.groupBox1.SuspendLayout(); this.panel2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.dv1)).BeginInit(); + this.contextMenuStrip1.SuspendLayout(); this.panel3.SuspendLayout(); this.panel6.SuspendLayout(); this.statusStrip1.SuspendLayout(); this.panel4.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nudAddDelay)).BeginInit(); this.SuspendLayout(); // // panel1 // this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel1.Controls.Add(this.label1); + this.panel1.Controls.Add(this.nudAddDelay); this.panel1.Controls.Add(this.chkShowBrowser); this.panel1.Controls.Add(this.chkRetryErrData); this.panel1.Controls.Add(this.groupBox1); @@ -90,7 +101,7 @@ this.panel1.Dock = System.Windows.Forms.DockStyle.Top; this.panel1.Location = new System.Drawing.Point(0, 34); this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(637, 106); + this.panel1.Size = new System.Drawing.Size(719, 106); this.panel1.TabIndex = 0; // // chkShowBrowser @@ -98,7 +109,7 @@ this.chkShowBrowser.AutoSize = true; this.chkShowBrowser.Checked = true; this.chkShowBrowser.CheckState = System.Windows.Forms.CheckState.Checked; - this.chkShowBrowser.Location = new System.Drawing.Point(515, 81); + this.chkShowBrowser.Location = new System.Drawing.Point(618, 50); this.chkShowBrowser.Name = "chkShowBrowser"; this.chkShowBrowser.Size = new System.Drawing.Size(96, 16); this.chkShowBrowser.TabIndex = 8; @@ -125,7 +136,7 @@ this.groupBox1.Controls.Add(this.radTargetAll); this.groupBox1.Location = new System.Drawing.Point(318, 34); this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(304, 39); + this.groupBox1.Size = new System.Drawing.Size(294, 39); this.groupBox1.TabIndex = 6; this.groupBox1.TabStop = false; this.groupBox1.Text = "검색대상"; @@ -133,7 +144,7 @@ // radTargetErr // this.radTargetErr.AutoSize = true; - this.radTargetErr.Location = new System.Drawing.Point(197, 16); + this.radTargetErr.Location = new System.Drawing.Point(239, 16); this.radTargetErr.Name = "radTargetErr"; this.radTargetErr.Size = new System.Drawing.Size(47, 16); this.radTargetErr.TabIndex = 9; @@ -143,7 +154,7 @@ // radTargetEmpty // this.radTargetEmpty.AutoSize = true; - this.radTargetEmpty.Location = new System.Drawing.Point(144, 16); + this.radTargetEmpty.Location = new System.Drawing.Point(172, 16); this.radTargetEmpty.Name = "radTargetEmpty"; this.radTargetEmpty.Size = new System.Drawing.Size(47, 16); this.radTargetEmpty.TabIndex = 8; @@ -153,7 +164,7 @@ // radTargetErrEmpty // this.radTargetErrEmpty.AutoSize = true; - this.radTargetErrEmpty.Location = new System.Drawing.Point(61, 17); + this.radTargetErrEmpty.Location = new System.Drawing.Point(75, 16); this.radTargetErrEmpty.Name = "radTargetErrEmpty"; this.radTargetErrEmpty.Size = new System.Drawing.Size(77, 16); this.radTargetErrEmpty.TabIndex = 7; @@ -209,7 +220,7 @@ // this.btn_Start.Location = new System.Drawing.Point(470, 4); this.btn_Start.Name = "btn_Start"; - this.btn_Start.Size = new System.Drawing.Size(75, 24); + this.btn_Start.Size = new System.Drawing.Size(153, 24); this.btn_Start.TabIndex = 2; this.btn_Start.Text = "검색시작"; this.btn_Start.UseVisualStyleBackColor = true; @@ -217,7 +228,7 @@ // // btn_Stop // - this.btn_Stop.Location = new System.Drawing.Point(548, 4); + this.btn_Stop.Location = new System.Drawing.Point(631, 4); this.btn_Stop.Name = "btn_Stop"; this.btn_Stop.Size = new System.Drawing.Size(75, 24); this.btn_Stop.TabIndex = 2; @@ -227,7 +238,7 @@ // // btn_Close // - this.btn_Close.Location = new System.Drawing.Point(548, 4); + this.btn_Close.Location = new System.Drawing.Point(631, 4); this.btn_Close.Name = "btn_Close"; this.btn_Close.Size = new System.Drawing.Size(75, 24); this.btn_Close.TabIndex = 2; @@ -270,14 +281,14 @@ this.panel2.Dock = System.Windows.Forms.DockStyle.Top; this.panel2.Location = new System.Drawing.Point(0, 0); this.panel2.Name = "panel2"; - this.panel2.Size = new System.Drawing.Size(637, 34); + this.panel2.Size = new System.Drawing.Size(719, 34); this.panel2.TabIndex = 0; // // btn_SiteDenote // - this.btn_SiteDenote.Location = new System.Drawing.Point(462, 5); + this.btn_SiteDenote.Location = new System.Drawing.Point(470, 5); this.btn_SiteDenote.Name = "btn_SiteDenote"; - this.btn_SiteDenote.Size = new System.Drawing.Size(77, 23); + this.btn_SiteDenote.Size = new System.Drawing.Size(155, 23); this.btn_SiteDenote.TabIndex = 4; this.btn_SiteDenote.Text = "사이트 표출"; this.btn_SiteDenote.UseVisualStyleBackColor = true; @@ -307,55 +318,52 @@ this.dv1.BackgroundColor = System.Drawing.SystemColors.Control; this.dv1.BorderStyle = System.Windows.Forms.BorderStyle.None; this.dv1.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single; - dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; - dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Control; - dataGridViewCellStyle2.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129))); - dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.WindowText; - dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight; - dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText; - dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.True; - this.dv1.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle2; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control; + dataGridViewCellStyle1.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129))); + dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.dv1.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1; this.dv1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dv1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.book_name, this.book_comp, this.Count, - this.dvc_remark}); + this.dvc_remark, + this.dvc_resulthtml}); + this.dv1.ContextMenuStrip = this.contextMenuStrip1; this.dv1.Dock = System.Windows.Forms.DockStyle.Fill; this.dv1.Location = new System.Drawing.Point(0, 0); this.dv1.Name = "dv1"; this.dv1.RowTemplate.Height = 23; - this.dv1.Size = new System.Drawing.Size(637, 542); + this.dv1.Size = new System.Drawing.Size(719, 542); this.dv1.TabIndex = 1; this.dv1.RowPostPaint += new System.Windows.Forms.DataGridViewRowPostPaintEventHandler(this.dataGridView1_RowPostPaint); this.dv1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.dataGridView1_KeyDown); // - // book_name + // contextMenuStrip1 // - this.book_name.HeaderText = "도서명(총서명)"; - this.book_name.Name = "book_name"; - this.book_name.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; - this.book_name.Width = 300; + this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.유니코드문자로붙여넝ㅎ기ToolStripMenuItem, + this.일반문자로붙여넣기ToolStripMenuItem}); + this.contextMenuStrip1.Name = "contextMenuStrip1"; + this.contextMenuStrip1.Size = new System.Drawing.Size(215, 48); // - // book_comp + // 유니코드문자로붙여넝ㅎ기ToolStripMenuItem // - this.book_comp.HeaderText = "출판사"; - this.book_comp.Name = "book_comp"; - this.book_comp.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; - this.book_comp.Width = 120; + this.유니코드문자로붙여넝ㅎ기ToolStripMenuItem.Name = "유니코드문자로붙여넝ㅎ기ToolStripMenuItem"; + this.유니코드문자로붙여넝ㅎ기ToolStripMenuItem.Size = new System.Drawing.Size(214, 22); + this.유니코드문자로붙여넝ㅎ기ToolStripMenuItem.Text = "유니코드 문자로 붙여넣기"; + this.유니코드문자로붙여넝ㅎ기ToolStripMenuItem.Click += new System.EventHandler(this.유니코드문자로붙여넝ㅎ기ToolStripMenuItem_Click); // - // Count + // 일반문자로붙여넣기ToolStripMenuItem // - this.Count.HeaderText = "검색 수"; - this.Count.Name = "Count"; - this.Count.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; - this.Count.Width = 50; - // - // dvc_remark - // - this.dvc_remark.HeaderText = "비고"; - this.dvc_remark.Name = "dvc_remark"; - this.dvc_remark.Width = 110; + this.일반문자로붙여넣기ToolStripMenuItem.Name = "일반문자로붙여넣기ToolStripMenuItem"; + this.일반문자로붙여넣기ToolStripMenuItem.Size = new System.Drawing.Size(214, 22); + this.일반문자로붙여넣기ToolStripMenuItem.Text = "일반 문자로 붙여넣기"; + this.일반문자로붙여넣기ToolStripMenuItem.Click += new System.EventHandler(this.일반문자로붙여넣기ToolStripMenuItem_Click); // // btn_ApplyFilter // @@ -376,7 +384,7 @@ this.panel3.Dock = System.Windows.Forms.DockStyle.Fill; this.panel3.Location = new System.Drawing.Point(0, 0); this.panel3.Name = "panel3"; - this.panel3.Size = new System.Drawing.Size(637, 738); + this.panel3.Size = new System.Drawing.Size(719, 738); this.panel3.TabIndex = 3; // // panel6 @@ -386,7 +394,7 @@ this.panel6.Dock = System.Windows.Forms.DockStyle.Fill; this.panel6.Location = new System.Drawing.Point(0, 174); this.panel6.Name = "panel6"; - this.panel6.Size = new System.Drawing.Size(637, 564); + this.panel6.Size = new System.Drawing.Size(719, 564); this.panel6.TabIndex = 3; // // statusStrip1 @@ -396,7 +404,7 @@ this.lbSite}); this.statusStrip1.Location = new System.Drawing.Point(0, 542); this.statusStrip1.Name = "statusStrip1"; - this.statusStrip1.Size = new System.Drawing.Size(637, 22); + this.statusStrip1.Size = new System.Drawing.Size(719, 22); this.statusStrip1.TabIndex = 2; this.statusStrip1.Text = "statusStrip1"; // @@ -424,7 +432,7 @@ this.panel4.Dock = System.Windows.Forms.DockStyle.Top; this.panel4.Location = new System.Drawing.Point(0, 140); this.panel4.Name = "panel4"; - this.panel4.Size = new System.Drawing.Size(637, 34); + this.panel4.Size = new System.Drawing.Size(719, 34); this.panel4.TabIndex = 2; // // chk_RemoveBrit @@ -477,11 +485,60 @@ this.chk_spChar.Text = "특수문자 제거"; this.chk_spChar.UseVisualStyleBackColor = true; // + // book_name + // + this.book_name.HeaderText = "도서명(총서명)"; + this.book_name.Name = "book_name"; + this.book_name.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.book_name.Width = 300; + // + // book_comp + // + this.book_comp.HeaderText = "출판사"; + this.book_comp.Name = "book_comp"; + this.book_comp.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.book_comp.Width = 120; + // + // Count + // + this.Count.HeaderText = "검색 수"; + this.Count.Name = "Count"; + this.Count.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.Count.Width = 50; + // + // dvc_remark + // + this.dvc_remark.HeaderText = "비고"; + this.dvc_remark.Name = "dvc_remark"; + this.dvc_remark.Width = 110; + // + // dvc_resulthtml + // + this.dvc_resulthtml.HeaderText = "결과"; + this.dvc_resulthtml.Name = "dvc_resulthtml"; + // + // nudAddDelay + // + this.nudAddDelay.Location = new System.Drawing.Point(618, 79); + this.nudAddDelay.Name = "nudAddDelay"; + this.nudAddDelay.Size = new System.Drawing.Size(56, 21); + this.nudAddDelay.TabIndex = 9; + this.nudAddDelay.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(559, 83); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(53, 12); + this.label1.TabIndex = 10; + this.label1.Text = "추가지연"; + // // Check_copyWD // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(637, 738); + this.ClientSize = new System.Drawing.Size(719, 738); this.Controls.Add(this.panel3); this.Name = "Check_copyWD"; this.Text = "복본조사(New)"; @@ -494,6 +551,7 @@ this.panel2.ResumeLayout(false); this.panel2.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.dv1)).EndInit(); + this.contextMenuStrip1.ResumeLayout(false); this.panel3.ResumeLayout(false); this.panel6.ResumeLayout(false); this.panel6.PerformLayout(); @@ -501,6 +559,7 @@ this.statusStrip1.PerformLayout(); this.panel4.ResumeLayout(false); this.panel4.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nudAddDelay)).EndInit(); this.ResumeLayout(false); } @@ -536,13 +595,19 @@ private System.Windows.Forms.RadioButton radTargetErr; private System.Windows.Forms.RadioButton radTargetEmpty; private System.Windows.Forms.RadioButton radTargetErrEmpty; - private System.Windows.Forms.DataGridViewTextBoxColumn book_name; - private System.Windows.Forms.DataGridViewTextBoxColumn book_comp; - private System.Windows.Forms.DataGridViewTextBoxColumn Count; - private System.Windows.Forms.DataGridViewTextBoxColumn dvc_remark; public System.Windows.Forms.CheckBox chkRetryErrData; public System.Windows.Forms.GroupBox groupBox1; public System.Windows.Forms.CheckBox chkShowBrowser; private System.Windows.Forms.ToolStripStatusLabel lbSite; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; + private System.Windows.Forms.ToolStripMenuItem 유니코드문자로붙여넝ㅎ기ToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem 일반문자로붙여넣기ToolStripMenuItem; + private System.Windows.Forms.DataGridViewTextBoxColumn book_name; + private System.Windows.Forms.DataGridViewTextBoxColumn book_comp; + private System.Windows.Forms.DataGridViewTextBoxColumn Count; + private System.Windows.Forms.DataGridViewTextBoxColumn dvc_remark; + private System.Windows.Forms.DataGridViewTextBoxColumn dvc_resulthtml; + private System.Windows.Forms.NumericUpDown nudAddDelay; + private System.Windows.Forms.Label label1; } } \ No newline at end of file diff --git a/unimarc/unimarc/마크/Check_copyWD.cs b/unimarc/unimarc/마크/Check_copyWD.cs index b409359..00422f6 100644 --- a/unimarc/unimarc/마크/Check_copyWD.cs +++ b/unimarc/unimarc/마크/Check_copyWD.cs @@ -12,6 +12,7 @@ using UniMarc.SearchModel; using System.Linq; using UniMarc; using System.Globalization; +using System.Threading.Tasks; namespace WindowsFormsApp1.Mac { @@ -39,6 +40,9 @@ namespace WindowsFormsApp1.Mac main = _main; db.DBcon(); + // 도서관별 지연시간 설정 초기화 + _delaySettings = new Helper_LibraryDelaySettings(); + var idx = 1; //도서검색(크롤링) _searchService = new BookSearchService(); @@ -450,6 +454,7 @@ namespace WindowsFormsApp1.Mac private readonly BookSearchService _searchService; private bool _isSearching = false; + private readonly Helper_LibraryDelaySettings _delaySettings; private async void InitializeChromeDriver() { @@ -612,7 +617,10 @@ namespace WindowsFormsApp1.Mac int cnt_ok = 0; int cnt_ng = 0; int cnt_er = 0; + int addDelay = (int)nudAddDelay.Value * 1000; //추가 지연시간 : 1개의 레코드 처리 후 추가 지연한다. + // 현재 지연시간을 도서관별로 저장 + SaveLibraryDelaySettings(searcher.SiteName, (int)nudAddDelay.Value); RetrySearch: foreach (DataGridViewRow drow in this.dv1.Rows) @@ -620,6 +628,10 @@ namespace WindowsFormsApp1.Mac if (this._isSearching == false) { drow.Cells["dvc_remark"].Value = "Cancel"; + if (dv1.Columns.Contains("dvc_resulthtml")) + { + drow.Cells["dvc_resulthtml"].Value = ""; + } break; } @@ -630,6 +642,10 @@ namespace WindowsFormsApp1.Mac { cnt_skip += 1; drow.Cells["dvc_remark"].Value = "No Title"; + if (dv1.Columns.Contains("dvc_resulthtml")) + { + drow.Cells["dvc_resulthtml"].Value = ""; + } drow.DefaultCellStyle.BackColor = Color.HotPink; drow.DefaultCellStyle.ForeColor = Color.Blue; continue; @@ -660,6 +676,13 @@ namespace WindowsFormsApp1.Mac { drow.Cells["dvc_remark"].Value = rlt.ErrorMessage; drow.Cells["Count"].Value = $"{rlt.BookCount}"; + + // ResultHtml을 dvc_resulthtml 컬럼에 저장 + if (dv1.Columns.Contains("dvc_resulthtml")) + { + drow.Cells["dvc_resulthtml"].Value = rlt.Resulthtml ?? ""; + } + if (rlt.BookCount == 0) { cnt_ng += 1; @@ -677,9 +700,18 @@ namespace WindowsFormsApp1.Mac { cnt_er += 1; drow.Cells["dvc_remark"].Value = $"Error|{rlt.ErrorMessage}"; + + // 오류시에도 ResultHtml이 있으면 저장 (디버깅용) + if (dv1.Columns.Contains("dvc_resulthtml")) + { + drow.Cells["dvc_resulthtml"].Value = rlt.Resulthtml ?? ""; + } + drow.DefaultCellStyle.BackColor = Color.HotPink; drow.DefaultCellStyle.ForeColor = Color.Blue; } + if(addDelay > 0) + await Task.Delay(addDelay); } if (_isSearching == true && retry == false && chkRetryErrData.Checked) @@ -861,6 +893,12 @@ namespace WindowsFormsApp1.Mac dv1.Rows[a].DefaultCellStyle.BackColor = Color.White; dv1.Rows[a].Cells["Count"].Value = ""; dv1.Rows[a].Cells["dvc_remark"].Value = ""; + + // dvc_resulthtml 컬럼도 초기화 + if (dv1.Columns.Contains("dvc_resulthtml")) + { + dv1.Rows[a].Cells["dvc_resulthtml"].Value = ""; + } } } @@ -912,6 +950,67 @@ namespace WindowsFormsApp1.Mac } chkShowBrowser.Enabled = !searcher.HttpApiMode; this.lbSite.Text = $"[{searcher.AreaCode}] {searcher.SiteUrl}"; + + // 저장된 지연시간 불러오기 + LoadLibraryDelaySettings(searcher.SiteName); + } + + /// + /// 선택된 도서관의 저장된 지연시간을 불러와서 UI에 설정 + /// + /// 도서관 이름 + private void LoadLibraryDelaySettings(string libraryName) + { + try + { + var savedDelay = _delaySettings.GetDelay(libraryName); + if (savedDelay > 0) + { + nudAddDelay.Value = savedDelay; + Console.WriteLine($"도서관 지연시간 불러옴: {libraryName} = {savedDelay}초"); + } + else + { + // 저장된 설정이 없으면 기본값 유지 + Console.WriteLine($"도서관 지연시간 설정 없음, 기본값 사용: {libraryName}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"도서관 지연시간 불러오기 오류: {ex.Message}"); + } + } + + /// + /// 검색 시작시 현재 지연시간을 저장 + /// + /// 도서관 이름 + /// 지연시간(초) + private void SaveLibraryDelaySettings(string libraryName, int delaySeconds) + { + try + { + _delaySettings.SetDelay(libraryName, delaySeconds); + Console.WriteLine($"도서관 지연시간 저장됨: {libraryName} = {delaySeconds}초"); + } + catch (Exception ex) + { + Console.WriteLine($"도서관 지연시간 저장 오류: {ex.Message}"); + } + } + + private void 유니코드문자로붙여넝ㅎ기ToolStripMenuItem_Click(object sender, EventArgs e) + { + Skill_Grid sg = new Skill_Grid(); + var newkey = new KeyEventArgs(Keys.Control | Keys.V | Keys.Alt ); + sg.Excel_to_DataGridView(this.dv1, newkey); + } + + private void 일반문자로붙여넣기ToolStripMenuItem_Click(object sender, EventArgs e) + { + Skill_Grid sg = new Skill_Grid(); + var newkey = new KeyEventArgs(Keys.Control | Keys.V); + sg.Excel_to_DataGridView(this.dv1, newkey); } } } diff --git a/unimarc/unimarc/마크/Check_copyWD.resx b/unimarc/unimarc/마크/Check_copyWD.resx index 9fc1aef..59145b0 100644 --- a/unimarc/unimarc/마크/Check_copyWD.resx +++ b/unimarc/unimarc/마크/Check_copyWD.resx @@ -129,8 +129,11 @@ True - - 17, 17 + + True + + + 134, 17 17, 17