feat: 도서관 검색 시스템 대폭 확장 및 크롤링 가이드 추가

- 새로운 도서관 검색기 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>
This commit is contained in:
Arin(asus)
2025-08-14 01:21:33 +09:00
parent f3715253a8
commit 946957bab2
30 changed files with 4136 additions and 380 deletions

View File

@@ -41,4 +41,116 @@
F:\(VHD) Program Files\Microsoft Visual Studio\2022\MSBuild\Current\Bin\msbuild.exe
## 프로젝트 파일명
UniMarc.csproj
UniMarc.csproj
## 도서관 검색기(크롤링) 클래스 작성 가이드
### 개요
도서관 웹사이트에서 도서 검색 결과를 크롤링하는 클래스들을 구현합니다.
각 도서관 사이트의 특성에 따라 HTTP 방식 또는 Selenium 방식을 선택합니다.
### 방식 선택 기준
- **HTTP 방식**: URL이 변경되는 사이트 (GET 파라미터로 검색)
- **Selenium 방식**: URL이 변경되지 않는 사이트 (POST 폼 제출)
### 1. HTTP 방식 (GET 요청)
**참고 파일**: `SuncheonLibSearcher.cs`
**특징**:
- HttpClient 사용
- URL 파라미터로 검색
- 빠른 실행 속도
- HttpApiMode = true
**주요 구현 포인트**:
```csharp
// URL 구성
var searchUrl = $"{SiteUrl}?search={encodedSearchTerm}&libCode={AreaCode}";
// HTTP 헤더 추가 (500 에러 방지)
request.Headers.Add("User-Agent", "Mozilla/5.0...");
request.Headers.Add("Accept", "text/html,application/xhtml+xml...");
// HTML에서 결과 수 정규식 추출
var patterns = new[] {
@"총\s*<strong[^>]*class=""cred""[^>]*>(\d+)</strong>\s*건"
};
```
### 2. Selenium 방식 (크롤링)
**참고 파일**: `GwangjuCityLibSearcher.cs`
**특징**:
- Selenium WebDriver 사용
- 폼 제출 방식
- 복잡한 UI 상호작용 가능
- HttpApiMode = false
**주요 구현 포인트**:
```csharp
// 도서관 선택 (드롭다운)
var libSelect = wait.Until(d => d.FindElement(By.CssSelector("select[name='libCode']")));
var selectElement = new SelectElement(libSelect);
selectElement.SelectByValue(AreaCode);
// 검색어 입력
var searchInput = wait.Until(d => d.FindElement(By.Id("bookSearchQuery")));
searchInput.Clear();
searchInput.SendKeys(searchTerm);
// 검색 버튼 클릭
var searchButton = wait.Until(d => d.FindElement(By.CssSelector("button.bookSearchBtn")));
searchButton.Click();
// 페이지 로딩 대기
await WaitForPageChange(wait);
```
### 3. 공통 인터페이스 구현
모든 검색기는 `ILibrarySearcher` 인터페이스를 구현해야 합니다:
```csharp
public interface ILibrarySearcher
{
Task<BookSearchResult> SearchAsync(string searchTerm);
Task StartDriver(bool showdriver = false);
void StopDriver();
string SiteName { get; }
string SiteUrl { get; }
bool HttpApiMode { get; set; }
int No { get; set; }
}
```
### 4. Check_copyWD.cs에 등록
새로운 검색기를 만든 후 반드시 Check_copyWD.cs의 생성자에 추가:
```csharp
// 예시: 완도군립도서관
idx = 1200;
_searchService.AddSearcher(new WandoLibSearcher(idx++, "MA", "완도군립도서관"));
_searchService.AddSearcher(new WandoLibSearcher(idx++, "MB", "노화공공도서관"));
```
### 5. 결과 추출 패턴
HTML에서 검색 결과 수를 추출하는 일반적인 패턴들:
```csharp
var patterns = new[] {
@"총\s*<strong[^>]*>(\d+)</strong>\s*건",
@"검색결과\s*총\s*(\d+)\s*건",
@"<span[^>]*class=""heighlight""[^>]*>(\d+)</span>",
@"총\s*(\d+)\s*권\(개\)"
};
```
### 6. 에러 처리
- 검색 결과 없음: `return 0`
- 추출 실패: `return -1`
- 예외 발생: BookSearchResult의 ErrorMessage에 설정
### 현재 구현된 검색기 목록
1. HTTP 방식: SuncheonLibSearcher, MokpoLibSearcher, GwangsanLibSearcher, YeosuLibSearcher
2. Selenium 방식: GwangjuCityLibSearcher, GoheungLibSearcher, BukguLibSearcher 계열, JeonbukEduLibSearcher
각 방식의 장단점을 고려하여 사이트 특성에 맞는 방식을 선택하여 구현하세요.

View File

@@ -4,12 +4,14 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using UniMarc.Properties;
namespace UniMarc
{
public static class PUB
{
public static arUtil.Log log;
public static UserSetting setting;
public static void Init()
{
@@ -20,6 +22,9 @@ namespace UniMarc
PUB.log.SubDirectory = logsubdir;
PUB.log.FileNameFormat = "{yyMMdd}";
#endregion
setting = new UserSetting();
setting.Load();
}
}

View File

@@ -0,0 +1,418 @@
using System;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System.Text.RegularExpressions;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using UniMarc.;
using OpenQA.Selenium.Chromium;
using UniMarc.SearchModel;
using System.Runtime.CompilerServices;
using AR;
using OpenQA.Selenium.Interactions;
namespace BokBonCheck
{
public class AnsanLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://lib.ansan.go.kr/DetailSearch";
public bool HttpApiMode { get; set; } = false;
public int No { get; set; }
private ChromiumDriver _driver;
public AnsanLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"안산시립({areaName})";
}
public void StopDriver()
{
if (_driver != null)
{
_driver.Quit();
_driver.Dispose();
_driver = null;
}
}
public async Task StartDriver(bool showdriver = false)
{
if (_driver == null)
{
try
{
if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download();
_driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver);
Console.WriteLine("AnsanLibSearcher Driver 초기화 완료");
}
catch (Exception ex)
{
Console.WriteLine($"AnsanLibSearcher Driver 초기화 실패: {ex.Message}");
throw new InvalidOperationException($"AnsanLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex);
}
}
}
virtual protected bool SelectLibrary(WebDriverWait wait)
{
try
{
// 1. 먼저 모든 전체선택 버튼을 찾아서 클릭하여 기존 선택을 모두 해제
Console.WriteLine("기존 선택 해제 중...");
var allSelectButtons = wait.Until(d => d.FindElements(By.CssSelector("div.library span.btnAllSelect")));
foreach (var button in allSelectButtons)
{
try
{
// 전체선택 버튼이 "전체선택"인지 "전체해제"인지 확인하고 필요시 클릭
var buttonText = button.Text.Trim();
Console.WriteLine($"전체선택 버튼 텍스트: {buttonText}");
// 전체선택 버튼을 두 번 클릭하여 모든 선택 해제 (선택 -> 해제)
SafeClick(button);
Thread.Sleep(300);
SafeClick(button);
Thread.Sleep(300);
Console.WriteLine("전체선택 버튼 클릭 완료 (해제)");
}
catch (Exception ex)
{
Console.WriteLine($"전체선택 버튼 클릭 실패: {ex.Message}");
}
}
// 2. 지정된 도서관만 선택
if (!string.IsNullOrEmpty(AreaCode))
{
Console.WriteLine($"특정 도서관 선택 중: {AreaCode}");
// div.library 안에서 해당 value를 가진 체크박스 찾기
var libraryCheckbox = wait.Until(d => d.FindElement(By.CssSelector($"div.library input[type='checkbox'][value='{AreaCode}']")));
// 체크박스 선택
SafeClick(libraryCheckbox);
Thread.Sleep(500);
// 선택 확인
if (libraryCheckbox.Selected)
{
Console.WriteLine($"{AreaCode} 도서관 선택 완료");
}
else
{
Console.WriteLine($"{AreaCode} 도서관 선택 실패, 다시 시도");
SafeClick(libraryCheckbox);
Thread.Sleep(300);
}
}
else
{
Console.WriteLine("전체 도서관으로 검색 - 모든 도서관 선택");
// AreaCode가 없으면 첫 번째 전체선택 버튼만 클릭하여 모든 도서관 선택
if (allSelectButtons.Count > 0)
{
SafeClick(allSelectButtons[0]);
Thread.Sleep(300);
Console.WriteLine("모든 도서관 선택 완료");
}
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"도서관 선택 실패: {ex.Message}");
return false;
}
}
protected void SafeClick(IWebElement element)
{
// 안정적인 클릭을 위한 여러 방법 시도
try
{
// 1. 요소가 보이도록 스크롤 후 일반 클릭
((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].scrollIntoView(true);", element);
Thread.Sleep(300);
element.Click();
}
catch
{
try
{
// 2. JavaScript로 클릭 시도
((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].click();", element);
}
catch
{
try
{
// 3. Actions 클래스 사용
var actions = new Actions(_driver);
actions.MoveToElement(element).Click().Perform();
}
catch
{
try
{
// 4. 체크박스의 경우 직접 체크 상태 변경
if (element.TagName.ToLower() == "input" && element.GetAttribute("type") == "checkbox")
{
((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].checked = !arguments[0].checked;", element);
}
else
{
// 일반 요소는 강제 클릭
((IJavaScriptExecutor)_driver).ExecuteScript("arguments[0].dispatchEvent(new Event('click'));", element);
}
}
catch (Exception ex)
{
Console.WriteLine($"모든 클릭 방법 실패: {ex.Message}");
}
}
}
}
}
public async Task<BookSearchResult> SearchAsync(string searchTerm)
{
var result = new BookSearchResult
{
SiteName = SiteName,
SearchTerm = searchTerm,
SearchTime = DateTime.Now
};
try
{
// 드라이버가 없으면 자동으로 시작
if (_driver == null)
{
await StartDriver();
}
var cururl = _driver.Url;
if (cururl.Equals(SiteUrl) == false)
_driver.Navigate().GoToUrl(SiteUrl);
// 페이지 로딩 대기
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15));
// 도서관 선택
if (SelectLibrary(wait) == false)
{
result.ErrorMessage = "도서관선택실패";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색어 입력
try
{
var searchInput = wait.Until(d => d.FindElement(By.Id("advTitle")));
searchInput.Clear();
searchInput.SendKeys(searchTerm);
}
catch (Exception ex)
{
result.ErrorMessage = $"검색어입력실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색 버튼 클릭
try
{
var searchButton = wait.Until(d => d.FindElement(By.CssSelector("a.btTxt.bg.primary")));
searchButton.Click();
}
catch (Exception ex)
{
result.ErrorMessage = $"검색버튼클릭실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 페이지 변경을 감지하는 메서드
await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15)));
// 검색 결과 수 추출
var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = ermsg;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = ermsg;
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = ex.Message;
result.BookCount = 0;
}
return result;
}
private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage)
{
errmessage = string.Empty;
try
{
// 1. 검색결과가 없는 경우 확인
try
{
var noResultElements = driver.FindElements(By.XPath("//*[contains(text(), '검색결과가 없습니다') or contains(text(), '검색된 자료가 없습니다')]"));
if (noResultElements.Count > 0)
{
errmessage = "검색결과없음";
return 0;
}
}
catch
{
// 검색결과가 있는 경우로 진행
}
// 2. 검색 결과 영역에서 직접 추출 시도
try
{
// 결과 요약 정보가 있는 영역 찾기
var resultInfoElements = driver.FindElements(By.CssSelector(".resultInfo, .result-info, .search-result, .totalResult"));
foreach (var element in resultInfoElements)
{
var text = element.Text;
var match = Regex.Match(text, @"총\s*(\d+)\s*건", RegexOptions.IgnoreCase);
if (match.Success && int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
catch { }
// 3. 페이지 소스에서 정규식으로 추출 시도
var pageSource = driver.PageSource;
// 검색 결과가 없다는 메시지 확인
if (pageSource.Contains("검색결과가 없습니다") ||
pageSource.Contains("검색된 자료가 없습니다") ||
pageSource.Contains("자료가 없습니다") ||
pageSource.Contains("총 0 건") ||
pageSource.Contains("총 0건"))
{
errmessage = "검색결과없음";
return 0;
}
// HTML에서 다양한 패턴 찾기 (안산시 도서관 특화)
var htmlPatterns = new[]
{
@"총\s*(\d+)\s*건",
@"<em[^>]*>총\s*(\d+)\s*건\s*</em>",
@"검색결과\s*:\s*총\s*(\d+)\s*건",
@"전체\s*(\d+)\s*건",
@"검색결과\s*총\s*(\d+)\s*건",
@"검색\s*결과\s*\(\s*총\s*(\d+)\s*건\s*\)",
@"총\s*<[^>]*>(\d+)</[^>]*>\s*건"
};
foreach (var pattern in htmlPatterns)
{
var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
errmessage = "결과수량을찾을수없음";
return -1;
}
catch (Exception ex)
{
errmessage = ex.Message;
return -1;
}
}
// 페이지 변경을 감지하는 메서드
public async Task WaitForPageChange(WebDriverWait wait)
{
try
{
await Task.Delay(500);
// 페이지 로딩 상태 확인
wait.Until(d =>
{
var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState");
return readyState.Equals("complete");
});
// 검색 결과 페이지가 로드될 때까지 대기
wait.Until(d =>
{
try
{
var pageSource = d.PageSource;
// 검색 결과나 관련 요소가 나타나면 로드 완료
return pageSource.Contains("검색결과") ||
pageSource.Contains("총") ||
pageSource.Contains("검색결과가 없습니다") ||
pageSource.Contains("건");
}
catch
{
return false;
}
});
}
catch (Exception ex)
{
// 모든 감지 방법이 실패하면 최소한의 대기 시간 적용
await Task.Delay(3000);
Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,438 @@
using AR;
using AR.Dialog;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Chromium;
using OpenQA.Selenium.Support.UI;
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using UniMarc.SearchModel;
using UniMarc.;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
namespace BokBonCheck
{
public class BukguLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl { get; protected set; } = "https://lib.bukgu.gwangju.kr/main/bookSearchSmartlib.do?PID=0301";
public bool HttpApiMode { get; set; } = false;
public int No { get; set; }
private ChromiumDriver _driver;
public BukguLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"광주북구스마트도서관({areaName})";
}
public void StopDriver()
{
if (_driver != null)
{
_driver.Quit();
_driver.Dispose();
_driver = null;
}
}
public async Task StartDriver(bool showdriver = false)
{
if (_driver == null)
{
try
{
if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download();
_driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver);
Console.WriteLine("BukguLibSearcher Driver 초기화 완료");
}
catch (Exception ex)
{
Console.WriteLine($"BukguLibSearcher Driver 초기화 실패: {ex.Message}");
throw new InvalidOperationException($"BukguLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex);
}
}
}
virtual protected bool SelectLibrary(WebDriverWait wait)
{
try
{
// 전체 선택인 경우 - 모든 체크박스 선택
if (string.IsNullOrEmpty(AreaCode) || AreaCode == "ALL")
{
var allCheckboxes1 = wait.Until(d => d.FindElements(By.CssSelector("input[name='libCode']")));
foreach (var checkbox in allCheckboxes1)
{
if (!checkbox.Selected)
{
SafeClick(checkbox);
Thread.Sleep(100);
}
}
Console.WriteLine("전체 도서관 선택됨");
return true;
}
// 특정 도서관 선택인 경우
var targetCheckboxId = $"lib{AreaCode}";
var targetCheckbox = wait.Until(d => d.FindElement(By.Id(targetCheckboxId)));
if (targetCheckbox == null)
{
Console.WriteLine($"도서관 체크박스를 찾을 수 없습니다: {targetCheckboxId}");
return false;
}
// 다른 체크박스들 해제
var allCheckboxes = wait.Until(d => d.FindElements(By.CssSelector("input[name='libCode']")));
foreach (var checkbox in allCheckboxes)
{
if (checkbox.GetAttribute("id") != targetCheckboxId && checkbox.Selected)
{
SafeClick(checkbox);
Thread.Sleep(100);
}
}
// 대상 체크박스 선택
if (!targetCheckbox.Selected)
{
SafeClick(targetCheckbox);
Thread.Sleep(300);
Console.WriteLine($"{AreaCode} 도서관으로 변경됨");
}
else
{
Console.WriteLine($"{AreaCode} 도서관이 이미 선택되어 있음");
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"도서관 선택 실패: {ex.Message}");
return false;
}
}
protected void SafeClick(IWebElement element)
{
// 안정적인 클릭을 위한 여러 방법 시도
try
{
// 1. JavaScript로 클릭 시도
var driver = ((IWrapsDriver)element).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", element);
}
catch
{
try
{
// 2. 요소가 보이도록 스크롤 후 클릭
var driver = ((IWrapsDriver)element).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", element);
Thread.Sleep(500);
element.Click();
}
catch
{
try
{
// 3. Actions 클래스 사용
var driver = ((IWrapsDriver)element).WrappedDriver;
var actions = new OpenQA.Selenium.Interactions.Actions(driver);
actions.MoveToElement(element).Click().Perform();
}
catch
{
// 4. 마지막 방법: JavaScript로 직접 체크 상태 변경
var driver = ((IWrapsDriver)element).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].checked = !arguments[0].checked;", element);
}
}
}
}
public async Task<BookSearchResult> SearchAsync(string searchTerm)
{
var result = new BookSearchResult
{
SiteName = SiteName,
SearchTerm = searchTerm,
SearchTime = DateTime.Now
};
try
{
// 드라이버가 없으면 자동으로 시작
if (_driver == null)
{
await StartDriver();
}
var cururl = _driver.Url;
if (cururl.Equals(SiteUrl) == false)
_driver.Navigate().GoToUrl(SiteUrl);
// 페이지 로딩 대기
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15));
// 도서관 선택
if (SelectLibrary(wait) == false)
{
result.ErrorMessage = "도서관선택실패";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색어 입력 - 다양한 선택자로 시도
try
{
IWebElement searchInput = null;
try
{
searchInput = wait.Until(d => d.FindElement(By.CssSelector("input[name='searchText']")));
}
catch { }
if (searchInput == null)
{
result.ErrorMessage = "검색창을 찾을 수 없음";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
searchInput.Clear();
searchInput.SendKeys(searchTerm);
}
catch (Exception ex)
{
result.ErrorMessage = $"검색어입력실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색 버튼 클릭 - 다양한 선택자로 시도
try
{
IWebElement searchButton = null;
try
{
searchButton = wait.Until(d => d.FindElement(By.CssSelector("button[type='submit']")));
}
catch { }
if (searchButton == null)
{
result.ErrorMessage = "검색버튼을 찾을 수 없음";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
searchButton.Click();
}
catch (Exception ex)
{
result.ErrorMessage = $"검색버튼클릭실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 페이지 변경을 감지하는 메서드
await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15)));
// 검색 결과 수 추출
var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = ermsg;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = ermsg;
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = ex.Message;
result.BookCount = 0;
}
return result;
}
private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage)
{
errmessage = string.Empty;
try
{
// 1. totalCount 요소에서 직접 추출 시도
try
{
var totalCountElement = driver.FindElement(By.CssSelector("p.totalCount"));
if (totalCountElement != null)
{
// strong 태그에서 직접 숫자 추출 시도
try
{
var strongElement = totalCountElement.FindElement(By.TagName("strong"));
if (strongElement != null)
{
var countText = strongElement.Text.Trim();
if (int.TryParse(countText, out int strongCount))
{
if (strongCount == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({strongCount}권)";
return strongCount;
}
}
}
catch { }
// 전체 텍스트에서 정규식으로 추출
var totalCountText = totalCountElement.Text;
var match = Regex.Match(totalCountText, @"전체\s*(\d+)\s*건", RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"totalCount 요소 검색 중 오류: {ex.Message}");
}
// 2. 페이지 소스에서 정규식으로 추출 시도
var pageSource = driver.PageSource;
// 검색 결과가 없다는 메시지 확인
if (pageSource.Contains("검색결과가 없습니다") ||
pageSource.Contains("검색된 자료가 없습니다") ||
pageSource.Contains("자료가 없습니다") ||
pageSource.Contains("전체 0 건"))
{
errmessage = "검색결과없음";
return 0;
}
// HTML에서 다양한 패턴 찾기
var htmlPatterns = new[]
{
@"전체\s*<strong>\s*(\d+)\s*</strong>\s*건",
@"전체\s+(\d+)\s+건",
@"총\s*(\d+)\s*권",
@"총\s*(\d+)\s*건",
@"검색결과\s*:\s*(\d+)"
};
foreach (var pattern in htmlPatterns)
{
var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
errmessage = "결과수량을찾을수없음";
return -1;
}
catch (Exception ex)
{
errmessage = ex.Message;
return -1;
}
}
// 페이지 변경을 감지하는 메서드
public async Task WaitForPageChange(WebDriverWait wait)
{
try
{
await Task.Delay(500);
// 페이지 로딩 상태 확인
wait.Until(d =>
{
var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState");
return readyState.Equals("complete");
});
// 검색 결과 페이지가 로드될 때까지 대기
wait.Until(d =>
{
try
{
var pageSource = d.PageSource;
// 검색 결과나 관련 요소가 나타나면 로드 완료
return pageSource.Contains("searchTitle") ||
pageSource.Contains("totalCount") ||
pageSource.Contains("검색결과") ||
pageSource.Contains("전체") ||
pageSource.Contains("자료가");
}
catch
{
return false;
}
});
}
catch (Exception ex)
{
// 모든 감지 방법이 실패하면 최소한의 대기 시간 적용
await Task.Delay(3000);
Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,30 @@
using AR;
using AR.Dialog;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Chromium;
using OpenQA.Selenium.Support.UI;
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using UniMarc.SearchModel;
using UniMarc.;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
namespace BokBonCheck
{
public class BukguPublicLibSearcher : BukguLibSearcher
{
public BukguPublicLibSearcher(int no, string areaCode, string areaName) : base(no, areaCode, areaName)
{
this.SiteName = $"광주북구공공도서관({areaName})";
base.SiteUrl = "https://lib.bukgu.gwangju.kr/main/bookSearch.do?PID=0301";
}
}
}

View File

@@ -0,0 +1,30 @@
using AR;
using AR.Dialog;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Chromium;
using OpenQA.Selenium.Support.UI;
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using UniMarc.SearchModel;
using UniMarc.;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
namespace BokBonCheck
{
public class BukguSlibSearcher : BukguLibSearcher
{
public BukguSlibSearcher(int no, string areaCode, string areaName) : base(no, areaCode, areaName)
{
this.SiteName = $"광주북구작은도서관({areaName})";
base.SiteUrl = "https://lib.bukgu.gwangju.kr/main/bookSearchSlib.do?PID=0301";
}
}
}

View File

@@ -18,6 +18,7 @@ namespace BokBonCheck
{
public class DLSSearcher : ILibrarySearcher
{
public bool HttpApiMode { get; set; } = false;
public string SiteName { get; protected set; } = "DLS";
public string SiteUrl => "https://dls1.edunet.net/DLS/bookMng/bookMain";

View File

@@ -0,0 +1,423 @@
using System;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System.Text.RegularExpressions;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using UniMarc.;
using OpenQA.Selenium.Chromium;
using UniMarc.SearchModel;
using System.Runtime.CompilerServices;
using AR;
namespace BokBonCheck
{
public class GoheungLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://www.ghlib.go.kr/BookSearch/detail";
public bool HttpApiMode { get; set; } = false;
public int No { get; set; }
private ChromiumDriver _driver;
public GoheungLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"고흥군립({areaName})";
}
public void StopDriver()
{
if (_driver != null)
{
_driver.Quit();
_driver.Dispose();
_driver = null;
}
}
public async Task StartDriver(bool showdriver = false)
{
if (_driver == null)
{
try
{
if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download();
_driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver);
Console.WriteLine("GoheungLibSearcher Driver 초기화 완료");
}
catch (Exception ex)
{
Console.WriteLine($"GoheungLibSearcher Driver 초기화 실패: {ex.Message}");
throw new InvalidOperationException($"GoheungLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex);
}
}
}
virtual protected bool SelectLibrary(WebDriverWait wait)
{
try
{
// 전체 선택인 경우
if (string.IsNullOrEmpty(AreaCode) || AreaCode == "ALL")
{
var allRadio = wait.Until(d => d.FindElement(By.Id("libALL")));
if (!allRadio.Selected)
{
SafeClick(allRadio);
Thread.Sleep(300);
Console.WriteLine("전체도서관 선택됨");
}
else
{
Console.WriteLine("전체도서관이 이미 선택되어 있음");
}
return true;
}
// 특정 도서관 선택인 경우
var targetRadioId = $"lib{AreaCode}";
var targetRadio = wait.Until(d => d.FindElement(By.Id(targetRadioId)));
if (targetRadio == null)
{
Console.WriteLine($"도서관 라디오 버튼을 찾을 수 없습니다: {targetRadioId}");
return false;
}
// 이미 선택되어 있지 않으면 선택
if (!targetRadio.Selected)
{
SafeClick(targetRadio);
Thread.Sleep(300);
Console.WriteLine($"{AreaCode} 도서관으로 변경됨");
}
else
{
Console.WriteLine($"{AreaCode} 도서관이 이미 선택되어 있음");
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"도서관 선택 실패: {ex.Message}");
return false;
}
}
protected void SafeClick(IWebElement element)
{
// 안정적인 클릭을 위한 여러 방법 시도
try
{
// 1. JavaScript로 클릭 시도
var driver = ((IWrapsDriver)element).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", element);
}
catch
{
try
{
// 2. 요소가 보이도록 스크롤 후 클릭
var driver = ((IWrapsDriver)element).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", element);
Thread.Sleep(500);
element.Click();
}
catch
{
try
{
// 3. Actions 클래스 사용
var driver = ((IWrapsDriver)element).WrappedDriver;
var actions = new OpenQA.Selenium.Interactions.Actions(driver);
actions.MoveToElement(element).Click().Perform();
}
catch
{
// 4. 마지막 방법: JavaScript로 직접 체크
var driver = ((IWrapsDriver)element).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].checked = true;", element);
}
}
}
}
public async Task<BookSearchResult> SearchAsync(string searchTerm)
{
var result = new BookSearchResult
{
SiteName = SiteName,
SearchTerm = searchTerm,
SearchTime = DateTime.Now
};
try
{
// 드라이버가 없으면 자동으로 시작
if (_driver == null)
{
await StartDriver();
}
var cururl = _driver.Url;
if (cururl.Equals(SiteUrl) == false)
_driver.Navigate().GoToUrl(SiteUrl);
// 페이지 로딩 대기
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15));
// 도서관 선택
if (SelectLibrary(wait) == false)
{
result.ErrorMessage = "도서관선택실패";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 도서명 입력
try
{
var titleInput = wait.Until(d => d.FindElement(By.Id("queryTitle")));
titleInput.Clear();
titleInput.SendKeys(searchTerm);
}
catch (Exception ex)
{
result.ErrorMessage = $"도서명입력실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색 버튼 클릭
try
{
var searchButton = wait.Until(d => d.FindElement(By.CssSelector("button[type='submit']")));
searchButton.Click();
}
catch (Exception ex)
{
result.ErrorMessage = $"검색버튼클릭실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 페이지 변경을 감지하는 메서드
await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15)));
// 검색 결과 수 추출
var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = ermsg;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = ermsg;
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = ex.Message;
result.BookCount = 0;
}
return result;
}
private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage)
{
errmessage = string.Empty;
try
{
// 1. 먼저 totalCount 요소에서 직접 추출 시도
try
{
var totalCountElement = driver.FindElement(By.CssSelector("p.totalCount"));
if (totalCountElement != null)
{
var totalCountText = totalCountElement.Text; // 예: "전체 1 건"
var match = Regex.Match(totalCountText, @"전체\s+(\d+)\s+건", RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
// strong 태그에서 직접 숫자 추출 시도
try
{
var strongElement = totalCountElement.FindElement(By.TagName("strong"));
if (strongElement != null && int.TryParse(strongElement.Text, out int strongCount))
{
if(strongCount == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({strongCount}권)";
return strongCount;
}
}
catch { }
}
}
catch (Exception ex)
{
Console.WriteLine($"totalCount 요소 검색 중 오류: {ex.Message}");
}
// 2. 페이지 소스에서 정규식으로 추출 시도
var pageSource = driver.PageSource;
// 검색 결과가 없다는 메시지 확인
if (pageSource.Contains("검색결과가 없습니다") ||
pageSource.Contains("검색된 자료가 없습니다") ||
pageSource.Contains("자료가 없습니다") ||
pageSource.Contains("전체 0 건"))
{
errmessage = "검색결과없음";
return 0;
}
// HTML에서 "전체 <strong>N</strong> 건" 패턴 찾기
var htmlPatterns = new[]
{
@"전체\s*<strong>(\d+)</strong>\s*건",
@"전체\s+(\d+)\s+건",
@"총\s*(\d+)\s*권",
@"총\s*(\d+)\s*건"
};
foreach (var pattern in htmlPatterns)
{
var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
// 3. 결과 테이블이나 리스트가 있는지 확인
try
{
var resultElements = driver.FindElements(By.CssSelector(".bookList, .searchResult, .result-list, table tbody tr"));
if (resultElements.Count > 0)
{
// 테이블 헤더나 빈 행을 제외한 실제 결과 개수 계산
var actualCount = 0;
foreach (var element in resultElements)
{
var text = element.Text?.Trim();
if (!string.IsNullOrEmpty(text) &&
!text.Contains("도서명") &&
!text.Contains("저자") &&
!text.Contains("출판사"))
{
actualCount++;
}
}
if (actualCount > 0)
{
errmessage = $"검색성공({actualCount}권)";
return actualCount;
}
}
}
catch (Exception ex)
{
Console.WriteLine($"결과 요소 검색 중 오류: {ex.Message}");
}
errmessage = "결과수량을찾을수없음";
return -1;
}
catch (Exception ex)
{
errmessage = ex.Message;
return -1;
}
}
// 페이지 변경을 감지하는 메서드
public async Task WaitForPageChange(WebDriverWait wait)
{
try
{
await Task.Delay(500);
// 페이지 로딩 상태 확인
wait.Until(d =>
{
var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState");
return readyState.Equals("complete");
});
// 검색 결과 페이지가 로드될 때까지 대기
wait.Until(d =>
{
try
{
var pageSource = d.PageSource;
// 검색 결과나 "검색결과가 없습니다" 메시지가 나타나면 로드 완료
return pageSource.Contains("검색결과") ||
pageSource.Contains("총") ||
pageSource.Contains("자료가") ||
pageSource.Contains("bookList") ||
pageSource.Contains("searchResult");
}
catch
{
return false;
}
});
}
catch (Exception ex)
{
// 모든 감지 방법이 실패하면 최소한의 대기 시간 적용
await Task.Delay(3000);
Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,346 @@
using System;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System.Text.RegularExpressions;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using UniMarc.;
using OpenQA.Selenium.Chromium;
using UniMarc.SearchModel;
using System.Runtime.CompilerServices;
using AR;
namespace BokBonCheck
{
public class GwangjuCityLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://citylib.gwangju.go.kr/main/bookSearch";
public bool HttpApiMode { get; set; } = false;
public int No { get; set; }
private ChromiumDriver _driver;
public GwangjuCityLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"광주시립({areaName})";
}
public void StopDriver()
{
if (_driver != null)
{
_driver.Quit();
_driver.Dispose();
_driver = null;
}
}
public async Task StartDriver(bool showdriver = false)
{
if (_driver == null)
{
try
{
if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download();
_driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver);
Console.WriteLine("GwangjuCityLibSearcher Driver 초기화 완료");
}
catch (Exception ex)
{
Console.WriteLine($"GwangjuCityLibSearcher Driver 초기화 실패: {ex.Message}");
throw new InvalidOperationException($"GwangjuCityLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex);
}
}
}
virtual protected bool SelectLibrary(WebDriverWait wait)
{
try
{
// 도서관 선택
if (!string.IsNullOrEmpty(AreaCode))
{
var libSelect = wait.Until(d => d.FindElement(By.CssSelector("select[name='libCode']")));
var selectElement = new SelectElement(libSelect);
selectElement.SelectByValue(AreaCode);
Thread.Sleep(300);
Console.WriteLine($"{AreaCode} 도서관으로 선택됨");
}
else
{
Console.WriteLine("전체 도서관으로 검색");
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"도서관 선택 실패: {ex.Message}");
return false;
}
}
public async Task<BookSearchResult> SearchAsync(string searchTerm)
{
var result = new BookSearchResult
{
SiteName = SiteName,
SearchTerm = searchTerm,
SearchTime = DateTime.Now
};
try
{
// 드라이버가 없으면 자동으로 시작
if (_driver == null)
{
await StartDriver();
}
var cururl = _driver.Url;
if (cururl.Equals(SiteUrl) == false)
_driver.Navigate().GoToUrl(SiteUrl);
// 페이지 로딩 대기
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15));
// 도서관 선택
if (SelectLibrary(wait) == false)
{
result.ErrorMessage = "도서관선택실패";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색어 입력
try
{
var searchInput = wait.Until(d => d.FindElement(By.Id("bookSearchQuery")));
searchInput.Clear();
searchInput.SendKeys(searchTerm);
}
catch (Exception ex)
{
result.ErrorMessage = $"검색어입력실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색 버튼 클릭
try
{
var searchButton = wait.Until(d => d.FindElement(By.CssSelector("button.bookSearchBtn")));
searchButton.Click();
}
catch (Exception ex)
{
result.ErrorMessage = $"검색버튼클릭실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 페이지 변경을 감지하는 메서드
await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15)));
// 검색 결과 수 추출
var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = ermsg;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = ermsg;
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = ex.Message;
result.BookCount = 0;
}
return result;
}
private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage)
{
errmessage = string.Empty;
try
{
// 1. 검색결과가 없는 경우 확인
try
{
var noResultElement = driver.FindElement(By.XPath("//p[contains(text(), '조회된 도서가 없습니다')]"));
if (noResultElement != null)
{
errmessage = "검색결과없음";
return 0;
}
}
catch
{
// 검색결과가 있는 경우로 진행
}
// 2. srch_info div에서 직접 추출 시도
try
{
var srchInfoElement = driver.FindElement(By.CssSelector("div.srch_info"));
if (srchInfoElement != null)
{
// span.heighlight에서 숫자 추출 시도
try
{
var highlightElement = srchInfoElement.FindElement(By.CssSelector("span.heighlight"));
if (highlightElement != null)
{
var countText = highlightElement.Text.Trim();
if (int.TryParse(countText, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
catch { }
// 전체 텍스트에서 정규식으로 추출
var srchInfoText = srchInfoElement.Text;
var match = Regex.Match(srchInfoText, @"검색결과\s*총\s*(\d+)\s*건", RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"srch_info 요소 검색 중 오류: {ex.Message}");
}
// 3. 페이지 소스에서 정규식으로 추출 시도
var pageSource = driver.PageSource;
// 검색 결과가 없다는 메시지 확인
if (pageSource.Contains("조회된 도서가 없습니다") ||
pageSource.Contains("검색결과가 없습니다") ||
pageSource.Contains("검색된 자료가 없습니다"))
{
errmessage = "검색결과없음";
return 0;
}
// HTML에서 다양한 패턴 찾기
var htmlPatterns = new[]
{
@"검색결과\s*총\s*<span[^>]*class=""heighlight""[^>]*>\s*(\d+)\s*</span>\s*건",
@"검색결과\s*총\s*(\d+)\s*건",
@"<span[^>]*class=""heighlight""[^>]*>\s*(\d+)\s*</span>",
@"총\s*(\d+)\s*건"
};
foreach (var pattern in htmlPatterns)
{
var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
errmessage = "결과수량을찾을수없음";
return -1;
}
catch (Exception ex)
{
errmessage = ex.Message;
return -1;
}
}
// 페이지 변경을 감지하는 메서드
public async Task WaitForPageChange(WebDriverWait wait)
{
try
{
await Task.Delay(500);
// 페이지 로딩 상태 확인
wait.Until(d =>
{
var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState");
return readyState.Equals("complete");
});
// 검색 결과 페이지가 로드될 때까지 대기
wait.Until(d =>
{
try
{
var pageSource = d.PageSource;
// 검색 결과나 관련 요소가 나타나면 로드 완료
return pageSource.Contains("srch_info") ||
pageSource.Contains("검색결과") ||
pageSource.Contains("조회된 도서가 없습니다") ||
pageSource.Contains("총") ||
pageSource.Contains("건");
}
catch
{
return false;
}
});
}
catch (Exception ex)
{
// 모든 감지 방법이 실패하면 최소한의 대기 시간 적용
await Task.Delay(3000);
Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,181 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Web;
using UniMarc.SearchModel;
using System.Text;
namespace BokBonCheck
{
public class GwangsanLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://lib.gwangsan.go.kr/main/bookSearch/advanced";
public bool HttpApiMode { get; set; } = true;
public int No { get; set; }
private static readonly HttpClient _httpClient = new HttpClient();
public GwangsanLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"광주광산구({areaName})";
}
public async Task StartDriver(bool showdriver = false)
{
// HTTP 클라이언트 사용으로 별도 드라이버 불필요
await Task.CompletedTask;
}
public void StopDriver()
{
// HTTP 클라이언트 사용으로 별도 정리 불필요
}
public async Task<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();
}
}
}

View File

@@ -11,6 +11,10 @@ namespace BokBonCheck
public interface ILibrarySearcher
{
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ũ<>Ѹ<EFBFBD><D1B8><EFBFBD> <20>ƴ<EFBFBD> HTTP ȣ<><C8A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ó<><C3B3><EFBFBD>˴ϴ<CBB4>
/// </summary>
bool HttpApiMode { get; set; }
int No { get; set; }
string SiteName { get; }
string SiteUrl { get; }

View File

@@ -0,0 +1,166 @@
using System;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Net.Http;
using System.Collections.Generic; // Added missing import
using OpenQA.Selenium.Support.UI; // Added missing import
namespace BokBonCheck
{
public class IksanLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://lib.iksan.go.kr/main/site/search/bookSearch.do";
public bool HttpApiMode { get; set; } = true;
public int No { get; set; }
private static readonly HttpClient _httpClient = new HttpClient();
public IksanLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"익산시립({areaName})";
}
public void StopDriver()
{
// HTTP 클라이언트 사용으로 별도 정리 불필요
}
public async Task StartDriver(bool showdriver = false)
{
// HTTP 클라이언트 사용으로 별도 드라이버 불필요
await Task.CompletedTask;
}
public async Task<BookSearchResult> SearchAsync(string searchTerm)
{
var result = new BookSearchResult
{
SiteName = SiteName,
SearchTerm = searchTerm,
SearchTime = DateTime.Now
};
try
{
// 검색어 URL 인코딩
var encodedSearchTerm = System.Web.HttpUtility.UrlEncode(searchTerm, System.Text.Encoding.UTF8);
// 검색 URL 구성 - GET 방식으로 URL 파라미터 전송
var searchUrl = $"{SiteUrl}?cmd_name=bookandnonbooksearch&search_type=detail&detail=OK&use_facet=N&all_lib=N&search_item=search_title&search_txt=&search_title={encodedSearchTerm}";
// 도서관 코드가 있으면 추가
if (!string.IsNullOrEmpty(AreaCode))
{
searchUrl += $"&manage_code={AreaCode}";
}
Console.WriteLine($"익산시통합도서관 검색 요청: {searchTerm}, 도서관코드: {AreaCode}");
Console.WriteLine($"검색 URL: {searchUrl}");
// HTTP GET 요청 실행
var response = await _httpClient.GetAsync(searchUrl);
response.EnsureSuccessStatusCode();
var htmlContent = await response.Content.ReadAsStringAsync();
// 검색 결과 수 추출
var resultCount = ExtractBookCount(htmlContent, out string errorMessage);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = errorMessage;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = $"검색성공({resultCount}권)";
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = ex.Message;
result.BookCount = 0;
}
return result;
}
private int ExtractBookCount(string htmlContent, out string errmessage)
{
errmessage = string.Empty;
try
{
// 실제 HTML 구조에 맞는 패턴으로 수정
var htmlPatterns = new[]
{
// "상세검색결과 <strong>3</strong>개" 패턴 (실제 구조)
@"상세검색결과\s*<strong[^>]*>\s*(\d+)\s*</strong>\s*개",
// "전체 <strong class="word">1</strong>개가 검색되었습니다" 패턴 (이전 패턴)
@"전체\s*<strong[^>]*class=""word""[^>]*>\s*(\d+)\s*</strong>\s*개가\s*검색되었습니다",
// "전체 <strong class="word">0</strong>개가 검색되었습니다" 패턴 (0인 경우)
@"전체\s*<strong[^>]*class=""word""[^>]*>\s*0\s*</strong>\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;
}
}
}

View File

@@ -0,0 +1,404 @@
using System;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System.Text.RegularExpressions;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using UniMarc.;
using OpenQA.Selenium.Chromium;
using UniMarc.SearchModel;
using System.Runtime.CompilerServices;
using AR;
namespace BokBonCheck
{
public class JeonbukEduLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://lib.jbe.go.kr/jbe/intro/search/index.do";
public bool HttpApiMode { get; set; } = false;
public int No { get; set; }
private ChromiumDriver _driver;
public JeonbukEduLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"전북교육청({areaName})";
}
public void StopDriver()
{
if (_driver != null)
{
_driver.Quit();
_driver.Dispose();
_driver = null;
}
}
public async Task StartDriver(bool showdriver = false)
{
if (_driver == null)
{
try
{
if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download();
_driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver);
Console.WriteLine("JeonbukEduLibSearcher Driver 초기화 완료");
}
catch (Exception ex)
{
Console.WriteLine($"JeonbukEduLibSearcher Driver 초기화 실패: {ex.Message}");
throw new InvalidOperationException($"JeonbukEduLibSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex);
}
}
}
virtual protected bool SelectLibrary(WebDriverWait wait)
{
try
{
// 전체 선택인 경우 - 모든 체크박스 선택
if (string.IsNullOrEmpty(AreaCode) || AreaCode == "ALL")
{
var allCheckboxes1 = wait.Until(d => d.FindElements(By.CssSelector("input[name='libraryCodes']")));
foreach (var checkbox in allCheckboxes1)
{
if (!checkbox.Selected)
{
SafeClick(checkbox);
Thread.Sleep(100);
}
}
Console.WriteLine("전체 도서관 선택됨");
return true;
}
// 특정 도서관 선택인 경우
var targetCheckbox = wait.Until(d => d.FindElement(By.CssSelector($"input[name='libraryCodes'][value='{AreaCode}']")));
if (targetCheckbox == null)
{
Console.WriteLine($"도서관 체크박스를 찾을 수 없습니다: {AreaCode}");
return false;
}
// 다른 체크박스들 해제
var allCheckboxes = wait.Until(d => d.FindElements(By.CssSelector("input[name='libraryCodes']")));
foreach (var checkbox in allCheckboxes)
{
if (checkbox.GetAttribute("value") != AreaCode && checkbox.Selected)
{
SafeClick(checkbox);
Thread.Sleep(100);
}
}
// 대상 체크박스 선택
if (!targetCheckbox.Selected)
{
SafeClick(targetCheckbox);
Thread.Sleep(300);
Console.WriteLine($"{AreaCode} 도서관으로 변경됨");
}
else
{
Console.WriteLine($"{AreaCode} 도서관이 이미 선택되어 있음");
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"도서관 선택 실패: {ex.Message}");
return false;
}
}
protected void SafeClick(IWebElement element)
{
// 안정적인 클릭을 위한 여러 방법 시도
try
{
// 1. JavaScript로 클릭 시도
var driver = ((IWrapsDriver)element).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", element);
}
catch
{
try
{
// 2. 요소가 보이도록 스크롤 후 클릭
var driver = ((IWrapsDriver)element).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", element);
Thread.Sleep(500);
element.Click();
}
catch
{
try
{
// 3. Actions 클래스 사용
var driver = ((IWrapsDriver)element).WrappedDriver;
var actions = new OpenQA.Selenium.Interactions.Actions(driver);
actions.MoveToElement(element).Click().Perform();
}
catch
{
// 4. 마지막 방법: JavaScript로 직접 체크 상태 변경
var driver = ((IWrapsDriver)element).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].checked = !arguments[0].checked;", element);
}
}
}
}
public async Task<BookSearchResult> SearchAsync(string searchTerm)
{
var result = new BookSearchResult
{
SiteName = SiteName,
SearchTerm = searchTerm,
SearchTime = DateTime.Now
};
try
{
// 드라이버가 없으면 자동으로 시작
if (_driver == null)
{
await StartDriver();
}
var cururl = _driver.Url;
if (cururl.Equals(SiteUrl) == false)
_driver.Navigate().GoToUrl(SiteUrl);
// 페이지 로딩 대기
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15));
// 도서관 선택
if (SelectLibrary(wait) == false)
{
result.ErrorMessage = "도서관선택실패";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색어 입력
try
{
var searchInput = wait.Until(d => d.FindElement(By.Id("search_text")));
searchInput.Clear();
searchInput.SendKeys(searchTerm);
}
catch (Exception ex)
{
result.ErrorMessage = $"검색어입력실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색 버튼 클릭
try
{
var searchButton = wait.Until(d => d.FindElement(By.Id("do-search")));
searchButton.Click();
}
catch (Exception ex)
{
result.ErrorMessage = $"검색버튼클릭실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 페이지 변경을 감지하는 메서드
await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15)));
// 검색 결과 수 추출
var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = ermsg;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = ermsg;
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = ex.Message;
result.BookCount = 0;
}
return result;
}
private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage)
{
errmessage = string.Empty;
try
{
// 1. search-info div에서 직접 추출 시도
try
{
var searchInfoElement = driver.FindElement(By.CssSelector("div.search-info"));
if (searchInfoElement != null)
{
var searchInfoText = searchInfoElement.Text;
// "총 N건이 검색되었습니다" 패턴 찾기
var match = Regex.Match(searchInfoText, @"총\s*(\d+)\s*건이\s*검색되었습니다", RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
// <b> 태그에서 직접 숫자 추출 시도
try
{
var boldElements = searchInfoElement.FindElements(By.TagName("b"));
foreach (var boldElement in boldElements)
{
var boldText = boldElement.Text.Trim();
if (int.TryParse(boldText, out int boldCount))
{
if (boldCount == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({boldCount}권)";
return boldCount;
}
}
}
catch { }
}
}
catch (Exception ex)
{
Console.WriteLine($"search-info 요소 검색 중 오류: {ex.Message}");
}
// 2. 페이지 소스에서 정규식으로 추출 시도
var pageSource = driver.PageSource;
// 검색 결과가 없다는 메시지 확인
if (pageSource.Contains("0건이 검색되었습니다") ||
pageSource.Contains("검색결과가 없습니다") ||
pageSource.Contains("검색된 자료가 없습니다"))
{
errmessage = "검색결과없음";
return 0;
}
// HTML에서 다양한 패턴 찾기
var htmlPatterns = new[]
{
@"총\s*<b>(\d+)</b>\s*건이\s*검색되었습니다",
@"총\s*(\d+)\s*건이\s*검색되었습니다",
@"총\s*<b>\s*(\d+)\s*</b>\s*건",
@"총\s*(\d+)\s*건"
};
foreach (var pattern in htmlPatterns)
{
var match = Regex.Match(pageSource, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
errmessage = "결과수량을찾을수없음";
return -1;
}
catch (Exception ex)
{
errmessage = ex.Message;
return -1;
}
}
// 페이지 변경을 감지하는 메서드
public async Task WaitForPageChange(WebDriverWait wait)
{
try
{
await Task.Delay(500);
// 페이지 로딩 상태 확인
wait.Until(d =>
{
var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState");
return readyState.Equals("complete");
});
// 검색 결과 페이지가 로드될 때까지 대기
wait.Until(d =>
{
try
{
var pageSource = d.PageSource;
// search-info div나 검색 결과 관련 요소가 나타나면 로드 완료
return pageSource.Contains("search-info") ||
pageSource.Contains("검색되었습니다") ||
pageSource.Contains("검색결과") ||
pageSource.Contains("총") ||
pageSource.Contains("건이");
}
catch
{
return false;
}
});
}
catch (Exception ex)
{
// 모든 감지 방법이 실패하면 최소한의 대기 시간 적용
await Task.Delay(3000);
Console.WriteLine($"페이지 변경 감지 실패: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,413 @@
using System;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System.Text.RegularExpressions;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using UniMarc.;
using OpenQA.Selenium.Chromium;
using UniMarc.SearchModel;
using System.Runtime.CompilerServices;
using AR;
namespace BokBonCheck
{
public class JunnamEduJiheaNuriSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://jnelib.jne.go.kr/book/search_book/search.es?mid=d20101000000";
public bool HttpApiMode { get; set; } = false;
public int No { get; set; }
private ChromiumDriver _driver;
public JunnamEduJiheaNuriSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"전남교육청행정자료실({areaName})";
}
public void StopDriver()
{
if (_driver != null)
{
_driver.Quit();
_driver.Dispose();
_driver = null;
}
}
public async Task StartDriver(bool showdriver = false)
{
if (_driver == null)
{
try
{
if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download();
_driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver);
Console.WriteLine("JunnamEduJiheaNuriSearcher Driver 초기화 완료");
}
catch (Exception ex)
{
Console.WriteLine($"JunnamEduJiheaNuriSearcher Driver 초기화 실패: {ex.Message}");
throw new InvalidOperationException($"JunnamEduJiheaNuriSearcher Driver 초기화에 실패했습니다: {ex.Message}", ex);
}
}
}
virtual protected bool SelectLibrary(WebDriverWait wait)
{
try
{
if (this.AreaCode.isEmpty()) return true;
// Areacode가 "ALL"인 경우
if (AreaCode == "ALL")
{
var allCheckbox = wait.Until(d => d.FindElement(By.Id("all_srch")));
if (!allCheckbox.Selected)
{
SafeClick(allCheckbox);
Thread.Sleep(300);
Console.WriteLine("전체 선택으로 변경됨");
}
else
{
Console.WriteLine("전체 선택이 이미 활성화되어 있음");
}
return true;
}
// 특정 지역 선택인 경우
var targetSelector = $"libInfo_{AreaCode}";
var targetCheckbox = wait.Until(d => d.FindElement(By.Id(targetSelector)));
if (targetCheckbox == null)
{
Console.WriteLine($"도서관 체크박스를 찾을 수 없습니다: {targetSelector}");
return false;
}
// 전체 선택이 되어 있으면 해제
var allCheckbox2 = wait.Until(d => d.FindElement(By.Id("all_srch")));
if (allCheckbox2.Selected)
{
SafeClick(allCheckbox2);
Thread.Sleep(300);
Console.WriteLine("전체 선택 해제됨");
}
// 대상 지역 선택
if (!targetCheckbox.Selected)
{
SafeClick(targetCheckbox);
Thread.Sleep(300);
Console.WriteLine($"{AreaCode} 지역으로 변경됨");
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"도서관 선택 실패: {ex.Message}");
return false;
}
}
protected void SafeClick(IWebElement searchBox)
{
// 안정적인 클릭을 위한 여러 방법 시도
try
{
// 1. JavaScript로 클릭 시도
var driver = ((IWrapsDriver)searchBox).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", searchBox);
}
catch
{
try
{
// 2. 요소가 보이도록 스크롤 후 클릭
var driver = ((IWrapsDriver)searchBox).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", searchBox);
Thread.Sleep(500);
searchBox.Click();
}
catch
{
try
{
// 3. Actions 클래스 사용
var driver = ((IWrapsDriver)searchBox).WrappedDriver;
var actions = new OpenQA.Selenium.Interactions.Actions(driver);
actions.MoveToElement(searchBox).Click().Perform();
}
catch
{
// 4. 마지막 방법: JavaScript로 직접 체크 해제
var driver = ((IWrapsDriver)searchBox).WrappedDriver;
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].checked = false;", searchBox);
}
}
}
}
public async Task<BookSearchResult> SearchAsync(string searchTerm)
{
var result = new BookSearchResult
{
SiteName = SiteName,
SearchTerm = searchTerm,
SearchTime = DateTime.Now
};
try
{
// 드라이버가 없으면 자동으로 시작
if (_driver == null)
{
await StartDriver();
}
var cururl = _driver.Url;
if (cururl.Equals(SiteUrl) == false)
_driver.Navigate().GoToUrl(SiteUrl);
// 페이지 로딩 대기
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15));
// 도서관 선택
if (SelectLibrary(wait) == false)
{
result.ErrorMessage = "도서관선택실패";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색 방법을 서명으로 변경
try
{
var searchKeyWordSelect = wait.Until(d => d.FindElement(By.Id("searchKeyWord")));
var selectElement = new SelectElement(searchKeyWordSelect);
selectElement.SelectByValue("1"); // 1 = 서명
Thread.Sleep(300);
}
catch (Exception ex)
{
result.ErrorMessage = $"검색방법선택실패({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색어 입력
IWebElement searchBox = null;
try
{
searchBox = wait.Until(d => d.FindElement(By.Id("searchWordText")));
searchBox.Clear();
searchBox.SendKeys(searchTerm);
}
catch (Exception ex)
{
result.ErrorMessage = $"검색창없음({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 검색 버튼 클릭
IWebElement searchButton = null;
try
{
searchButton = _driver.FindElement(By.CssSelector("button[type='submit']"));
if (searchButton != null)
searchButton.Click();
else
{
result.ErrorMessage = $"검색버튼없음";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
}
catch (Exception ex)
{
result.ErrorMessage = $"검색버튼없음({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 페이지 변경을 감지하는 메서드
await WaitForPageChange(new WebDriverWait(_driver, TimeSpan.FromSeconds(15)));
// 검색 결과 수 추출
var resultCount = ExtractBookCount(_driver, searchTerm, out string ermsg);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = ermsg;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = ermsg;
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = ex.Message;
result.BookCount = 0;
}
return result;
}
private int ExtractBookCount(IWebDriver driver, string searchTerm, out string errmessage)
{
errmessage = string.Empty;
try
{
// 먼저 검색결과가 없는 경우 확인
try
{
var noResultDiv = driver.FindElement(By.CssSelector(".SearchResult .resultBook"));
var noResultText = noResultDiv.Text;
if (noResultText.Contains("검색결과가 없습니다"))
{
errmessage = "검색결과없음";
return 0;
}
}
catch
{
// 검색결과가 없는 경우 div가 없을 수도 있음
}
// 검색결과가 있는 경우 p.page_info에서 전체 건수 추출
try
{
var pageInfo = driver.FindElement(By.CssSelector("p.page_info"));
var pageInfoText = pageInfo.Text; // 예: "전체 7건, 현재 페이지 1/1"
var match = System.Text.RegularExpressions.Regex.Match(pageInfoText, @"전체\s*(\d+)건");
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int vqty))
{
errmessage = $"검색성공({vqty}건)";
return vqty;
}
else
{
errmessage = $"수량값오류({match.Groups[1].Value})";
return -1;
}
}
else
{
errmessage = "수량항목없음";
return -1;
}
}
catch (Exception ex)
{
// page_info가 없는 경우 검색결과가 없는 것으로 판단
errmessage = "검색결과없음";
return 0;
}
}
catch (Exception ex)
{
errmessage = ex.Message;
return -1;
}
}
// 페이지 변경을 감지하는 메서드
public async Task WaitForPageChange(WebDriverWait wait)
{
try
{
await Task.Delay(500);
// 페이지 로딩 상태 확인
wait.Until(d =>
{
var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState");
return readyState.Equals("complete");
});
// 검색 결과 페이지가 로드될 때까지 대기
wait.Until(d =>
{
try
{
// 검색결과가 없는 경우 확인
try
{
var noResultDiv = d.FindElement(By.CssSelector(".SearchResult .resultBook"));
if (noResultDiv.Text.Contains("검색결과가 없습니다"))
{
return true; // 검색결과 없음 페이지 로드 완료
}
}
catch
{
// 검색결과가 있는 경우로 진행
}
// 검색결과가 있는 경우 page_info 확인
try
{
var pageInfo = d.FindElement(By.CssSelector("p.page_info"));
var pageInfoText = pageInfo.Text;
// "전체 N건" 형식이 나타나면 로드 완료
return pageInfoText.Contains("전체") && pageInfoText.Contains("건");
}
catch
{
return false;
}
}
catch
{
return false;
}
});
}
catch (Exception ex)
{
// 모든 감지 방법이 실패하면 최소한의 대기 시간 적용
await Task.Delay(2000);
throw new Exception($"페이지 변경 감지 실패: {ex.Message}");
}
}
}
}

View File

@@ -19,10 +19,10 @@ namespace BokBonCheck
public class JunnamEduSearcher : ILibrarySearcher
{
public string Areacode { get; set; } = string.Empty;
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://jnelib.jne.go.kr/book/search_book/search.es?mid=d50101000000";
public bool HttpApiMode { get; set; } = false;
public int No { get; set; }
private ChromiumDriver _driver;
@@ -30,7 +30,7 @@ namespace BokBonCheck
public JunnamEduSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.Areacode = areaCode;
this.AreaCode = areaCode;
this.SiteName = $"전남교육청({areaName})";
}
@@ -71,28 +71,28 @@ namespace BokBonCheck
try
{
// Areacode가 "ALL"인 경우
if (Areacode == "ALL")
if (AreaCode == "ALL")
{
var allCheckbox = wait.Until(d => d.FindElement(By.CssSelector("#all_srch")));
var allCheckbox1 = wait.Until(d => d.FindElement(By.CssSelector("#all_srch")));
// 이미 전체가 선택되어 있으면 변경하지 않음
if (allCheckbox.Selected)
if (allCheckbox1.Selected)
{
Console.WriteLine("전체 선택이 이미 활성화되어 있음");
return true;
}
// 전체 선택이 안되어 있으면 활성화
SafeClick(allCheckbox);
SafeClick(allCheckbox1);
Thread.Sleep(300);
Console.WriteLine("전체 선택으로 변경됨");
return true;
}
// 특정 지역 선택인 경우
var targetSelector = $"#libInfo_{Areacode}";
var targetSelector = $"#libInfo_{AreaCode}";
var targetCheckbox = wait.Until(d => d.FindElement(By.CssSelector(targetSelector)));
if (targetCheckbox == null)
{
Console.WriteLine($"도서관 체크박스를 찾을 수 없습니다: {targetSelector}");
@@ -107,7 +107,7 @@ namespace BokBonCheck
// 이미 원하는 지역이 선택되어 있고, 전체 선택이 해제되어 있으면 변경하지 않음
if (isTargetSelected && !isAllSelected)
{
Console.WriteLine($"{Areacode} 지역이 이미 선택되어 있음");
Console.WriteLine($"{AreaCode} 지역이 이미 선택되어 있음");
return true;
}
@@ -124,9 +124,9 @@ namespace BokBonCheck
{
SafeClick(targetCheckbox);
Thread.Sleep(300);
Console.WriteLine($"{Areacode} 지역으로 변경됨");
Console.WriteLine($"{AreaCode} 지역으로 변경됨");
}
return true;
}
catch (Exception ex)
@@ -191,7 +191,9 @@ namespace BokBonCheck
await StartDriver();
}
_driver.Navigate().GoToUrl(SiteUrl);
var cururl = _driver.Url;
if (cururl.Equals(SiteUrl) == false)
_driver.Navigate().GoToUrl(SiteUrl);
// 페이지 로딩 대기
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(15));
@@ -247,7 +249,7 @@ namespace BokBonCheck
try
{
searchButton = _driver.FindElement(By.CssSelector("button[type='submit']"));
if(searchButton != null)
if (searchButton != null)
searchButton.Click();
else
{
@@ -321,7 +323,7 @@ namespace BokBonCheck
{
var pageInfo = driver.FindElement(By.CssSelector("p.page_info"));
var pageInfoText = pageInfo.Text; // 예: "전체 7건, 현재 페이지 1/1"
var match = System.Text.RegularExpressions.Regex.Match(pageInfoText, @"전체\s*(\d+)건");
if (match.Success)
{

View File

@@ -13,73 +13,23 @@ using OpenQA.Selenium.Chromium;
namespace BokBonCheck
{
public class KwangjuCityLibrarySearcher_choisangjun : KwangjuCityLibrarySearcher
{
public KwangjuCityLibrarySearcher_choisangjun(int no) : base(no)
{
SelectorValue = "MF";
SiteName = "광주시교육청통합도서관(분관최상준도서관)";
}
}
public class KwangjuCityLibrarySearcher_dagachi : KwangjuCityLibrarySearcher
{
public KwangjuCityLibrarySearcher_dagachi(int no) : base(no)
{
SelectorValue = "ME";
SiteName = "광주시교육청통합도서관(송정다가치문화도서관)";
}
}
public class KwangjuCityLibrarySearcher_central : KwangjuCityLibrarySearcher
{
public KwangjuCityLibrarySearcher_central(int no) : base(no)
{
SelectorValue = "MD";
SiteName = "광주시교육청통합도서관(중앙도서관)";
}
}
public class KwangjuCityLibrarySearcher_Student : KwangjuCityLibrarySearcher
{
public KwangjuCityLibrarySearcher_Student(int no) : base(no)
{
SelectorValue = "MC";
SiteName = "광주시교육청통합도서관(학생교육문화회관)";
}
}
public class KwangjuCityLibrarySearcher_Kumho : KwangjuCityLibrarySearcher
{
public KwangjuCityLibrarySearcher_Kumho(int no) : base(no)
{
SelectorValue = "MB";
SiteName = "광주시교육청통합도서관(금호평생교육관)";
}
}
public class KwangjuCityLibrarySearcher_Indi : KwangjuCityLibrarySearcher
{
public KwangjuCityLibrarySearcher_Indi(int no) : base(no)
{
SelectorValue = "MA";
SiteName = "광주시교육청통합도서관(학생독립운동기념회관)";
}
}
public abstract class KwangjuCityLibrarySearcher : ILibrarySearcher
public class KwangjuCityEduLibrarySearcher : ILibrarySearcher
{
public int No { get; set; }
protected string AreaCode = "";
public string SiteName { get; protected set; } = "광주시교육청통합도서관";
public string SiteUrl => "https://lib.gen.go.kr/main/site/search/bookSearch.do#simple";
protected string SelectorValue = "";
public bool HttpApiMode { get; set; } = false;
private ChromiumDriver _driver;
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_MINIMIZE = 6;
public KwangjuCityLibrarySearcher(int no)
public KwangjuCityEduLibrarySearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"광주시교육청통합도서관({areaName})";
}
public async Task StartDriver(bool showBrowser = false)
@@ -119,7 +69,7 @@ namespace BokBonCheck
var select = new OpenQA.Selenium.Support.UI.SelectElement(selectElement);
// value가 "MA"인 옵션 선택
select.SelectByValue(SelectorValue);
select.SelectByValue(AreaCode);
}
catch
{

View File

@@ -0,0 +1,188 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Web;
using UniMarc.SearchModel;
using System.Text;
namespace BokBonCheck
{
public class MokpoLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://mokpolib.or.kr/dls_lt/index.php";
public bool HttpApiMode { get; set; } = true;
public int No { get; set; }
private static readonly HttpClient _httpClient = new HttpClient();
public MokpoLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"목포시립({areaName})";
}
public async Task StartDriver(bool showdriver = false)
{
// HTTP 클라이언트 사용으로 별도 드라이버 불필요
await Task.CompletedTask;
}
public void StopDriver()
{
// HTTP 클라이언트 사용으로 별도 정리 불필요
}
public async Task<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}?mod=wdDataSearch&act=searchResultList&manageSearch=&detailSearch=";
// 도서관 코드가 있으면 추가
if (!string.IsNullOrEmpty(AreaCode))
{
searchUrl += $"&manageCode%5B{AreaCode}%5D={AreaCode}";
}
// 검색 방식을 제목으로 설정하고 검색어 추가
searchUrl += $"&searchItem%5B%5D=title&searchWord%5B%5D={encodedSearchTerm}";
Console.WriteLine($"목포시립도서관 검색 URL: {searchUrl}");
// HTTP GET 요청 실행
var response = await _httpClient.GetAsync(searchUrl);
response.EnsureSuccessStatusCode();
var htmlContent = await response.Content.ReadAsStringAsync();
// 검색 결과 수 추출
var resultCount = ExtractBookCount(htmlContent, out string errorMessage);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = errorMessage;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = $"검색성공({resultCount}권)";
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = $"검색 오류: {ex.Message}";
result.BookCount = 0;
Console.WriteLine($"목포시립도서관 검색 오류: {ex.Message}");
}
return result;
}
private int ExtractBookCount(string htmlContent, out string errorMessage)
{
errorMessage = string.Empty;
try
{
// HTML에서 "총 <strong class="cyan">N권(개)</strong>" 패턴 찾기
var patterns = new[]
{
@"총\s*<strong[^>]*class=""cyan""[^>]*>(\d+)권\(개\)</strong>",
@"총\s*<strong[^>]*>(\d+)권\(개\)</strong>",
@"총\s*(\d+)권\(개\)",
@"총\s*<strong[^>]*class=""cyan""[^>]*>(\d+)</strong>",
@"총\s*<strong[^>]*>(\d+)</strong>"
};
foreach (var pattern in patterns)
{
var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errorMessage = "검색결과없음";
return 0;
}
errorMessage = $"검색성공({count}권)";
return count;
}
}
}
// 검색 결과가 없다는 메시지 확인
if (htmlContent.Contains("검색결과가 없습니다") ||
htmlContent.Contains("검색된 자료가 없습니다") ||
htmlContent.Contains("자료가 없습니다") ||
htmlContent.Contains("총 0권(개)"))
{
errorMessage = "검색결과없음";
return 0;
}
// 더 넓은 범위로 숫자 찾기 시도
var generalPatterns = new[]
{
@"총\s*.*?(\d+)\s*권",
@"총\s*.*?(\d+)\s*개",
@"총\s*.*?(\d+)",
@"<strong[^>]*class=""cyan""[^>]*>(\d+)</strong>"
};
foreach (var pattern in generalPatterns)
{
var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errorMessage = "검색결과없음";
return 0;
}
errorMessage = $"검색성공({count}권)";
return count;
}
}
}
errorMessage = "검색결과 패턴을 찾을 수 없음";
return -1;
}
catch (Exception ex)
{
errorMessage = $"결과 분석 오류: {ex.Message}";
return -1;
}
}
public Task WaitForPageChange(OpenQA.Selenium.Support.UI.WebDriverWait wait)
{
throw new NotImplementedException();
}
}
}

View File

@@ -17,198 +17,14 @@ using System.Runtime.CompilerServices;
namespace BokBonCheck
{
public class NamguLibrarySearcher_Munhwa : NamguLibrarySearcher
{
public NamguLibrarySearcher_Munhwa(int no) : base(no)
{
SiteName = "남구통합도서관(문화정보도서관)"; // 문화관 검색기
}
protected override bool SelectLibrary(WebDriverWait wait)
{
IWebElement searchBox = null;
try
{
var selector = "#clickAll";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == true)
{
SafeClick(searchBox);
}
selector = "#libMA";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == false)
{
SafeClick(searchBox);
}
return true;
}
catch
{
return false;
}
}
}
public class NamguLibrarySearcher_Purungil : NamguLibrarySearcher
{
public NamguLibrarySearcher_Purungil(int no) : base(no)
{
SiteName = "남구통합도서관(푸른길도서관)";
}
protected override bool SelectLibrary(WebDriverWait wait)
{
IWebElement searchBox = null;
try
{
var selector = "#clickAll";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == true)
{
SafeClick(searchBox);
}
selector = "#libMB";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == false)
{
SafeClick(searchBox);
}
return true;
}
catch
{
return false;
}
}
}
public class NamguLibrarySearcher_Children : NamguLibrarySearcher
{
public NamguLibrarySearcher_Children(int no) : base(no)
{
SiteName = "남구통합도서관(청소년도서관)";
}
protected override bool SelectLibrary(WebDriverWait wait)
{
IWebElement searchBox = null;
try
{
var selector = "#clickAll";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == true)
{
SafeClick(searchBox);
}
selector = "#libMC";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == false)
{
//searchBox.Click(); // 체크박스 선택
SafeClick(searchBox);
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return false;
}
}
}
public class NamguLibrarySearcher_Hyocheon : NamguLibrarySearcher
{
public NamguLibrarySearcher_Hyocheon(int no) : base(no)
{
SiteName = "남구통합도서관(효천어울림도서관)";
}
protected override bool SelectLibrary(WebDriverWait wait)
{
IWebElement searchBox = null;
try
{
var selector = "#clickAll";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == true)
{
SafeClick(searchBox);
}
selector = "#libSW";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == false)
{
SafeClick(searchBox);
}
return true;
}
catch
{
return false;
}
}
}
public class NamguLibrarySearcher_Smart : NamguLibrarySearcher
{
public NamguLibrarySearcher_Smart(int no) : base(no)
{
SiteName = "남구통합도서관(스마트도서관)";
}
protected override bool SelectLibrary(WebDriverWait wait)
{
IWebElement searchBox = null;
try
{
var selector = "#clickAll";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == true)
{
SafeClick(searchBox);
}
selector = "#libSQ";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == false)
{
SafeClick(searchBox);
}
return true;
}
catch
{
return false;
}
}
}
public class NamguLibrarySearcher : ILibrarySearcher
{
protected string AreaCode = "";
public string SiteName { get; protected set; } = "남구통합도서관(전체)";
public string SiteUrl => "https://lib.namgu.gwangju.kr/main/bookSearch";
public bool HttpApiMode { get; set; } = false;
public int No { get; set; }
private ChromiumDriver _driver;
@@ -219,9 +35,11 @@ namespace BokBonCheck
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_MINIMIZE = 6;
public NamguLibrarySearcher(int no)
public NamguLibrarySearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"남구통합도서관({areaName})";
}
@@ -264,15 +82,24 @@ namespace BokBonCheck
var selector = "#clickAll";
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == true)
{
SafeClick(searchBox);
}
selector = AreaCode;
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
if (searchBox == null) return false;
if (searchBox.Selected == false)
{
SafeClick(searchBox);
}
return true;
}
catch (Exception ex)
catch
{
Console.WriteLine(ex.Message);
return false;
}
}

View File

@@ -0,0 +1,229 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Web;
using UniMarc.SearchModel;
using System.Text;
namespace BokBonCheck
{
public class SuncheonLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://library.suncheon.go.kr/lib/book/search/searchIndex.do";
public bool HttpApiMode { get; set; } = true;
public int No { get; set; }
private static readonly HttpClient _httpClient = new HttpClient()
{
DefaultRequestHeaders =
{
{ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" }
}
};
public SuncheonLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"순천시립({areaName})";
}
public async Task StartDriver(bool showdriver = false)
{
// HTTP 클라이언트 사용으로 별도 드라이버 불필요
await Task.CompletedTask;
}
public void StopDriver()
{
// HTTP 클라이언트 사용으로 별도 정리 불필요
}
public async Task<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}?menuCd=L001001001&alpha=&vcindex=&currentPageNo=1&nPageSize=10&searchType=title&search={encodedSearchTerm}&mediaCode=";
// 도서관 코드가 있으면 추가
if (!string.IsNullOrEmpty(AreaCode))
{
if (AreaCode == "mini")
{
// 작은도서관의 경우 모든 코드를 포함
searchUrl += "&manageCd=AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,BB,BC,BD,BF,BG,BH,BI,BJ,BM,BQ,BS,BW,CA,CB,CC,CD,CE,CF,CG,CH,CI,CJ,CK,CL,CM,CN,CO,CP,CQ,DA,DB,DC,DD,DE,DF,DG,DH,DI,DJ,DK,DL,DS,GD";
}
else
{
searchUrl += $"&manageCd={AreaCode}";
}
}
Console.WriteLine($"순천시립도서관 검색 URL: {searchUrl}");
// HTTP GET 요청 실행 (추가 헤더 포함)
using (var request = new HttpRequestMessage(HttpMethod.Get, searchUrl))
{
// 브라우저와 유사한 헤더 추가
request.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
request.Headers.Add("Accept-Language", "ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3");
request.Headers.Add("Accept-Encoding", "gzip, deflate, br");
request.Headers.Add("Connection", "keep-alive");
request.Headers.Add("Upgrade-Insecure-Requests", "1");
var response = await _httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync();
throw new HttpRequestException($"HTTP {(int)response.StatusCode} {response.StatusCode}: {errorContent}");
}
var htmlContent = await response.Content.ReadAsStringAsync();
// 검색 결과 수 추출
var resultCount = ExtractBookCount(htmlContent, out string errorMessage);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = errorMessage;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = $"검색성공({resultCount}권)";
}
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = $"검색 오류: {ex.Message}";
result.BookCount = 0;
Console.WriteLine($"순천시립도서관 검색 오류: {ex.Message}");
}
return result;
}
private int ExtractBookCount(string htmlContent, out string errorMessage)
{
errorMessage = string.Empty;
try
{
// HTML에서 "총 <strong class="cred">N</strong>건" 패턴 찾기
var patterns = new[]
{
@"총\s*<strong[^>]*class=""cred""[^>]*>(\d+)</strong>\s*건",
@"총\s*<strong[^>]*>(\d+)</strong>\s*건",
@"총\s*(\d+)\s*건",
@"<strong[^>]*class=""cred""[^>]*>(\d+)</strong>"
};
foreach (var pattern in patterns)
{
var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errorMessage = "검색결과없음";
return 0;
}
errorMessage = $"검색성공({count}권)";
return count;
}
}
}
// 검색 결과가 없다는 메시지 확인
if (htmlContent.Contains("검색결과가 없습니다") ||
htmlContent.Contains("검색된 자료가 없습니다") ||
htmlContent.Contains("자료가 없습니다") ||
htmlContent.Contains("총 0건"))
{
errorMessage = "검색결과없음";
return 0;
}
// resultCon 영역에서 더 자세히 검색
var resultConPattern = @"<div[^>]*class=""resultCon[^""]*""[^>]*>.*?총\s*<strong[^>]*>(\d+)</strong>\s*건.*?</div>";
var resultConMatch = Regex.Match(htmlContent, resultConPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
if (resultConMatch.Success)
{
if (int.TryParse(resultConMatch.Groups[1].Value, out int count))
{
if (count == 0)
{
errorMessage = "검색결과없음";
return 0;
}
errorMessage = $"검색성공({count}권)";
return count;
}
}
// 더 넓은 범위로 숫자 찾기 시도
var generalPatterns = new[]
{
@"검색한\s*결과\s*총\s*<strong[^>]*>(\d+)</strong>",
@"검색결과를\s*찾았습니다[^>]*>(\d+)</strong>",
@"<strong[^>]*class=""cred""[^>]*>(\d+)</strong>"
};
foreach (var pattern in generalPatterns)
{
var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errorMessage = "검색결과없음";
return 0;
}
errorMessage = $"검색성공({count}권)";
return count;
}
}
}
errorMessage = "검색결과 패턴을 찾을 수 없음";
return -1;
}
catch (Exception ex)
{
errorMessage = $"결과 분석 오류: {ex.Message}";
return -1;
}
}
public Task WaitForPageChange(OpenQA.Selenium.Support.UI.WebDriverWait wait)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,167 @@
using System;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System.Text.RegularExpressions;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using UniMarc.;
using OpenQA.Selenium.Chromium;
using UniMarc.SearchModel;
using System.Runtime.CompilerServices;
using AR;
using System.Net.Http;
namespace BokBonCheck
{
public class WandoLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://wandolib.kr/dls_le/index.php";
public bool HttpApiMode { get; set; } = true;
public int No { get; set; }
private ChromiumDriver _driver;
private static readonly HttpClient _httpClient = new HttpClient();
public WandoLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"완도군립({areaName})";
}
public void StopDriver()
{
// HTTP 클라이언트 사용으로 별도 정리 불필요
}
public async Task StartDriver(bool showdriver = false)
{
// HTTP 클라이언트 사용으로 별도 드라이버 불필요
await Task.CompletedTask;
}
public async Task<BookSearchResult> SearchAsync(string searchTerm)
{
var result = new BookSearchResult
{
SiteName = SiteName,
SearchTerm = searchTerm,
SearchTime = DateTime.Now
};
try
{
// 검색어 URL 인코딩
var encodedSearchTerm = System.Web.HttpUtility.UrlEncode(searchTerm, System.Text.Encoding.UTF8);
// 검색 URL 구성
var searchUrl = $"{SiteUrl}?mod=wdDataSearch&act=searchIList&item=title&word={encodedSearchTerm}";
// 도서관 코드가 있으면 추가
if (!string.IsNullOrEmpty(AreaCode))
{
searchUrl += $"&manageCode%5B{AreaCode}%5D={AreaCode}";
}
Console.WriteLine($"완도군립도서관 검색 URL: {searchUrl}");
// HTTP GET 요청 실행
var response = await _httpClient.GetAsync(searchUrl);
response.EnsureSuccessStatusCode();
var htmlContent = await response.Content.ReadAsStringAsync();
// 검색 결과 수 추출
var resultCount = ExtractBookCount(htmlContent, out string errorMessage);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = errorMessage;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = $"검색성공({resultCount}권)";
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = ex.Message;
result.BookCount = 0;
}
return result;
}
private int ExtractBookCount(string htmlContent, out string errmessage)
{
errmessage = string.Empty;
try
{
// 검색 결과가 없다는 메시지 확인
if (htmlContent.Contains("0권(개)") || htmlContent.Contains("검색결과가 없습니다"))
{
errmessage = "검색결과없음";
return 0;
}
// HTML에서 다양한 패턴 찾기
var htmlPatterns = new[]
{
@"총\s*<strong[^>]*class=""cyan""[^>]*>\s*(\d+)\s*권\(개\)",
@"총\s*(\d+)\s*권\(개\)",
@"<strong[^>]*class=""cyan""[^>]*>\s*(\d+)\s*권",
@"(\d+)\s*권\(개\)"
};
foreach (var pattern in htmlPatterns)
{
var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
if (count == 0)
{
errmessage = "검색결과없음";
return 0;
}
errmessage = $"검색성공({count}권)";
return count;
}
}
}
errmessage = "결과수량을찾을수없음";
return -1;
}
catch (Exception ex)
{
errmessage = ex.Message;
return -1;
}
}
// 페이지 변경을 감지하는 메서드 (HTTP 방식에서는 불필요)
public async Task WaitForPageChange(WebDriverWait wait)
{
// HTTP 방식에서는 즉시 응답이 오므로 대기 불필요
await Task.CompletedTask;
}
}
}

View File

@@ -0,0 +1,162 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Web;
using UniMarc.SearchModel;
using System.Text;
using OpenQA.Selenium.Support.UI;
namespace BokBonCheck
{
public class YeosuLibSearcher : ILibrarySearcher
{
protected string AreaCode { get; set; } = string.Empty;
public string SiteName { get; protected set; }
public string SiteUrl => "https://yslib.yeosu.go.kr/dls_kapi/index.php";
public int No { get; set; }
public bool HttpApiMode { get; set; } = true;
private static readonly HttpClient _httpClient = new HttpClient();
public YeosuLibSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.AreaCode = areaCode;
this.SiteName = $"여수시립({areaName})";
}
public async Task StartDriver(bool showdriver = false)
{
// HTTP 클라이언트 사용으로 별도 드라이버 불필요
await Task.CompletedTask;
}
public void StopDriver()
{
// HTTP 클라이언트 사용으로 별도 정리 불필요
}
public async Task<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}?act=searchResultList&mod=wdDataSearch";
// 지역코드가 있으면 추가 (전체인 경우 facetManageCode 생략)
if (!string.IsNullOrEmpty(AreaCode))
{
searchUrl += $"&facetManageCode={AreaCode}";
}
searchUrl += $"&searchWord={encodedSearchTerm}&author=&keyword=&publisher=&pubYearS=&pubYearE=&dataSort=rk+desc";
Console.WriteLine($"여수시립도서관 검색 URL: {searchUrl}");
// HTTP GET 요청 실행
var response = await _httpClient.GetAsync(searchUrl);
response.EnsureSuccessStatusCode();
var htmlContent = await response.Content.ReadAsStringAsync();
// 검색 결과 수 추출
var resultCount = ExtractBookCount(htmlContent, out string errorMessage);
if (resultCount == -1)
{
result.BookCount = 0;
result.IsSuccess = false;
result.ErrorMessage = errorMessage;
}
else
{
result.BookCount = resultCount;
result.IsSuccess = true;
result.ErrorMessage = $"검색성공({resultCount}권)";
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.ErrorMessage = $"검색 오류: {ex.Message}";
result.BookCount = 0;
Console.WriteLine($"여수시립도서관 검색 오류: {ex.Message}");
}
return result;
}
private int ExtractBookCount(string htmlContent, out string errorMessage)
{
errorMessage = string.Empty;
try
{
// HTML에서 "총 N권(개)" 패턴 찾기
var pattern = @"총\s*<strong[^>]*class=""cyan""[^>]*>(\d+)권\(개\)</strong>";
var match = Regex.Match(htmlContent, pattern, RegexOptions.IgnoreCase);
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int count))
{
errorMessage = $"검색성공({count}권)";
return count;
}
else
{
errorMessage = $"수량값오류({match.Groups[1].Value})";
return -1;
}
}
else
{
// 패턴을 찾지 못한 경우 다른 패턴으로 시도
var alternatePattern = @"총\s*<strong[^>]*>(\d+)권\(개\)</strong>";
var alternateMatch = Regex.Match(htmlContent, alternatePattern, RegexOptions.IgnoreCase);
if (alternateMatch.Success)
{
if (int.TryParse(alternateMatch.Groups[1].Value, out int count))
{
errorMessage = $"검색성공({count}권)";
return count;
}
}
errorMessage = "검색결과 패턴을 찾을 수 없음";
return -1;
}
}
catch (Exception ex)
{
errorMessage = $"결과 분석 오류: {ex.Message}";
return -1;
}
}
public Task WaitForPageChange(WebDriverWait wait)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,6 +1,22 @@
namespace UniMarc.Properties {
using System.Security.Permissions;
namespace UniMarc.Properties {
public class UserSetting : AR.Setting
{
public string LastSearchTarget { get; set; }
public string LastSearchTargetDLS { get; set; }
public override void AfterLoad()
{
}
public override void AfterSave()
{
}
}
// 이 클래스를 사용하여 설정 클래스에 대한 특정 이벤트를 처리할 수 있습니다.
// SettingChanging 이벤트는 설정 값이 변경되기 전에 발생합니다.
// PropertyChanged 이벤트는 설정 값이 변경된 후에 발생합니다.

View File

@@ -225,13 +225,27 @@
<DependentUpon>Reference.svcmap</DependentUpon>
</Compile>
<Compile Include="PUB.cs" />
<Compile Include="SearchModel\AnsanLibSearcher.cs" />
<Compile Include="SearchModel\BookSearchService.cs" />
<Compile Include="SearchModel\BukguLibSearcher.cs" />
<Compile Include="SearchModel\BukguPublicLibSearcher.cs" />
<Compile Include="SearchModel\BukguSlibSearcher.cs" />
<Compile Include="SearchModel\DLSSearcher.cs" />
<Compile Include="SearchModel\DownloadProgressForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="SearchModel\GoheungLibSearcher.cs" />
<Compile Include="SearchModel\GwangjuCityLibSearcher.cs" />
<Compile Include="SearchModel\GwangsanLibSearcher.cs" />
<Compile Include="SearchModel\IksanLibSearcher.cs" />
<Compile Include="SearchModel\ILibrarySearcher.cs" />
<Compile Include="SearchModel\KwangjuCityLibrarySearcher.cs" />
<Compile Include="SearchModel\JeonbukEduLibSearcher.cs" />
<Compile Include="SearchModel\MokpoLibSearcher.cs" />
<Compile Include="SearchModel\SuncheonLibSearcher.cs" />
<Compile Include="SearchModel\WandoLibSearcher.cs" />
<Compile Include="SearchModel\YeosuLibSearcher.cs" />
<Compile Include="SearchModel\JunnamEduJiheaNuriSearcher.cs" />
<Compile Include="SearchModel\KwangjuCityEduLibrarySearcher.cs" />
<Compile Include="SearchModel\JunnamEduSearcher.cs" />
<Compile Include="SearchModel\NamguLibrarySearcher.cs" />
<Compile Include="SearchModel\SeleniumHelper.cs" />

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishUrlHistory>E:\UniMarcApplicationUpdateFiles\|ftp://ftpgloria%401.215.250.130/|ftp://ftpgloria%401.215.250.130:50005/|sftp://ftpgloria%401.215.250.130/|ftp://ftpgloria%401.215.250.130/unimarc/</PublishUrlHistory>

View File

@@ -68,7 +68,7 @@ namespace UniMarc.마크
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.ReadOnly = true;
this.dataGridView1.RowTemplate.Height = 23;
this.dataGridView1.Size = new System.Drawing.Size(436, 186);
this.dataGridView1.Size = new System.Drawing.Size(464, 583);
this.dataGridView1.TabIndex = 1;
this.dataGridView1.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellDoubleClick);
this.dataGridView1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.dataGridView1_KeyDown);
@@ -96,7 +96,7 @@ namespace UniMarc.마크
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
this.panel1.Location = new System.Drawing.Point(0, 0);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(436, 32);
this.panel1.Size = new System.Drawing.Size(464, 32);
this.panel1.TabIndex = 2;
//
// btn_Close
@@ -144,14 +144,14 @@ namespace UniMarc.마크
this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel2.Location = new System.Drawing.Point(0, 32);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(436, 186);
this.panel2.Size = new System.Drawing.Size(464, 583);
this.panel2.TabIndex = 3;
//
// Check_Copy_Sub_List
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(436, 218);
this.ClientSize = new System.Drawing.Size(464, 615);
this.Controls.Add(this.panel2);
this.Controls.Add(this.panel1);
this.Name = "Check_Copy_Sub_List";

View File

@@ -133,7 +133,6 @@ namespace UniMarc.마크
{
var cc2 = cc as Check_copyWD;
cc2.btn_SearchList.Text = list_name;
cc2.SearchCount.Value = cc2.dv1.Rows.Count - 1;
}

View File

@@ -67,7 +67,7 @@ namespace UniMarc.마크
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.ReadOnly = true;
this.dataGridView1.RowTemplate.Height = 23;
this.dataGridView1.Size = new System.Drawing.Size(514, 261);
this.dataGridView1.Size = new System.Drawing.Size(514, 606);
this.dataGridView1.TabIndex = 0;
this.dataGridView1.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellDoubleClick);
this.dataGridView1.RowPostPaint += new System.Windows.Forms.DataGridViewRowPostPaintEventHandler(this.dataGridView1_RowPostPaint);
@@ -119,7 +119,7 @@ namespace UniMarc.마크
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(514, 261);
this.ClientSize = new System.Drawing.Size(514, 606);
this.Controls.Add(this.dataGridView1);
this.Name = "Check_Copy_Sub_Search";
this.Text = "도서관 검색";

View File

@@ -42,9 +42,7 @@
this.btn_SearchList = new System.Windows.Forms.Button();
this.btn_Start = new System.Windows.Forms.Button();
this.btn_Stop = new System.Windows.Forms.Button();
this.SearchCount = new System.Windows.Forms.NumericUpDown();
this.btn_Close = new System.Windows.Forms.Button();
this.label2 = new System.Windows.Forms.Label();
this.tb_SearchTarget = new System.Windows.Forms.ComboBox();
this.btSearchLibrary = new System.Windows.Forms.Label();
this.panel2 = new System.Windows.Forms.Panel();
@@ -61,16 +59,15 @@
this.panel6 = new System.Windows.Forms.Panel();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.lblStatus = new System.Windows.Forms.ToolStripLabel();
this.lbSite = new System.Windows.Forms.ToolStripStatusLabel();
this.panel4 = new System.Windows.Forms.Panel();
this.chk_RemoveBrit = new System.Windows.Forms.CheckBox();
this.btn_ResultEmpty = new System.Windows.Forms.Button();
this.btn_GridReset = new System.Windows.Forms.Button();
this.btn_OpenMemo = new System.Windows.Forms.Button();
this.chk_spChar = new System.Windows.Forms.CheckBox();
this.lbSite = new System.Windows.Forms.ToolStripStatusLabel();
this.panel1.SuspendLayout();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.SearchCount)).BeginInit();
this.panel2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dv1)).BeginInit();
this.panel3.SuspendLayout();
@@ -228,19 +225,6 @@
this.btn_Stop.UseVisualStyleBackColor = true;
this.btn_Stop.Click += new System.EventHandler(this.btn_Stop_Click);
//
// SearchCount
//
this.SearchCount.Location = new System.Drawing.Point(491, 6);
this.SearchCount.Maximum = new decimal(new int[] {
10000,
0,
0,
0});
this.SearchCount.Name = "SearchCount";
this.SearchCount.Size = new System.Drawing.Size(54, 21);
this.SearchCount.TabIndex = 2;
this.SearchCount.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// btn_Close
//
this.btn_Close.Location = new System.Drawing.Point(548, 4);
@@ -251,23 +235,15 @@
this.btn_Close.UseVisualStyleBackColor = true;
this.btn_Close.Click += new System.EventHandler(this.btn_Close_Click);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(444, 10);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(45, 12);
this.label2.TabIndex = 0;
this.label2.Text = "검색 수";
//
// tb_SearchTarget
//
this.tb_SearchTarget.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
this.tb_SearchTarget.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
this.tb_SearchTarget.Enabled = false;
this.tb_SearchTarget.ImeMode = System.Windows.Forms.ImeMode.Hangul;
this.tb_SearchTarget.Location = new System.Drawing.Point(66, 6);
this.tb_SearchTarget.Name = "tb_SearchTarget";
this.tb_SearchTarget.Size = new System.Drawing.Size(243, 20);
this.tb_SearchTarget.Size = new System.Drawing.Size(390, 20);
this.tb_SearchTarget.TabIndex = 1;
this.tb_SearchTarget.SelectedIndexChanged += new System.EventHandler(this.tb_SearchTarget_SelectedIndexChanged);
this.tb_SearchTarget.KeyDown += new System.Windows.Forms.KeyEventHandler(this.tb_SearchTarget_KeyDown);
@@ -288,11 +264,9 @@
this.panel2.Controls.Add(this.btn_SiteDenote);
this.panel2.Controls.Add(this.lbl_PW);
this.panel2.Controls.Add(this.lbl_ID);
this.panel2.Controls.Add(this.SearchCount);
this.panel2.Controls.Add(this.tb_SearchTarget);
this.panel2.Controls.Add(this.btSearchLibrary);
this.panel2.Controls.Add(this.btn_Close);
this.panel2.Controls.Add(this.label2);
this.panel2.Dock = System.Windows.Forms.DockStyle.Top;
this.panel2.Location = new System.Drawing.Point(0, 0);
this.panel2.Name = "panel2";
@@ -301,7 +275,7 @@
//
// btn_SiteDenote
//
this.btn_SiteDenote.Location = new System.Drawing.Point(317, 5);
this.btn_SiteDenote.Location = new System.Drawing.Point(462, 5);
this.btn_SiteDenote.Name = "btn_SiteDenote";
this.btn_SiteDenote.Size = new System.Drawing.Size(77, 23);
this.btn_SiteDenote.TabIndex = 4;
@@ -432,6 +406,12 @@
this.lblStatus.Size = new System.Drawing.Size(27, 20);
this.lblStatus.Text = "WD";
//
// lbSite
//
this.lbSite.Name = "lbSite";
this.lbSite.Size = new System.Drawing.Size(55, 17);
this.lbSite.Text = "-- Site --";
//
// panel4
//
this.panel4.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
@@ -497,12 +477,6 @@
this.chk_spChar.Text = "특수문자 제거";
this.chk_spChar.UseVisualStyleBackColor = true;
//
// lbSite
//
this.lbSite.Name = "lbSite";
this.lbSite.Size = new System.Drawing.Size(55, 17);
this.lbSite.Text = "-- Site --";
//
// Check_copyWD
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
@@ -510,14 +484,13 @@
this.ClientSize = new System.Drawing.Size(637, 738);
this.Controls.Add(this.panel3);
this.Name = "Check_copyWD";
this.Text = "복본조사(WebDriver)";
this.Text = "복본조사(New)";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Check_copy_FormClosing);
this.Load += new System.EventHandler(this.Check_copy_Load);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.SearchCount)).EndInit();
this.panel2.ResumeLayout(false);
this.panel2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dv1)).EndInit();
@@ -536,7 +509,6 @@
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label btSearchLibrary;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Button btn_Close;
private System.Windows.Forms.Button btn_Stop;
@@ -550,7 +522,6 @@
private System.Windows.Forms.Panel panel4;
private System.Windows.Forms.CheckBox chk_spChar;
private System.Windows.Forms.CheckBox chk_RemoveBrit;
public System.Windows.Forms.NumericUpDown SearchCount;
private System.Windows.Forms.RadioButton rb_isNumber;
private System.Windows.Forms.RadioButton rb_isHave;
private System.Windows.Forms.Button btn_GridReset;

View File

@@ -10,6 +10,7 @@ using BokBonCheck;
using AR;
using UniMarc.SearchModel;
using System.Linq;
using UniMarc;
namespace WindowsFormsApp1.Mac
{
@@ -22,6 +23,15 @@ namespace WindowsFormsApp1.Mac
public string SearcherNo;
private bool isStop = false;
Main main;
bool GetDemoRunAuth
{
get
{
return (DateTime.Now <= new DateTime(2025, 09, 30));
}
}
public Check_copyWD(Main _main)
{
InitializeComponent();
@@ -32,56 +42,307 @@ namespace WindowsFormsApp1.Mac
//도서검색(크롤링)
_searchService = new BookSearchService();
_searchService.AddSearcher(new NamguLibrarySearcher_Munhwa(idx++));
_searchService.AddSearcher(new NamguLibrarySearcher_Children(idx++));
_searchService.AddSearcher(new NamguLibrarySearcher_Smart(idx++));
_searchService.AddSearcher(new NamguLibrarySearcher_Purungil(idx++));
_searchService.AddSearcher(new NamguLibrarySearcher_Hyocheon(idx++));
_searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libMA", "문화정보도서관"));
_searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libMC", "청소년도서관"));
_searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libSQ", "스마트도서관"));
_searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libMB", "푸른길도서관"));
_searchService.AddSearcher(new NamguLibrarySearcher(idx++, "#libSW", "효천어울림도서관"));
//광주시청도서광
idx = 30;
_searchService.AddSearcher(new KwangjuCityLibrarySearcher_central(idx++));
_searchService.AddSearcher(new KwangjuCityLibrarySearcher_choisangjun(idx++));
_searchService.AddSearcher(new KwangjuCityLibrarySearcher_dagachi(idx++));
_searchService.AddSearcher(new KwangjuCityLibrarySearcher_Indi(idx++));
_searchService.AddSearcher(new KwangjuCityLibrarySearcher_Student(idx++));
_searchService.AddSearcher(new KwangjuCityLibrarySearcher_Kumho(idx++));
//전남교육청통합도서관
idx = 50;
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "ALL", "전체"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146010", "목포"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146007", "나주"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146006", "남평"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146025", "광양"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146008", "담양"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146005", "구례"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146012", "보성"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146013", "벌교"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146023", "화순"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146019", "장흥"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146022", "해남"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146017", "영암"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146011", "무안"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146021", "함평"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146016", "영광"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146018", "장성"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146020", "진도"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146003", "곡성"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146059", "학생교육문화회관"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146042", "광양평생"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146002", "고흥평생"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00346002", "교육연수원"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00324008", "교육연구정보원"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00346030", "전라남도국제교육원"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146180", "순천만생태문화교육원"));
if (GetDemoRunAuth == true)
{
//광주시교육청통합도서관
idx = 50;
_searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MD", "중앙도서관"));
_searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MF", "분관최상준도서관"));
_searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "ME", "송정다가치문화도서관"));
_searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MA", "학생독립운동기념회관"));
_searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MC", "학생교육문화회관"));
_searchService.AddSearcher(new KwangjuCityEduLibrarySearcher(idx++, "MB", "금호평생교육관"));
//전남교육청통합도서관
idx = 100;
//_searchService.AddSearcher(new JunnamEduSearcher(idx++, "ALL", "전체"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146010", "목포"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146007", "나주"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146006", "남평"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146025", "광양"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146008", "담양"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146005", "구례"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146012", "보성"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146013", "벌교"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146023", "화순"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146019", "장흥"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146022", "해남"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146017", "영암"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146011", "무안"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146021", "함평"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146016", "영광"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146018", "장성"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146020", "진도"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146003", "곡성"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146059", "학생교육문화회관"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146042", "광양평생"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146002", "고흥평생"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00346002", "교육연수원"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00324008", "교육연구정보원"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00346030", "전라남도국제교육원"));
_searchService.AddSearcher(new JunnamEduSearcher(idx++, "00146180", "순천만생태문화교육원"));
//전남교육청행정자료실
idx = 150;
_searchService.AddSearcher(new JunnamEduJiheaNuriSearcher(idx++, "00324010", "지혜누리"));
//여수시립
idx = 200;
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "MG", "여수이순신도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "MA", "여수시립쌍봉도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "MB", "여수시립현암도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "MC", "여수시립환경도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "MD", "여수시립돌산도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "ME", "여수시립소라도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "MF", "여수시립율촌도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "PA", "거문도은빛바다도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "PB", "치매안심센터작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "PC", "청솔글누리작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "PD", "동부도시보건작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "PE", "화양열린작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "PF", "여문늘벗작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "PH", "국동작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SA", "아주타운아파트작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SB", "책이랑나랑작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SC", "현천작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SE", "꿈꾸는영어전문작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SF", "학마을작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SG", "웅천지웰작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SH", "한려작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SJ", "주은금고작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SK", "광림작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SM", "민들레작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SO", "원앙작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SP", "푸른정원작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SR", "로얄골드빌작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SS", "꿈을키우는작은도서관"));
//_searchService.AddSearcher(new YeosuLibSearcher(idx++, "ST", "KLAS MANAGEMENT"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SY", "신기부영작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "SZ", "국동365열린도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "TA", "지웰2차 작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "TB", "이편한 작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "TC", "채움늘 작은도서관"));
_searchService.AddSearcher(new YeosuLibSearcher(idx++, "TD", "웅천글꽃 작은도서관"));
//고흥군립
idx = 300;
_searchService.AddSearcher(new GoheungLibSearcher(idx++, "MA", "고흥군립중앙도서관"));
_searchService.AddSearcher(new GoheungLibSearcher(idx++, "MB", "고흥군립남부도서관"));
_searchService.AddSearcher(new GoheungLibSearcher(idx++, "MC", "고흥군립북부도서관"));
_searchService.AddSearcher(new GoheungLibSearcher(idx++, "MD", "과역작은도서관"));
_searchService.AddSearcher(new GoheungLibSearcher(idx++, "ME", "나로한빛 작은도서관"));
_searchService.AddSearcher(new GoheungLibSearcher(idx++, "MF", "도화푸른꿈 작은도서관"));
_searchService.AddSearcher(new GoheungLibSearcher(idx++, "MG", "포두작은도서관"));
//광주북구 통합도서관
idx = 400;
_searchService.AddSearcher(new BukguLibSearcher(idx++, "101", "스마트도서관1호점"));
_searchService.AddSearcher(new BukguLibSearcher(idx++, "102", "스마트도서관2호점"));
_searchService.AddSearcher(new BukguLibSearcher(idx++, "105", "스마트도서관3호점"));
//광주북구 작은도서관
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "729038", "꿈나무작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129094", "동림동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "729046", "두암1동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129069", "두암2동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129068", "두암3동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129056", "매곡동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129074", "문흥2동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129054", "삼각동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "729068", "시화문화마을작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129057", "신안동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129220", "양산동주민작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129085", "오치1동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129070", "오치2동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129055", "용봉작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129064", "운암1동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129071", "운암2동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129058", "운암3동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "729175", "임동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "729085", "중앙동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129075", "중흥1동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "729155", "징검다리작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "129065", "풍향동작은도서관"));
_searchService.AddSearcher(new BukguSlibSearcher(idx++, "729154", "햇살마루작은도서관"));
//광주북구 공공도서관
_searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "124010", "일곡도서관"));
_searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "129131", "운암도서관"));
_searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "129225", "양산도서관"));
_searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "129226", "중흥도서관"));
_searchService.AddSearcher(new BukguPublicLibSearcher(idx++, "129227", "신용도서관"));
//전북교육청도서관
idx = 700;
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MV", "고창도서관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MB", "군산학생교육문화관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MC", "군산학생교육문화관 대야분관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MH", "김제학생교육문화관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MJ", "김제학생교육문화관 금산분관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MF", "남원학생교육문화관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MG", "남원학생교육문화관운봉분관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MR", "무주도서관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MK", "부안학생교육문화관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MU", "순창도서관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MP", "완주도서관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MD", "익산학생교육문화관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_ME", "익산학생교육문화관 함열분관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MT", "임실도서관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MS", "장수도서관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MA", "전주학생교육문화관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MQ", "진안도서관"));
_searchService.AddSearcher(new JeonbukEduLibSearcher(idx++, "lib_MN", "정읍학생복지회관"));
//광주광산구 통합도서관
idx = 800;
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "JM", "장덕도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "MB", "이야기꽃도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "MA", "첨단도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "BR", "신가도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "WN", "운남어린이도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "MC", "스마트도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CA", "첨단빛누리작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CB", "사랑나눔작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CC", "혜윰마루작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CD", "책놀터작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CE", "우산동작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CF", "더불어락작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CG", "임곡꿈쟁이작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CH", "유노윤호HUG작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CI", "늘품작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CJ", "왁자지껄수다놀이터작은도서관"));
_searchService.AddSearcher(new GwangsanLibSearcher(idx++, "CK", "송정다누리작은도서관"));
//목포시립도서관
idx = 900;
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MA", "목포시립도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MX", "목포어울림도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MJ", "목포어린이도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MQ", "목포영어도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "ME", "꿈나무작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MB", "꿈돌이작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MS", "노을작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MW", "늘푸른작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MH", "무지개작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MF", "반딧불작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MP", "삼학작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MV", "샛별작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MO", "소나무작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MN", "숲속작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MK", "연꽃작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MD", "옹달샘작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MC", "은하수작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MM", "청개구리작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MI", "초롱초롱작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "ML", "푸른솔작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MU", "푸른숲작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MR", "행복마을작은도서관"));
_searchService.AddSearcher(new MokpoLibSearcher(idx++, "MT", "호돌이작은도서관"));
//순천시립도서관
idx = 1000;
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MA", "삼산도서관"));
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MB", "그림책도서관"));
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "BR", "연향도서관"));
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MH", "기적의도서관"));
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MC", "조례호수도서관"));
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MJ", "신대도서관"));
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MK", "어울림도서관"));
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MD", "해룡농어촌도서관"));
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "MG", "풍덕글마루도서관"));
_searchService.AddSearcher(new SuncheonLibSearcher(idx++, "mini", "작은도서관"));
//광주시립도서관
idx = 1100;
_searchService.AddSearcher(new GwangjuCityLibSearcher(idx++, "124003", "무등도서관"));
_searchService.AddSearcher(new GwangjuCityLibSearcher(idx++, "124004", "사직도서관"));
_searchService.AddSearcher(new GwangjuCityLibSearcher(idx++, "124008", "산수도서관"));
_searchService.AddSearcher(new GwangjuCityLibSearcher(idx++, "129228", "디지털정보도서관"));
// 완도군립도서관
idx = 1200;
_searchService.AddSearcher(new WandoLibSearcher(idx++, "MA", "완도군립도서관"));
_searchService.AddSearcher(new WandoLibSearcher(idx++, "MB", "노화공공도서관"));
_searchService.AddSearcher(new WandoLibSearcher(idx++, "MC", "금일공공도서관"));
_searchService.AddSearcher(new WandoLibSearcher(idx++, "MD", "보길윤선도작은도서관"));
_searchService.AddSearcher(new WandoLibSearcher(idx++, "ME", "군외동백숲작은도서관"));
_searchService.AddSearcher(new WandoLibSearcher(idx++, "MF", "도담도담작은도서관"));
// 익산시통합도서관
idx = 1300;
_searchService.AddSearcher(new IksanLibSearcher(idx++, "MO", "모현도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "BR", "영등도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "MA", "마동도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "BU", "부송도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "MC", "유천도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "HW", "황등도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "MB", "금마도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SX", "모인여행숲도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SW", "수도산그림책숲도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "UQ", "스마트도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SI", "글마루작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SD", "깊은샘작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SE", "꿈꾸는뜰작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SO", "꿈드림작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SK", "낭산작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SB", "동산작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SJ", "모현뜰작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SH", "배산작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SC", "봄나루작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SF", "부송작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SA", "삼성동어린이도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SQ", "어양작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SP", "어울림작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SG", "예솔작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SL", "왕궁작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SN", "울림작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SM", "행복세상작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SR", "그루터기작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SS", "늘봄작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SU", "망성아담작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SV", "함라등대작은도서관"));
_searchService.AddSearcher(new IksanLibSearcher(idx++, "SY", "무학정원작은도서관"));
// 안산시중앙도서관
//idx = 1400;
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MA", "중앙도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MD", "감골도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MB", "관산도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "ME", "단원어린이도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "ND", "대부도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "NF", "미디어도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MH", "반월도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MX", "본오도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "ML", "부곡도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "NK", "상록수도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MF", "상록어린이도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "NC", "선부도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MC", "성포도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MS", "수암도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "MR", "원고잔도서관"));
//_searchService.AddSearcher(new AnsanLibSearcher(idx++, "NJ", "월피예술도서관"));
}
this.tb_SearchTarget.Items.Clear();
// this.tb_SearchTarget.Items.Add("-- 검색대상을 선택하세요 --");
foreach (var item in _searchService.GetAvailableSites().OrderBy(t=>t))
// this.tb_SearchTarget.Items.Add("-- 검색대상을 선택하세요 --");
foreach (var item in _searchService.GetAvailableSites().OrderBy(t => t))
tb_SearchTarget.Items.Add(item);
if (PUB.setting.LastSearchTarget.isEmpty() == false)
tb_SearchTarget.Text = PUB.setting.LastSearchTarget;
this.Text = $"복본조사(New:{_searchService.GetAvailableSites().Count})";
}
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
@@ -97,6 +358,7 @@ namespace WindowsFormsApp1.Mac
//크롤링
InitializeChromeDriver();
tb_SearchTarget.Enabled = true;
}
@@ -220,10 +482,9 @@ namespace WindowsFormsApp1.Mac
{
Skill_Grid sg = new Skill_Grid();
sg.Excel_to_DataGridView(sender, e);
SearchCount.Value = dv1.Rows.Count;
}
async private void btn_Start_Click(object sender, EventArgs e)
{
var searcher = this.GetSearcher();
@@ -233,14 +494,9 @@ namespace WindowsFormsApp1.Mac
return;
}
SearchCount.Value = dv1.Rows.Count; //250619
RowCount = 0; //250619
dv1.CurrentCell = dv1.Rows[0].Cells[0];
int SCount = Convert.ToInt32(SearchCount.Value);
if (DateTime.Now > new DateTime(2025, 09, 30))
if (GetDemoRunAuth == false)
{
UTIL.MsgE("테스트 기간이 종료되었습니다\n개발자 문의 하세요");
return;
@@ -254,10 +510,12 @@ namespace WindowsFormsApp1.Mac
try
{
PUB.setting.LastSearchTarget = this.tb_SearchTarget.Text.Trim();
PUB.setting.Save();
this._isSearching = true;
btn_Start.Enabled = false;
btn_Stop.Enabled = false;
btn_Stop.Enabled = true;
await searcher.StartDriver(this.chkShowBrowser.Checked);
byte searchopt = 0; //전체검색
if (radTargetEmpty.Checked) searchopt = 1;
@@ -278,7 +536,7 @@ namespace WindowsFormsApp1.Mac
if (this._isSearching == false)
{
drow.Cells["dvc_remark"].Value = "Cancel";
continue;
break;
}
dv1.CurrentCell = drow.Cells[0];
@@ -340,7 +598,7 @@ namespace WindowsFormsApp1.Mac
}
}
if (retry == false && chkRetryErrData.Checked)
if (_isSearching == true && retry == false && chkRetryErrData.Checked)
{
retry = true;
radTargetErr.Checked = true;
@@ -371,11 +629,8 @@ namespace WindowsFormsApp1.Mac
btn_Start.Enabled = true;
this._isSearching = false;
}
}
int RowCount = 0;
/// <summary>
@@ -521,7 +776,7 @@ namespace WindowsFormsApp1.Mac
dv1.Rows[a].DefaultCellStyle.ForeColor = Color.Black;
dv1.Rows[a].DefaultCellStyle.BackColor = Color.White;
dv1.Rows[a].Cells["Count"].Value = "";
RowCount = 0;
dv1.Rows[a].Cells["dvc_remark"].Value = "";
}
}
@@ -540,7 +795,7 @@ namespace WindowsFormsApp1.Mac
{
if (this.tb_SearchTarget.SelectedIndex < 0) return null;
return this._searchService.Get(tb_SearchTarget.Text);
}
private void btn_SiteDenote_Click(object sender, EventArgs e)
@@ -567,10 +822,12 @@ namespace WindowsFormsApp1.Mac
var searcher = this._searchService.Get(this.tb_SearchTarget.Text);
if (searcher == null)
{
chkShowBrowser.Enabled = true;
lbSite.Text = "-- site --";
return;
}
this.lbSite.Text = searcher.SiteUrl;
chkShowBrowser.Enabled = !searcher.HttpApiMode;
this.lbSite.Text = searcher.SiteUrl;
}
}
}

View File

@@ -9,6 +9,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using UniMarc;
using UniMarc.SearchModel;
namespace WindowsFormsApp1.Mac
@@ -179,6 +180,8 @@ namespace WindowsFormsApp1.Mac
btn_Search.Enabled = true;
PUB.setting.LastSearchTargetDLS = tb_SearchClient.Text.Trim();
PUB.setting.Save();
//로그인
_searcher = new DLSSearcher(50);