This commit is contained in:
2025-08-13 18:39:23 +09:00
parent 0c190e112f
commit f3715253a8
9 changed files with 450 additions and 382 deletions

View File

@@ -1,7 +1,8 @@
{
"permissions": {
"allow": [
"Bash(git add:*)"
"Bash(git add:*)",
"WebFetch(domain:jnelib.jne.go.kr)"
],
"deny": [],
"ask": []

View File

@@ -36,6 +36,12 @@ namespace BokBonCheck
{
return _searchers.Where(t => t.No == no).FirstOrDefault();
}
public ILibrarySearcher Get(string no)
{
return _searchers.Where(t => t.SiteName == no).FirstOrDefault();
}
public void AddSearcher(ILibrarySearcher searcher)
{
if (!_searchers.Any(s => s.SiteName == searcher.SiteName))

View File

@@ -17,56 +17,21 @@ using System.Runtime.CompilerServices;
namespace BokBonCheck
{
public class JunnamEduSearcher_Mokpo : JunnamEduSearcher
{
public JunnamEduSearcher_Mokpo(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 JunnamEduSearcher : ILibrarySearcher
{
public string SiteName { get; protected set; } = "전남교육청(전체)";
public string SiteUrl => "https://lib.namgu.gwangju.kr/main/bookSearch";
public 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 int No { get; set; }
private ChromiumDriver _driver;
public JunnamEduSearcher(int no)
public JunnamEduSearcher(int no, string areaCode, string areaName)
{
this.No = no;
this.Areacode = areaCode;
this.SiteName = $"전남교육청({areaName})";
}
@@ -88,7 +53,7 @@ namespace BokBonCheck
try
{
if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download();
_driver = await SeleniumHelper.CreateDriver();
_driver = await SeleniumHelper.CreateDriver(ShowBrowser: showdriver);
Console.WriteLine("NamguLibrarySearcher Driver 초기화 완료");
}
catch (Exception ex)
@@ -103,21 +68,70 @@ namespace BokBonCheck
virtual protected 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 == false)
// Areacode가 "ALL"인 경우
if (Areacode == "ALL")
{
SafeClick(searchBox);
var allCheckbox = wait.Until(d => d.FindElement(By.CssSelector("#all_srch")));
// 이미 전체가 선택되어 있으면 변경하지 않음
if (allCheckbox.Selected)
{
Console.WriteLine("전체 선택이 이미 활성화되어 있음");
return true;
}
// 전체 선택이 안되어 있으면 활성화
SafeClick(allCheckbox);
Thread.Sleep(300);
Console.WriteLine("전체 선택으로 변경됨");
return true;
}
// 특정 지역 선택인 경우
var targetSelector = $"#libInfo_{Areacode}";
var targetCheckbox = wait.Until(d => d.FindElement(By.CssSelector(targetSelector)));
if (targetCheckbox == null)
{
Console.WriteLine($"도서관 체크박스를 찾을 수 없습니다: {targetSelector}");
return false;
}
// 현재 상태 확인
var allCheckbox = wait.Until(d => d.FindElement(By.CssSelector("#all_srch")));
bool isAllSelected = allCheckbox.Selected;
bool isTargetSelected = targetCheckbox.Selected;
// 이미 원하는 지역이 선택되어 있고, 전체 선택이 해제되어 있으면 변경하지 않음
if (isTargetSelected && !isAllSelected)
{
Console.WriteLine($"{Areacode} 지역이 이미 선택되어 있음");
return true;
}
// 전체 선택이 되어 있으면 해제
if (isAllSelected)
{
SafeClick(allCheckbox);
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);
Console.WriteLine($"도서관 선택 실패: {ex.Message}");
return false;
}
}
@@ -203,31 +217,21 @@ namespace BokBonCheck
IWebElement searchBox = null;
try
{
// 여러 가능한 선택자 시도
var selectors = new[]
{
"input[name='query']",
"input[id='query']",
"input[type='text']",
};
foreach (var selector in selectors)
try
{
try
{
searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
break;
}
catch
{
continue;
}
searchBox = wait.Until(d => d.FindElement(By.CssSelector("#searchWordText")));
}
catch (Exception ex)
{
result.ErrorMessage = $"검색창없음({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
if (searchBox == null)
{
throw new Exception("검색창을 찾을 수 없습니다.");
}
}
catch (Exception ex)
{
@@ -242,45 +246,24 @@ namespace BokBonCheck
IWebElement searchButton = null;
try
{
var buttonSelectors = new[]
{
"button[type='submit']",
"input[type='submit']",
".search-btn",
".btn-search",
"button:contains('검색')",
"input[value*='검색']",
"button[class*='search']",
"input[class*='search']"
};
foreach (var selector in buttonSelectors)
{
try
{
searchButton = _driver.FindElement(By.CssSelector(selector));
break;
}
catch
{
continue;
}
}
if (searchButton == null)
{
// Enter 키로 검색 시도
searchBox.SendKeys(Keys.Enter);
}
searchButton = _driver.FindElement(By.CssSelector("button[type='submit']"));
if(searchButton != null)
searchButton.Click();
else
{
searchButton.Click();
result.ErrorMessage = $"검색버튼없음";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
}
catch (Exception ex)
{
// Enter 키로 검색 시도
searchBox.SendKeys(Keys.Enter);
result.ErrorMessage = $"검색버튼없음({ex.Message})";
result.BookCount = -1;
result.IsSuccess = false;
return result;
}
// 페이지 변경을 감지하는 메서드
@@ -317,44 +300,53 @@ namespace BokBonCheck
errmessage = string.Empty;
try
{
// div.search-result 내부의 span에서 '전체 N' 텍스트 추출
var resultDiv = driver.FindElement(By.CssSelector("div.search-result"));
var bodytext = resultDiv.Text;
if (bodytext.Contains("검색결과가 없습니다"))
// 먼저 검색결과가 없는 경우 확인
try
{
errmessage = "검색결과없음";
return 0;
}
var searchkey = resultDiv.FindElement(By.XPath("//*[@id=\"sub\"]/section[3]/div/div/div/div/div[2]/div[1]/p/b"));
var searchtitle = searchkey.Text;
if (searchTerm.Contains(searchtitle) == false)
{
errmessage = $"검색어불일치({searchtitle}/{searchTerm})";
return -1;
}
var span = resultDiv.FindElement(By.XPath(".//span[contains(text(),'전체')]"));
string text = span.Text; // 예: "전체 5 "
var match = System.Text.RegularExpressions.Regex.Match(text, @"전체\s*(\d+)");
if (match.Success)
{
if (int.TryParse(match.Groups[1].Value, out int vqty) == false)
var noResultDiv = driver.FindElement(By.CssSelector(".SearchResult .resultBook"));
var noResultText = noResultDiv.Text;
if (noResultText.Contains("검색결과가 없습니다"))
{
errmessage = $"수량값오류({match.Groups[1].Value})";
return -1;
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
{
searchTerm = string.Empty;
return vqty;
errmessage = "수량항목없음";
return -1;
}
}
else
catch (Exception ex)
{
errmessage = "수량항목없음";
return -1;
// page_info가 없는 경우 검색결과가 없는 것으로 판단
errmessage = "검색결과없음";
return 0;
}
}
@@ -374,27 +366,44 @@ namespace BokBonCheck
{
await Task.Delay(500);
// 방법 4: 페이지 로딩 상태 확인
// 페이지 로딩 상태 확인
wait.Until(d =>
{
var readyState = ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState");
return readyState.Equals("complete");
});
// 방법 5: 특정 텍스트가 페이지에 나타날 때까지 대기
// 검색 결과 페이지가 로드될 때까지 대기
wait.Until(d =>
{
try
{
var byclassname = By.ClassName("search-result");
var elm = d.FindElement(byclassname);
if (elm == null)
// 검색결과가 없는 경우 확인
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;
}
var pageText = elm.Text;
if (pageText.Contains("검색결과가 없습니다")) return true;
return pageText.Contains("에 대하여") && pageText.Contains("검색되었습니다");
}
catch
{

View File

@@ -89,7 +89,7 @@ namespace BokBonCheck
try
{
if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download();
_driver = await SeleniumHelper.CreateDriver();
_driver = await SeleniumHelper.CreateDriver(ShowBrowser:showBrowser);
Console.WriteLine("KwangjuCityLibrarySearcher Driver 초기화 완료");
}
catch (Exception ex)

View File

@@ -243,7 +243,7 @@ namespace BokBonCheck
try
{
if (SeleniumHelper.IsReady == false) await SeleniumHelper.Download();
_driver = await SeleniumHelper.CreateDriver();
_driver = await SeleniumHelper.CreateDriver(ShowBrowser:showdriver);
Console.WriteLine("NamguLibrarySearcher Driver 초기화 완료");
}
catch (Exception ex)

View File

@@ -198,15 +198,15 @@ namespace UniMarc.SearchModel
{
IsReady = false;
Console.WriteLine($"드라이버 생성 테스트 중 오류: {ex.Message}");
if (progressForm != null)
progressForm.UpdateProgress(100, $"테스트 실패: {ex.Message}");
return false;
}
}
public static async Task<ChromiumDriver> CreateDriver(ChromiumOptions options = null, DownloadProgressForm progressForm = null, bool ShowBrowser=false)
public static async Task<ChromiumDriver> CreateDriver(ChromiumOptions options = null, DownloadProgressForm progressForm = null, bool ShowBrowser = false)
{
if (progressForm != null)
@@ -224,25 +224,25 @@ namespace UniMarc.SearchModel
if (Browser == eBrowserType.edge)
{
// Edge 드라이버 서비스 생성 (콘솔창 숨김)
var edgeService = string.IsNullOrEmpty(DriverPath) ?
var edgeService = string.IsNullOrEmpty(DriverPath) ?
EdgeDriverService.CreateDefaultService() :
EdgeDriverService.CreateDefaultService(System.IO.Path.GetDirectoryName(DriverPath));
edgeService.HideCommandPromptWindow = true;
edgeService.SuppressInitialDiagnosticInformation = true;
driver = new EdgeDriver(edgeService, (EdgeOptions)options);
}
else if (Browser == eBrowserType.chrome)
{
// Chrome 드라이버 서비스 생성 (콘솔창 숨김)
var chromeService = string.IsNullOrEmpty(DriverPath) ?
var chromeService = string.IsNullOrEmpty(DriverPath) ?
ChromeDriverService.CreateDefaultService() :
ChromeDriverService.CreateDefaultService(System.IO.Path.GetDirectoryName(DriverPath));
chromeService.HideCommandPromptWindow = true;
chromeService.SuppressInitialDiagnosticInformation = true;
driver = new ChromeDriver(chromeService, (ChromeOptions)options);
}
@@ -285,42 +285,42 @@ namespace UniMarc.SearchModel
return null;
}
}
static ChromiumOptions CreateBaseBrowserOption(bool hideBrowser = true, int width = 800, int height = 600)
static ChromiumOptions CreateBaseBrowserOption(bool hideBrowser = true, int width = 800, int height = 600)
{
ChromiumOptions options = null;
if (Browser == eBrowserType.edge)
{
ChromiumOptions options = null;
if (Browser == eBrowserType.edge)
{
options = new EdgeOptions();
}
else if (Browser == eBrowserType.chrome)
{
options = new ChromeOptions();
}
//options.AddArgument($"--user-data-dir={userDataDir}");
options.AddArgument("--no-first-run");
options.AddArgument("--no-default-browser-check");
options.AddArgument("--disable-default-apps");
options.AddArgument("--disable-popup-blocking");
options.AddArgument("--disable-translate");
options.AddArgument("--disable-background-timer-throttling");
options.AddArgument("--disable-renderer-backgrounding");
options.AddArgument("--disable-backgrounding-occluded-windows");
options.AddArgument("--disable-client-side-phishing-detection");
options.AddArgument("--disable-sync");
options.AddArgument("--disable-extensions");
options.AddArgument("--disable-component-extensions-with-background-pages");
options.AddArgument("--disable-background-networking");
options.AddArgument("--disable-background-mode");
options.AddArgument("--no-sandbox");
options.AddArgument("--disable-dev-shm-usage");
options.AddArgument("--disable-blink-features=AutomationControlled");
options.AddArgument("--remote-debugging-port=0"); // 랜덤 포트 사용
if (hideBrowser) options.AddArgument("--headless");
options.AddArgument($"--window-size={width},{height}");
return options;
options = new EdgeOptions();
}
public static void KillExistingDrivers()
else if (Browser == eBrowserType.chrome)
{
options = new ChromeOptions();
}
//options.AddArgument($"--user-data-dir={userDataDir}");
options.AddArgument("--no-first-run");
options.AddArgument("--no-default-browser-check");
options.AddArgument("--disable-default-apps");
options.AddArgument("--disable-popup-blocking");
options.AddArgument("--disable-translate");
options.AddArgument("--disable-background-timer-throttling");
options.AddArgument("--disable-renderer-backgrounding");
options.AddArgument("--disable-backgrounding-occluded-windows");
options.AddArgument("--disable-client-side-phishing-detection");
options.AddArgument("--disable-sync");
options.AddArgument("--disable-extensions");
options.AddArgument("--disable-component-extensions-with-background-pages");
options.AddArgument("--disable-background-networking");
options.AddArgument("--disable-background-mode");
options.AddArgument("--no-sandbox");
options.AddArgument("--disable-dev-shm-usage");
options.AddArgument("--disable-blink-features=AutomationControlled");
options.AddArgument("--remote-debugging-port=0"); // 랜덤 포트 사용
if (hideBrowser) options.AddArgument("--headless");
options.AddArgument($"--window-size={width},{height}");
return options;
}
public static async Task KillExistingDriversAsync()
{
string[] processNames = { };
if (Browser == eBrowserType.chrome)
@@ -336,26 +336,33 @@ namespace UniMarc.SearchModel
if (processes.Length > 0)
{
Console.WriteLine($"기존 {processName} 프로세스 {processes.Length}개를 종료하는 중...");
var killTasks = new List<Task>();
foreach (Process process in processes)
{
try
killTasks.Add(Task.Run(() =>
{
process.Kill();
process.WaitForExit(3000);
}
catch (Exception ex)
{
Console.WriteLine($"{processName} 종료 중 오류: {ex.Message}");
}
try
{
process.Kill();
process.WaitForExit(3000);
}
catch (Exception ex)
{
Console.WriteLine($"{processName} 종료 중 오류: {ex.Message}");
}
}));
}
await Task.WhenAll(killTasks);
}
}
// 임시 WebDriver 사용자 데이터 폴더들 정리
ClearDriverCache();
await Task.Run(() => ClearDriverCache());
// 프로세스가 완전히 종료될 때까지 대기
Thread.Sleep(2000);
await Task.Delay(2000);
}
catch (Exception ex)
{
@@ -401,6 +408,11 @@ namespace UniMarc.SearchModel
}
}
public static void KillExistingDrivers()
{
KillExistingDriversAsync().GetAwaiter().GetResult();
}
public static void Dispose()
{
try
@@ -412,7 +424,25 @@ namespace UniMarc.SearchModel
{
Console.WriteLine($"Exception-Dispose={ex.Message}");
}
finally {
finally
{
IsReady = false;
}
}
public static async Task DisposeAsync()
{
try
{
await KillExistingDriversAsync();
ClearDriverCache();
}
catch (Exception ex)
{
Console.WriteLine($"Exception-DisposeAsync={ex.Message}");
}
finally
{
IsReady = false;
}
}

View File

@@ -28,7 +28,7 @@
/// </summary>
private void InitializeComponent()
{
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
this.panel1 = new System.Windows.Forms.Panel();
this.chkShowBrowser = new System.Windows.Forms.CheckBox();
this.chkRetryErrData = new System.Windows.Forms.CheckBox();
@@ -45,8 +45,8 @@
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.TextBox();
this.btSearchLibrary = new System.Windows.Forms.Button();
this.tb_SearchTarget = new System.Windows.Forms.ComboBox();
this.btSearchLibrary = new System.Windows.Forms.Label();
this.panel2 = new System.Windows.Forms.Panel();
this.btn_SiteDenote = new System.Windows.Forms.Button();
this.lbl_PW = new System.Windows.Forms.Label();
@@ -67,6 +67,7 @@
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();
@@ -98,6 +99,8 @@
// chkShowBrowser
//
this.chkShowBrowser.AutoSize = true;
this.chkShowBrowser.Checked = true;
this.chkShowBrowser.CheckState = System.Windows.Forms.CheckState.Checked;
this.chkShowBrowser.Location = new System.Drawing.Point(515, 81);
this.chkShowBrowser.Name = "chkShowBrowser";
this.chkShowBrowser.Size = new System.Drawing.Size(96, 16);
@@ -259,19 +262,22 @@
//
// tb_SearchTarget
//
this.tb_SearchTarget.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
this.tb_SearchTarget.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
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, 21);
this.tb_SearchTarget.Size = new System.Drawing.Size(243, 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);
//
// btSearchLibrary
//
this.btSearchLibrary.AutoSize = true;
this.btSearchLibrary.Location = new System.Drawing.Point(3, 5);
this.btSearchLibrary.Location = new System.Drawing.Point(7, 10);
this.btSearchLibrary.Name = "btSearchLibrary";
this.btSearchLibrary.Size = new System.Drawing.Size(63, 23);
this.btSearchLibrary.Size = new System.Drawing.Size(53, 12);
this.btSearchLibrary.TabIndex = 0;
this.btSearchLibrary.Text = "검색대상";
this.btSearchLibrary.Click += new System.EventHandler(this.btSearchLibrary_Click);
@@ -327,14 +333,14 @@
this.dv1.BackgroundColor = System.Drawing.SystemColors.Control;
this.dv1.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dv1.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
dataGridViewCellStyle1.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText;
dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
this.dv1.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Control;
dataGridViewCellStyle2.Font = new System.Drawing.Font("굴림", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.WindowText;
dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
this.dv1.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle2;
this.dv1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dv1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.book_name,
@@ -412,7 +418,8 @@
// statusStrip1
//
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.lblStatus});
this.lblStatus,
this.lbSite});
this.statusStrip1.Location = new System.Drawing.Point(0, 542);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(637, 22);
@@ -490,6 +497,12 @@
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);
@@ -522,13 +535,13 @@
#endregion
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Button btSearchLibrary;
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;
private System.Windows.Forms.Button btn_Start;
public System.Windows.Forms.TextBox tb_SearchTarget;
public System.Windows.Forms.ComboBox tb_SearchTarget;
public System.Windows.Forms.DataGridView dv1;
public System.Windows.Forms.Button btn_SearchList;
private System.Windows.Forms.Button btn_ApplyFilter;
@@ -559,5 +572,6 @@
public System.Windows.Forms.CheckBox chkRetryErrData;
public System.Windows.Forms.GroupBox groupBox1;
public System.Windows.Forms.CheckBox chkShowBrowser;
private System.Windows.Forms.ToolStripStatusLabel lbSite;
}
}

View File

@@ -9,6 +9,7 @@ using UniMarc.마크;
using BokBonCheck;
using AR;
using UniMarc.SearchModel;
using System.Linq;
namespace WindowsFormsApp1.Mac
{
@@ -30,7 +31,7 @@ namespace WindowsFormsApp1.Mac
var idx = 1;
//도서검색(크롤링)
_searchService = new BookSearchService();
_searchService.AddSearcher(new NamguLibrarySearcher_Munhwa(idx++));
_searchService.AddSearcher(new NamguLibrarySearcher_Children(idx++));
_searchService.AddSearcher(new NamguLibrarySearcher_Smart(idx++));
@@ -48,30 +49,39 @@ namespace WindowsFormsApp1.Mac
//전남교육청통합도서관
idx = 50;
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //목포
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //나주
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //남평
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //광양
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //구례
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //보성
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //벌교
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //화순
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //장흥
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //해남
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //영암
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //무안
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //함평
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //영광
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //장성
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //진도
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //곡성
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //학생교육문화원
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //광양평생
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //고흥평색
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //교육연수원
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //교육연구정보원
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //전라남도국제교육원
_searchService.AddSearcher(new JunnamEduSearcher_Mokpo(idx++)); //순천만생태문화교육원
_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", "순천만생태문화교육원"));
this.tb_SearchTarget.Items.Clear();
// this.tb_SearchTarget.Items.Add("-- 검색대상을 선택하세요 --");
foreach (var item in _searchService.GetAvailableSites().OrderBy(t=>t))
tb_SearchTarget.Items.Add(item);
}
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
@@ -82,9 +92,6 @@ namespace WindowsFormsApp1.Mac
private void Check_copy_Load(object sender, EventArgs e)
{
string[] Grid = { "----- 빈칸으로 놔주세요 -----", "빈칸", "" };
dv1.Rows.Add(Grid);
this.Show();
Application.DoEvents();
@@ -117,7 +124,7 @@ namespace WindowsFormsApp1.Mac
}
//기존프로세스 삭제
SeleniumHelper.KillExistingDrivers();
await SeleniumHelper.KillExistingDriversAsync();
//준비된 경우에는 다운로드 하지 않는다.
if (SeleniumHelper.IsReady)
@@ -169,7 +176,7 @@ namespace WindowsFormsApp1.Mac
}
}
}
catch (Exception ex)
{
@@ -219,22 +226,12 @@ namespace WindowsFormsApp1.Mac
async private void btn_Start_Click(object sender, EventArgs e)
{
var libraryName = tb_SearchTarget.Text.Trim();
if (libraryName.isEmpty())
var searcher = this.GetSearcher();
if (searcher == null)
{
tb_SearchTarget.Focus();
UTIL.MsgE("도서관을 선택하세요");
UTIL.MsgE("검색 대상을 선택하세요");
return;
}
if (libraryName.Contains("Kolasys.net"))
{
if (lbl_ID.Text == " " || lbl_PW.Text == " ")
{
UTIL.MsgE("해당 도서관은 로그인정보가 필요합니다.\n복본조사 창을 껏다켜주세요.");
return;
}
}
SearchCount.Value = dv1.Rows.Count; //250619
RowCount = 0; //250619
@@ -243,154 +240,136 @@ namespace WindowsFormsApp1.Mac
int SCount = Convert.ToInt32(SearchCount.Value);
if (SearcherNo.toInt() < 1)
if (DateTime.Now > new DateTime(2025, 09, 30))
{
//legacy
UTIL.MsgE("이 기능은 기존 복본검사를 사용하세요");
UTIL.MsgE("테스트 기간이 종료되었습니다\n개발자 문의 하세요");
return;
}
else
if (SeleniumHelper.IsReady == false)
{
if (DateTime.Now > new DateTime(2025, 09, 30))
{
UTIL.MsgE("테스트 기간이 종료되었습니다\n개발자 문의 하세요");
return;
}
UTIL.MsgE("웹드라이버가 초기화되지 않았습니다\n1.크롬브라우즈가 설치되어있는지 확인하세요\n\n2.개발자 문의하세요");
return;
}
if (SeleniumHelper.IsReady == false)
{
UTIL.MsgE("웹드라이버가 초기화되지 않았습니다\n1.크롬브라우즈가 설치되어있는지 확인하세요\n\n2.개발자 문의하세요");
return;
}
try
{
try
this._isSearching = true;
btn_Start.Enabled = false;
btn_Stop.Enabled = false;
await searcher.StartDriver(this.chkShowBrowser.Checked);
byte searchopt = 0; //전체검색
if (radTargetEmpty.Checked) searchopt = 1;
else if (radTargetErr.Checked) searchopt = 2;
else if (radTargetErrEmpty.Checked) searchopt = 3;
bool retry = false;
var cnt_totalcnt = 0;
int cnt_skip = 0;
int cnt_ok = 0;
int cnt_ng = 0;
int cnt_er = 0;
RetrySearch:
foreach (DataGridViewRow drow in this.dv1.Rows)
{
var searcher = this._searchService.Get(this.SearcherNo.toInt());
if (searcher == null)
if (this._isSearching == false)
{
UTIL.MsgE($"검색서비스가 존재하지 않습니다. 개발자에 문의 하세요\n검색서비스번호:{SearcherNo}");
return;
drow.Cells["dvc_remark"].Value = "Cancel";
continue;
}
this._isSearching = true;
btn_Start.Enabled = false;
btn_Stop.Enabled = false;
await searcher.StartDriver(this.chkShowBrowser.Checked);
byte searchopt = 0; //전체검색
if (radTargetEmpty.Checked) searchopt = 1;
else if (radTargetErr.Checked) searchopt = 2;
else if (radTargetErrEmpty.Checked) searchopt = 3;
bool retry = false;
var cnt_totalcnt = 0;
int cnt_skip = 0;
int cnt_ok = 0;
int cnt_ng = 0;
int cnt_er = 0;
RetrySearch:
foreach (DataGridViewRow drow in this.dv1.Rows)
dv1.CurrentCell = drow.Cells[0];
//var vBookName = drow.Cells["book_name"].Value;
var bookName = drow.Cells["book_name"].Value?.ToString() ?? string.Empty;
if (bookName.isEmpty())
{
if (this._isSearching == false)
{
drow.Cells["dvc_remark"].Value = "Cancel";
cnt_skip += 1;
drow.Cells["dvc_remark"].Value = "No Title";
drow.DefaultCellStyle.BackColor = Color.HotPink;
drow.DefaultCellStyle.ForeColor = Color.Blue;
continue;
}
var cntValue = drow.Cells["Count"].Value?.ToString() ?? string.Empty;
cntValue = cntValue.ToLower().Trim();
if (radTargetErr.Checked) //오류데이터만 처리
{
if (cntValue.Equals("-1") == false)
continue;
}
dv1.CurrentCell = drow.Cells[0];
//var vBookName = drow.Cells["book_name"].Value;
var bookName = drow.Cells["book_name"].Value?.ToString() ?? string.Empty;
if (bookName.isEmpty())
{
cnt_skip += 1;
drow.Cells["dvc_remark"].Value = "No Title";
drow.DefaultCellStyle.BackColor = Color.HotPink;
drow.DefaultCellStyle.ForeColor = Color.Blue;
}
else if (radTargetEmpty.Checked) //빈데이터만 처리
{
if (int.TryParse(cntValue, out int cntvalue) && cntvalue > 0)
continue;
}
}
else if (radTargetErrEmpty.Checked) //오류+빈데이터처리
{
if (cntValue.Equals("-1") == false && int.TryParse(cntValue, out int cntvalue) && cntvalue > 0)
continue;
}
var cntValue = drow.Cells["Count"].Value?.ToString() ?? string.Empty;
cntValue = cntValue.ToLower().Trim();
if (radTargetErr.Checked) //오류데이터만 처리
cnt_totalcnt += 1;
var rlt = await searcher.SearchAsync(bookName);
if (rlt.IsSuccess)
{
drow.Cells["dvc_remark"].Value = rlt.ErrorMessage;
drow.Cells["Count"].Value = $"{rlt.BookCount}";
if (rlt.BookCount == 0)
{
if (cntValue.Equals("-1") == false)
continue;
}
else if (radTargetEmpty.Checked) //빈데이터만 처리
{
if (int.TryParse(cntValue, out int cntvalue) && cntvalue > 0)
continue;
}
else if (radTargetErrEmpty.Checked) //오류+빈데이터처리
{
if (cntValue.Equals("-1") == false && int.TryParse(cntValue, out int cntvalue) && cntvalue > 0)
continue;
}
cnt_totalcnt += 1;
var rlt = await searcher.SearchAsync(bookName);
if (rlt.IsSuccess)
{
drow.Cells["dvc_remark"].Value = rlt.ErrorMessage;
drow.Cells["Count"].Value = $"{rlt.BookCount}";
if (rlt.BookCount == 0)
{
cnt_ng += 1;
drow.DefaultCellStyle.BackColor = Color.LightGray;
drow.DefaultCellStyle.ForeColor = Color.Red;
}
else
{
cnt_ok += 1;
drow.DefaultCellStyle.BackColor = Color.Yellow;
drow.DefaultCellStyle.ForeColor = Color.Blue;
}
cnt_ng += 1;
drow.DefaultCellStyle.BackColor = Color.LightGray;
drow.DefaultCellStyle.ForeColor = Color.Red;
}
else
{
cnt_er += 1;
drow.Cells["dvc_remark"].Value = $"Error|{rlt.ErrorMessage}";
drow.DefaultCellStyle.BackColor = Color.HotPink;
cnt_ok += 1;
drow.DefaultCellStyle.BackColor = Color.Yellow;
drow.DefaultCellStyle.ForeColor = Color.Blue;
}
}
if (retry == false && chkRetryErrData.Checked)
{
retry = true;
radTargetErr.Checked = true;
goto RetrySearch;
}
else
{
searcher.StopDriver();
UTIL.MsgI($"검색 완료\n전체:{cnt_totalcnt}건/오류:{cnt_er}/성공:{cnt_ok}/없음:{cnt_ng}");
//재시도한경우라면 원복해준다
if (retry)
{
if (searchopt == 1) radTargetEmpty.Checked = true;
else if (searchopt == 2) radTargetErr.Checked = true;
else if (searchopt == 3) radTargetErrEmpty.Checked = true;
else radTargetAll.Checked = true;
}
cnt_er += 1;
drow.Cells["dvc_remark"].Value = $"Error|{rlt.ErrorMessage}";
drow.DefaultCellStyle.BackColor = Color.HotPink;
drow.DefaultCellStyle.ForeColor = Color.Blue;
}
}
catch (Exception ex)
{
UTIL.MsgE("Web Driver Error\n" + ex.Message);
}
finally
{
btn_Stop.Enabled = true;
btn_Start.Enabled = true;
this._isSearching = false;
}
if (retry == false && chkRetryErrData.Checked)
{
retry = true;
radTargetErr.Checked = true;
goto RetrySearch;
}
else
{
searcher.StopDriver();
UTIL.MsgI($"검색 완료\n전체:{cnt_totalcnt}건/오류:{cnt_er}/성공:{cnt_ok}/없음:{cnt_ng}");
//재시도한경우라면 원복해준다
if (retry)
{
if (searchopt == 1) radTargetEmpty.Checked = true;
else if (searchopt == 2) radTargetErr.Checked = true;
else if (searchopt == 3) radTargetErrEmpty.Checked = true;
else radTargetAll.Checked = true;
}
}
}
catch (Exception ex)
{
UTIL.MsgE("Web Driver Error\n" + ex.Message);
}
finally
{
btn_Stop.Enabled = true;
btn_Start.Enabled = true;
this._isSearching = false;
}
@@ -557,15 +536,41 @@ namespace WindowsFormsApp1.Mac
}
ILibrarySearcher GetSearcher()
{
if (this.tb_SearchTarget.SelectedIndex < 0) return null;
return this._searchService.Get(tb_SearchTarget.Text);
}
private void btn_SiteDenote_Click(object sender, EventArgs e)
{
if (URL == null) return;
AR.UTIL.RunExplorer(URL);
var searcher = this.GetSearcher();
if (searcher == null)
{
UTIL.MsgE("검색 대상을 선택하세요");
return;
}
var url = searcher.SiteUrl;
if (url.Contains("\"") == false) url = $"\"{url}\"";
AR.UTIL.RunExplorer(url);
}
private void btSearchLibrary_Click(object sender, EventArgs e)
{
SearchLibrary();
}
private void tb_SearchTarget_SelectedIndexChanged(object sender, EventArgs e)
{
var searcher = this._searchService.Get(this.tb_SearchTarget.Text);
if (searcher == null)
{
lbSite.Text = "-- site --";
return;
}
this.lbSite.Text = searcher.SiteUrl;
}
}
}

View File

@@ -132,4 +132,7 @@
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>