 946957bab2
			
		
	
	946957bab2
	
	
	
		
			
			- 새로운 도서관 검색기 15개 추가 (HTTP/Selenium 방식) * HTTP 방식: 순천시립, 목포시립, 광산구, 여수시립 * Selenium 방식: 광주시립, 고흥군립, 북구통합, 전북교육청, 안산시립 등 - 도서관 검색기 작성 가이드를 CLAUDE.md에 추가 - ILibrarySearcher 인터페이스에 HttpApiMode 속성 추가 - 기존 검색기들 리팩토링 및 통합 (NamguLibrarySearcher 등) - Check_copyWD.cs에 모든 새로운 도서관 등록 완료 - 설정 관리 시스템 개선 (UserSetting 클래스 추가) 총 200개 이상의 도서관 지원으로 복본조사 범위 대폭 확대 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			181 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Net.Http;
 | |
| using System.Threading.Tasks;
 | |
| using System.Text.RegularExpressions;
 | |
| using System.Web;
 | |
| using UniMarc.SearchModel;
 | |
| using System.Text;
 | |
| 
 | |
| namespace BokBonCheck
 | |
| {
 | |
|     public class GwangsanLibSearcher : ILibrarySearcher
 | |
|     {
 | |
|         protected string AreaCode { get; set; } = string.Empty;
 | |
|         public string SiteName { get; protected set; }
 | |
|         public string SiteUrl => "https://lib.gwangsan.go.kr/main/bookSearch/advanced";
 | |
|         public bool HttpApiMode { get; set; } = true;
 | |
| 
 | |
|         public int No { get; set; }
 | |
| 
 | |
|         private static readonly HttpClient _httpClient = new HttpClient();
 | |
| 
 | |
|         public GwangsanLibSearcher(int no, string areaCode, string areaName)
 | |
|         {
 | |
|             this.No = no;
 | |
|             this.AreaCode = areaCode;
 | |
|             this.SiteName = $"광주광산구({areaName})";
 | |
|         }
 | |
| 
 | |
|         public async Task StartDriver(bool showdriver = false)
 | |
|         {
 | |
|             // HTTP 클라이언트 사용으로 별도 드라이버 불필요
 | |
|             await Task.CompletedTask;
 | |
|         }
 | |
| 
 | |
|         public void StopDriver()
 | |
|         {
 | |
|             // HTTP 클라이언트 사용으로 별도 정리 불필요
 | |
|         }
 | |
| 
 | |
|         public async Task<BookSearchResult> SearchAsync(string searchTerm)
 | |
|         {
 | |
|             var result = new BookSearchResult
 | |
|             {
 | |
|                 SiteName = SiteName,
 | |
|                 SearchTerm = searchTerm,
 | |
|                 SearchTime = DateTime.Now
 | |
|             };
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 // 검색어 URL 인코딩
 | |
|                 var encodedSearchTerm = HttpUtility.UrlEncode(searchTerm, Encoding.UTF8);
 | |
|                 
 | |
|                 // 상세검색 URL 구성 (도서명만 검색)
 | |
|                 var searchUrl = $"{SiteUrl}?query={encodedSearchTerm}&queryPublisher=&queryAuthor=&queryPubYearFr=&queryPubYearTo=";
 | |
|                 
 | |
|                 // 도서관 코드가 있으면 추가
 | |
|                 if (!string.IsNullOrEmpty(AreaCode))
 | |
|                 {
 | |
|                     searchUrl += $"&libCodeArr={AreaCode}";
 | |
|                 }
 | |
| 
 | |
|                 Console.WriteLine($"광주광산구 검색 URL: {searchUrl}");
 | |
| 
 | |
|                 // HTTP GET 요청 실행
 | |
|                 var response = await _httpClient.GetAsync(searchUrl);
 | |
|                 response.EnsureSuccessStatusCode();
 | |
| 
 | |
|                 var htmlContent = await response.Content.ReadAsStringAsync();
 | |
| 
 | |
|                 // 검색 결과 수 추출
 | |
|                 var resultCount = ExtractBookCount(htmlContent, out string errorMessage);
 | |
|                 
 | |
|                 if (resultCount == -1)
 | |
|                 {
 | |
|                     result.BookCount = 0;
 | |
|                     result.IsSuccess = false;
 | |
|                     result.ErrorMessage = errorMessage;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     result.BookCount = resultCount;
 | |
|                     result.IsSuccess = true;
 | |
|                     result.ErrorMessage = $"검색성공({resultCount}권)";
 | |
|                 }
 | |
| 
 | |
|             }
 | |
|             catch (Exception ex)
 | |
|             {
 | |
|                 result.IsSuccess = false;
 | |
|                 result.ErrorMessage = $"검색 오류: {ex.Message}";
 | |
|                 result.BookCount = 0;
 | |
|                 Console.WriteLine($"광주광산구 검색 오류: {ex.Message}");
 | |
|             }
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         private int ExtractBookCount(string htmlContent, out string errorMessage)
 | |
|         {
 | |
|             errorMessage = string.Empty;
 | |
|             
 | |
|             try
 | |
|             {
 | |
|                 // 검색 결과가 없는 경우 확인
 | |
|                 if (htmlContent.Contains("검색결과가 없습니다") ||
 | |
|                     htmlContent.Contains("검색된 자료가 없습니다") ||
 | |
|                     htmlContent.Contains("자료가 없습니다"))
 | |
|                 {
 | |
|                     errorMessage = "검색결과없음";
 | |
|                     return 0;
 | |
|                 }
 | |
| 
 | |
|                 // HTML에서 "전체 <strong>N</strong> 건" 패턴 찾기
 | |
|                 var patterns = new[]
 | |
|                 {
 | |
|                     @"전체\s*<strong>\s*(\d+)\s*</strong>\s*건",
 | |
|                     @"전체\s+(\d+)\s+건",
 | |
|                     @"총\s*(\d+)\s*권",
 | |
|                     @"총\s*(\d+)\s*건"
 | |
|                 };
 | |
| 
 | |
|                 foreach (var pattern in patterns)
 | |
|                 {
 | |
|                     var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase);
 | |
|                     if (match.Success)
 | |
|                     {
 | |
|                         if (int.TryParse(match.Groups[1].Value, out int count))
 | |
|                         {
 | |
|                             if (count == 0)
 | |
|                             {
 | |
|                                 errorMessage = "검색결과없음";
 | |
|                                 return 0;
 | |
|                             }
 | |
|                             errorMessage = $"검색성공({count}권)";
 | |
|                             return count;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 // 더 자세한 패턴으로 시도 (줄바꿈 포함)
 | |
|                 var multilinePatterns = new[]
 | |
|                 {
 | |
|                     @"전체\s*<strong>\s*\r?\n?\s*(\d+)\s*\r?\n?\s*</strong>\s*건",
 | |
|                     @"<strong>\s*\r?\n?\s*(\d+)\s*\r?\n?\s*</strong>\s*건"
 | |
|                 };
 | |
| 
 | |
|                 foreach (var pattern in multilinePatterns)
 | |
|                 {
 | |
|                     var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
 | |
|                     if (match.Success)
 | |
|                     {
 | |
|                         if (int.TryParse(match.Groups[1].Value, out int count))
 | |
|                         {
 | |
|                             if (count == 0)
 | |
|                             {
 | |
|                                 errorMessage = "검색결과없음";
 | |
|                                 return 0;
 | |
|                             }
 | |
|                             errorMessage = $"검색성공({count}권)";
 | |
|                             return count;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                     
 | |
|                 errorMessage = "검색결과 패턴을 찾을 수 없음";
 | |
|                 return -1;
 | |
|             }
 | |
|             catch (Exception ex)
 | |
|             {
 | |
|                 errorMessage = $"결과 분석 오류: {ex.Message}";
 | |
|                 return -1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public Task WaitForPageChange(OpenQA.Selenium.Support.UI.WebDriverWait wait)
 | |
|         {
 | |
|             throw new NotImplementedException();
 | |
|         }
 | |
|     }
 | |
| } |