using System; using System.Threading.Tasks; using System.Text.RegularExpressions; using System.Net.Http; using System.Collections.Generic; // Added missing import using OpenQA.Selenium.Support.UI; // Added missing import namespace BokBonCheck { public class IksanLibSearcher : ILibrarySearcher { public string AreaCode { get; set; } = string.Empty; public string SiteName { get; protected set; } public string SiteUrl => "https://lib.iksan.go.kr/main/site/search/bookSearch.do"; public bool HttpApiMode { get; set; } = true; public int No { get; set; } private static readonly HttpClient _httpClient = new HttpClient() { DefaultRequestHeaders = { { "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } } }; public IksanLibSearcher(int no, string areaCode, string areaName) { this.No = no; this.AreaCode = areaCode; this.SiteName = $"익산시립({areaName})"; } public void StopDriver() { // HTTP 클라이언트 사용으로 별도 정리 불필요 } public async Task StartDriver(bool showdriver = false) { // HTTP 클라이언트 사용으로 별도 드라이버 불필요 await Task.CompletedTask; } public async Task SearchAsync(string searchTerm) { var result = new BookSearchResult { SiteName = SiteName, SearchTerm = searchTerm, SearchTime = DateTime.Now }; try { // 검색어 URL 인코딩 var encodedSearchTerm = System.Web.HttpUtility.UrlEncode(searchTerm, System.Text.Encoding.UTF8); // 검색 URL 구성 - GET 방식으로 URL 파라미터 전송 var searchUrl = $"{SiteUrl}?cmd_name=bookandnonbooksearch&search_type=detail&detail=OK&use_facet=N&all_lib=N&search_item=search_title&search_txt=&search_title={encodedSearchTerm}"; // 도서관 코드가 있으면 추가 if (!string.IsNullOrEmpty(AreaCode)) { searchUrl += $"&manage_code={AreaCode}"; } Console.WriteLine($"익산시통합도서관 검색 요청: {searchTerm}, 도서관코드: {AreaCode}"); Console.WriteLine($"검색 URL: {searchUrl}"); // HTTP GET 요청 실행 (추가 헤더 포함) using (var request = new HttpRequestMessage(HttpMethod.Get, searchUrl)) { // 브라우저와 유사한 헤더 추가 request.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); request.Headers.Add("Accept-Language", "ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3"); request.Headers.Add("Accept-Encoding", "gzip, deflate, br"); request.Headers.Add("Connection", "keep-alive"); request.Headers.Add("Upgrade-Insecure-Requests", "1"); var response = await _httpClient.SendAsync(request); response.EnsureSuccessStatusCode(); var htmlContent = await response.Content.ReadAsStringAsync(); // 검색 결과 수 추출 var resultCount = ExtractBookCount(htmlContent, out string errorMessage); if (resultCount == -1) { result.BookCount = 0; result.IsSuccess = false; result.ErrorMessage = errorMessage; } else { result.BookCount = resultCount; result.IsSuccess = true; result.ErrorMessage = $"검색성공({resultCount}권)"; } } } catch (Exception ex) { result.IsSuccess = false; result.ErrorMessage = ex.Message; result.BookCount = 0; } return result; } private int ExtractBookCount(string htmlContent, out string errmessage) { errmessage = string.Empty; try { // 실제 HTML 구조에 맞는 패턴으로 수정 var htmlPatterns = new[] { // "상세검색결과 3개" 패턴 (실제 구조) @"상세검색결과\s*]*>\s*(\d+)\s*\s*개", // "전체 1개가 검색되었습니다" 패턴 (이전 패턴) @"전체\s*]*class=""word""[^>]*>\s*(\d+)\s*\s*개가\s*검색되었습니다", // "전체 0개가 검색되었습니다" 패턴 (0인 경우) @"전체\s*]*class=""word""[^>]*>\s*0\s*\s*개가\s*검색되었습니다", // 일반적인 "전체 X개가 검색되었습니다" 패턴 @"전체\s*(\d+)\s*개가\s*검색되었습니다", // 숫자만 찾는 패턴 (마지막 수단) @"(\d+)\s*개가\s*검색되었습니다" }; foreach (var pattern in htmlPatterns) { var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase); if (match.Success) { // 0인 경우를 먼저 확인 if (pattern.Contains(@"\s*0\s*")) { errmessage = "검색결과없음"; return 0; } // 숫자 추출 if (int.TryParse(match.Groups[1].Value, out int count)) { if (count == 0) { errmessage = "검색결과없음"; return 0; } errmessage = $"검색성공({count}권)"; return count; } } } // 디버깅을 위해 HTML 내용 일부 출력 Console.WriteLine($"HTML 내용 일부: {htmlContent.Substring(0, Math.Min(1000, htmlContent.Length))}"); errmessage = "결과수량을찾을수없음"; return -1; } catch (Exception ex) { errmessage = ex.Message; return -1; } } // 페이지 변경을 감지하는 메서드 (HTTP 방식에서는 불필요) public async Task WaitForPageChange(WebDriverWait wait) { // HTTP 방식에서는 즉시 응답이 오므로 대기 불필요 await Task.CompletedTask; } } }