dongnelibrary 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,11 +4,9 @@
4
4
 
5
5
  # DongneLibrary
6
6
 
7
- > 도서관 책을 빌릴 수 있는지 확인하는 프로그램
8
-
7
+ > 도서관 책을 빌릴 수 있는지 검색
9
8
 
10
9
  도서관 이름을 생략하면 모든 도서관을 검색한다.
11
- [블로그](https://agvim.wordpress.com/2017/01/20/check-if-a-library-book-was-rented/)에서 간단한 설명을 볼 수 있다.
12
10
 
13
11
  ## install with git
14
12
 
@@ -59,38 +57,38 @@ dongne () {
59
57
  npm install dongnelibrary
60
58
 
61
59
  ```javascript
62
- const dl = require('dongnelibrary');
63
- dl.search({
64
- title: 'javascript',
65
- libraryName: ['여주','판교']
66
- }, function (err, book) {
67
- console.log(book.libraryName + ' "' + book.title + '"');
68
- book.booklist.forEach(function (book) {
69
- console.log((book.exist?' ✓ ':' ') +' '+ book.title);
70
- });
71
- }, function (err, books) {
72
- console.log(books.length + ' 개의 도서관을 검색했습니다.');
73
- });
60
+ const dl = require("dongnelibrary");
61
+ dl.search(
62
+ {
63
+ title: "javascript",
64
+ libraryName: ["여주", "판교"],
65
+ },
66
+ function (err, book) {
67
+ console.log(book.libraryName + ' "' + book.title + '"');
68
+ book.booklist.forEach(function (book) {
69
+ console.log((book.exist ? " ✓ " : " ✖ ") + " " + book.title);
70
+ });
71
+ },
72
+ function (err, books) {
73
+ console.log(books.length + " 개의 도서관을 검색했습니다.");
74
+ },
75
+ );
74
76
  ```
75
77
 
76
78
  ## 검색 가능한 도서관
77
79
 
78
- * [경기교육통합도서관][gg-url] (경기중앙교육도서관,경기평택교육도서관,경기광주교육도서관,경기여주가남교육도서관,경기포천교육도서관,경기김포교육도서관,경기과천교육도서관,경기성남교육도서관,경기화성교육도서관,경기의정부교육도서관,경기평생교육학습관)
79
- * [군포시도서관][gunpo-url] (산본도서관,당동도서관,대야도서관,어린이도서관,이동도서관,중앙도서관,누리천문대,시청북카페밥상머리,부곡도서관,당정문화도서관,동화나무어린이도서관,금정작은도서관,재궁꿈나무도서관,궁내동작은도서관,노루목작은도서관,버드나무에부는바람작은도서관,꿈쟁이도서관,우리마을도서관,북카페사랑아이엔지,산본역도서관,하늘정원작은도서관,꿈이지,꿈드림작은도서관,여담작은도서관)
80
- * [성남시도서관][snlib-url] (논골도서관,중원어린이도서관,중앙도서관,분당도서관,구미도서관,해오름도서관,중원도서관,무지개도서관,판교도서관,위례도서관,수정도서관,책테마파크도서관,운중도서관,서현도서관,복정도서관,판교어린이도서관)
81
- * [오산시도서관][osan-url] (중앙도서관,꿈두레도서관,초평도서관,햇살마루도서관,청학도서관,양산도서관,소리울도서관,무지개도서관,고현초꿈키움도서관,쌍용예가시민개방도서관)
82
- * [화성시립도서관][hscity-url] (남양도서관,태안도서관,삼괴도서관,병점도서관,샘내도서관,두빛나래도서관,봉담도서관,둥지나래도서관,목동이음터도서관,기아행복마루도서관,동탄복합문화센터도서관,송산도서관,정남도서관,비봉도서관,진안도서관,중앙이음터도서관,양감도서관,다원이음터도서관,송린이음터도서관,팔탄도서관,마도도서관,봉담커피앤북도서관,왕배푸른숲도서관,노을빛도서관,서연이음터도서관,호연이음터도서관,늘봄이음터도서관)
83
- * [수원시도서관][suwon-url] (선경도서관,중앙도서관,영통도서관,슬기샘도서관,바른샘도서관,지혜샘도서관,서수원도서관,북수원도서관,태장마루도서관,한아름도서관,반달어린이도서관,사랑샘도서관,희망샘도서관,화홍어린이도서관,대추골도서관,한림도서관,창룡도서관,버드내도서관,광교홍재도서관,호매실도서관,일월도서관,화서다산도서관,광교푸른숲도서관,매여울도서관,망포글빛도서관)
80
+ - [경기교육통합도서관][gg-url] (경기중앙교육도서관,경기평택교육도서관,경기광주교육도서관,경기여주가남교육도서관,경기포천교육도서관,경기김포교육도서관,경기과천교육도서관,경기성남교육도서관,경기화성교육도서관,경기의정부교육도서관,경기평생교육학습관)
81
+ - [군포시도서관][gunpo-url] (산본도서관,당동도서관,대야도서관,어린이도서관,이동도서관,중앙도서관,누리천문대,시청북카페밥상머리,부곡도서관,당정문화도서관,동화나무어린이도서관,금정작은도서관,재궁꿈나무도서관,궁내동작은도서관,노루목작은도서관,버드나무에부는바람작은도서관,꿈쟁이도서관,우리마을도서관,북카페사랑아이엔지,산본역도서관,하늘정원작은도서관,꿈이지,꿈드림작은도서관,여담작은도서관)
82
+ - [성남시도서관][snlib-url] (논골도서관,중원어린이도서관,중앙도서관,분당도서관,구미도서관,해오름도서관,중원도서관,무지개도서관,판교도서관,위례도서관,수정도서관,책테마파크도서관,운중도서관,서현도서관,복정도서관,판교어린이도서관)
83
+ - [오산시도서관][osan-url] (중앙도서관,꿈두레도서관,초평도서관,햇살마루도서관,청학도서관,양산도서관,소리울도서관,무지개도서관,고현초꿈키움도서관,쌍용예가시민개방도서관)
84
+ - [화성시립도서관][hscity-url] (남양도서관,태안도서관,삼괴도서관,병점도서관,샘내도서관,두빛나래도서관,봉담도서관,둥지나래도서관,목동이음터도서관,기아행복마루도서관,동탄복합문화센터도서관,송산도서관,정남도서관,비봉도서관,진안도서관,중앙이음터도서관,양감도서관,다원이음터도서관,송린이음터도서관,팔탄도서관,마도도서관,봉담커피앤북도서관,왕배푸른숲도서관,노을빛도서관,서연이음터도서관,호연이음터도서관,늘봄이음터도서관)
85
+ - [수원시도서관][suwon-url] (선경도서관,중앙도서관,창룡도서관,화서다산도서관,호매실도서관,서수원도서관,한림도서관,버드내도서관,북수원도서관,대추골도서관,일월도서관,광교홍재도서관,태장마루도서관,광교푸른숲도서관,매여울도서관,망포글빛도서관,슬기샘도서관,지혜샘어린이도서관,바른샘어린이도서관,한아름도서관,반달어린이도서관,사랑샘도서관,희망샘도서관)
84
86
 
85
87
  ## 마무리
86
88
 
87
89
  [온라인에서][sample-url] 테스트 할 수 있으며 명령어가 불편하다면 [웹 서비스][web-ui-url]를 사용할 수 있다.
88
- 웹 서비스가 느리다면 검색 서버를 [로컬에 설치][dongnelibraryspa] 할 수 있다.
89
90
  [Web API 서비스][web-api]도 지원한다.
90
91
 
91
- [![SPA for dongnelibrary](https://agvim.files.wordpress.com/2017/07/dongne23.png?w=128)](https://dongne.herokuapp.com/)
92
- [![APP for dongnelibrary](https://agvim.files.wordpress.com/2019/06/dlserver.png?w=128)](https://dlserver.herokuapp.com/app/)
93
-
94
92
  ## Test
95
93
 
96
94
  npm test
@@ -107,14 +105,12 @@ dl.search({
107
105
  [npm-url]: https://npmjs.org/package/dongnelibrary
108
106
  [travis-build-image]: https://travis-ci.org/afrontend/dongnelibrary.svg?branch=master
109
107
  [travis-build-url]: https://travis-ci.org/afrontend/dongnelibrary
110
- [web-ui-url]: https://dongne.herokuapp.com "무료 서버라서 10초 정도 느리게 로딩될 수 있어요"
108
+ [web-ui-url]: https://dongne.onrender.com
111
109
  [web-api]: https://github.com/afrontend/dlserver "같은 기능을 지원하는 Web API"
112
-
113
110
  [gg-url]: http://www.gglib.or.kr
114
111
  [gunpo-url]: http://www.gunpolib.go.kr
115
112
  [hscity-url]: https://hscitylib.or.kr
116
113
  [osan-url]: http://www.osanlibrary.go.kr
117
114
  [snlib-url]: http://www.snlib.net
118
115
  [suwon-url]: http://www.suwonlib.go.kr
119
-
120
116
  [sample-url]: https://npm.runkit.com/dongnelibrary
package/package.json CHANGED
@@ -3,11 +3,11 @@
3
3
  "engines": {
4
4
  "node": ">=16.0.0"
5
5
  },
6
- "version": "0.2.2",
6
+ "version": "0.2.4",
7
7
  "description": "책을 빌릴 수 있는지 확인한다.",
8
8
  "main": "src/dongnelibrary.js",
9
9
  "bin": {
10
- "dongnelibrary": "./src/cli.js"
10
+ "dongnelibrary": "src/cli.js"
11
11
  },
12
12
  "scripts": {
13
13
  "test": "mocha",
@@ -53,7 +53,6 @@
53
53
  "configstore": "^4.0.0",
54
54
  "figlet": "^1.2.1",
55
55
  "inquirer": "^6.2.2",
56
- "jquery": "^3.6.4",
57
56
  "jsdom": "^21.1.1",
58
57
  "lodash": "^4.17.20",
59
58
  "request": "^2.73.0"
@@ -1,96 +1,130 @@
1
- const getLibraryNames = require('../util.js').getLibraryNames;
2
- const jquery = require('jquery');
3
- const req = require('request')
1
+ const getLibraryNames = require("../util.js").getLibraryNames;
2
+ const req = require("request");
4
3
  const { JSDOM } = require("jsdom");
5
4
 
6
5
  const libraryList = [
7
- {code: 'MA', name: '중앙도서관'},
8
- {code: 'MG', name: '꿈두레도서관'},
9
- {code: 'ME', name: '초평도서관'},
10
- {code: 'MC', name: '햇살마루도서관'},
11
- {code: 'MB', name: '청학도서관'},
12
- {code: 'MD', name: '양산도서관'},
13
- {code: 'MI', name: '소리울도서관'},
14
- {code: 'MY', name: '무지개도서관'},
15
- {code: 'MH', name: '고현초꿈키움도서관'},
16
- {code: 'MJ', name: '쌍용예가시민개방도서관'},
6
+ { code: "MA", name: "중앙도서관" },
7
+ { code: "MG", name: "꿈두레도서관" },
8
+ { code: "ME", name: "초평도서관" },
9
+ { code: "MC", name: "햇살마루도서관" },
10
+ { code: "MB", name: "청학도서관" },
11
+ { code: "MD", name: "양산도서관" },
12
+ { code: "MI", name: "소리울도서관" },
13
+ { code: "MY", name: "무지개도서관" },
14
+ { code: "MH", name: "고현초꿈키움도서관" },
15
+ { code: "MJ", name: "쌍용예가시민개방도서관" },
17
16
  ];
18
17
 
19
18
  function getLibraryCode(libraryName) {
20
- const found = libraryList.find(lib => (lib.name === libraryName));
21
- return found ? found.code : '';
19
+ const found = libraryList.find((lib) => lib.name === libraryName);
20
+ return found ? found.code : "";
22
21
  }
23
22
 
24
23
  function search(opt, getBook) {
25
- let title = opt.title
26
- let libraryName = opt.libraryName
24
+ let title = opt.title;
25
+ let libraryName = opt.libraryName;
27
26
 
28
27
  if (!title) {
29
28
  if (getBook) {
30
- getBook({msg: 'Need a book name'});
29
+ getBook({ msg: "Need a book name" });
31
30
  }
32
31
  return;
33
32
  }
34
33
 
35
- // https://www.osanlibrary.go.kr/kolaseek/plus/search/plusSearchResultList.do?searchType=SIMPLE&searchCategory=ALL&searchLibraryArr=MA&searchKey=ALL&searchKeyword=javascript&searchRecordCount=20
36
- const lcode = getLibraryCode(libraryName)
37
- req.get({
38
- url: `https://www.osanlibrary.go.kr/kolaseek/plus/search/plusSearchResultList.do`,
39
- timeout: 20000,
40
- qs: {
41
- searchType: "SIMPLE",
42
- searchCategory: "ALL",
43
- searchLibraryArr: lcode,
44
- searchKey: "ALL",
45
- searchKeyword: title,
46
- searchRecordCount: 1000,
47
- }
48
- }, function (err, res, body) {
49
- if (err || (res && res.statusCode !== 200)) {
50
- let msg = '';
34
+ // https://www.osanlibrary.go.kr/intro/program/plusSearchResultList.do?searchType=SIMPLE&searchCategory=ALL&searchLibraryArr=MA&searchKey=ALL&searchKeyword=javascript&searchRecordCount=20
35
+ const lcode = getLibraryCode(libraryName);
36
+ req.get(
37
+ {
38
+ url: `https://www.osanlibrary.go.kr/intro/program/plusSearchResultList.do`,
39
+ timeout: 20000,
40
+ qs: {
41
+ searchType: "SIMPLE",
42
+ searchCategory: "ALL",
43
+ searchLibraryArr: lcode,
44
+ searchKey: "ALL",
45
+ searchKeyword: title,
46
+ searchRecordCount: 1000,
47
+ },
48
+ },
49
+ function (err, res, body) {
50
+ if (err || (res && res.statusCode !== 200)) {
51
+ let msg = "";
51
52
 
52
- if (err) {
53
- msg = err;
54
- }
53
+ if (err) {
54
+ msg = err;
55
+ }
55
56
 
56
- if (res && res.statusCode) {
57
- msg = msg + " " + res.statusCode;
58
- }
57
+ if (res && res.statusCode) {
58
+ msg = msg + " " + res.statusCode;
59
+ }
59
60
 
60
- if (getBook) {
61
- getBook({msg: msg});
62
- }
63
- } else {
64
- const dom = new JSDOM(body);
65
- const $ = jquery(dom.window)
66
- const count = $('b.themeFC').text().match(/\d+/)[0]
67
- const booklist = []
68
- $('.resultList > li').each((_, a) => {
69
- const title = $(a).find('.tit a').text().trim()
70
- const rented = $(a).find('.bookStateBar .txt b').text().trim()
71
- const b = $(a).find('.site > span:first-child').text().split(']')
72
- const libraryName = b && b[1] ? b[1].trim() : ''
73
- if (title) {
74
- booklist.push({
75
- libraryName,
76
- title,
77
- maxoffset: count,
78
- exist: rented.includes('대출가능')
79
- });
61
+ if (getBook) {
62
+ getBook({ msg: msg });
80
63
  }
81
- })
82
- getBook(null, {
83
- startPage: opt.startPage,
84
- totalBookCount: count,
85
- booklist,
86
- });
87
- }
88
- });
64
+ } else {
65
+ const dom = new JSDOM(body);
66
+ const document = dom.window.document;
67
+
68
+ // Extract total count from "총 <span class="highlight">44</span>건"
69
+ const highlightSpans = document.querySelectorAll("span.highlight");
70
+ let count = "0";
71
+ for (const span of highlightSpans) {
72
+ const text = span.textContent.trim();
73
+ if (/^\d+$/.test(text)) {
74
+ count = text;
75
+ break;
76
+ }
77
+ }
78
+
79
+ const booklist = [];
80
+ const bookItems = document.querySelectorAll(".bookList .listWrap > li");
81
+ bookItems.forEach((li) => {
82
+ // Get title from .book_name span
83
+ const titleEl = li.querySelector(".book_name span");
84
+ const bookTitle = titleEl ? titleEl.textContent.trim() : "";
85
+
86
+ // Get availability status from .status p
87
+ const statusEl = li.querySelector(".status p");
88
+ const statusText = statusEl ? statusEl.textContent.trim() : "";
89
+ const exist = statusText.includes("대출가능");
90
+
91
+ // Get library name from ".book_info .fb p" containing "소장도서관"
92
+ let libName = "";
93
+ const fbParagraphs = li.querySelectorAll(".book_info .fb p");
94
+ fbParagraphs.forEach((p) => {
95
+ const text = p.textContent;
96
+ if (text.includes("소장도서관")) {
97
+ // Format: "[공공]오산시중앙도서관" - extract library name after "]"
98
+ const match = text.match(/\](.+)$/);
99
+ if (match) {
100
+ libName = match[1].trim();
101
+ }
102
+ }
103
+ });
104
+
105
+ if (bookTitle) {
106
+ booklist.push({
107
+ libraryName: libName,
108
+ title: bookTitle,
109
+ maxoffset: count,
110
+ exist: exist,
111
+ });
112
+ }
113
+ });
114
+
115
+ getBook(null, {
116
+ startPage: opt.startPage,
117
+ totalBookCount: count,
118
+ booklist,
119
+ });
120
+ }
121
+ },
122
+ );
89
123
  }
90
124
 
91
125
  module.exports = {
92
126
  search,
93
- getLibraryNames: function() {
127
+ getLibraryNames: function () {
94
128
  return getLibraryNames(libraryList);
95
- }
129
+ },
96
130
  };
@@ -78,13 +78,16 @@ function search(opt, getBook) {
78
78
  $('.resultList > li').each((_, a) => {
79
79
  const title = $(a).find('.tit a').text().trim()
80
80
  const rented = $(a).find('.bookStateBar .txt b').text()
81
- const libraryName = $(a).find('.site > span:first-child').text().split(':')[1].trim()
82
- booklist.push({
83
- libraryName,
84
- title,
85
- maxoffset: count,
86
- exist: rented.includes('대출가능')
87
- });
81
+ const b = $(a).find('.site > span:first-child').text().split(':')
82
+ const libraryName = b && b[1] ? b[1].trim() : ''
83
+ if (title) {
84
+ booklist.push({
85
+ libraryName,
86
+ title,
87
+ maxoffset: count,
88
+ exist: rented.includes('대출가능')
89
+ });
90
+ }
88
91
  })
89
92
  }
90
93
  getBook(null, {
@@ -1,106 +1,174 @@
1
- const getLibraryNames = require('../util.js').getLibraryNames;
2
- const req = require('request');
1
+ const getLibraryNames = require("../util.js").getLibraryNames;
2
+ const req = require("request");
3
3
 
4
4
  const libraryList = [
5
- {code: 'MA', name: '선경도서관'},
6
- {code: 'MB', name: '중앙도서관'},
7
- {code: 'MC', name: '영통도서관'},
8
- {code: 'MD', name: '슬기샘도서관'},
9
- {code: 'ME', name: '바른샘도서관'},
10
- {code: 'MF', name: '지혜샘도서관'},
11
- {code: 'MG', name: '서수원도서관'},
12
- {code: 'MH', name: '북수원도서관'},
13
- {code: 'MI', name: '태장마루도서관'},
14
- {code: 'MK', name: '한아름도서관'},
15
- {code: 'MM', name: '반달어린이도서관'},
16
- {code: 'MN', name: '사랑샘도서관'},
17
- {code: 'MO', name: '희망샘도서관'},
18
- {code: 'MP', name: '화홍어린이도서관'},
19
- {code: 'MT', name: '대추골도서관'},
20
- {code: 'MU', name: '한림도서관'},
21
- {code: 'MV', name: '창룡도서관'},
22
- {code: 'MW', name: '버드내도서관'},
23
- {code: 'MX', name: '광교홍재도서관'},
24
- {code: 'MY', name: '호매실도서관'},
25
- {code: 'MZ', name: '일월도서관'},
26
- {code: 'SB', name: '화서다산도서관'},
27
- {code: 'SC', name: '광교푸른숲도서관'},
28
- {code: 'SD', name: '매여울도서관'},
29
- {code: 'SE', name: '망포글빛도서관'},
5
+ { code: "141025", name: "선경도서관" },
6
+ { code: "141024", name: "중앙도서관" },
7
+ { code: "141549", name: "창룡도서관" },
8
+ { code: "141572", name: "화서다산도서관" },
9
+ { code: "141552", name: "호매실도서관" },
10
+ { code: "141093", name: "서수원도서관" },
11
+ { code: "141542", name: "한림도서관" },
12
+ { code: "141550", name: "버드내도서관" },
13
+ { code: "141092", name: "북수원도서관" },
14
+ { code: "141537", name: "대추골도서관" },
15
+ { code: "141557", name: "일월도서관" },
16
+ { code: "141551", name: "광교홍재도서관" },
17
+ { code: "141301", name: "태장마루도서관" },
18
+ { code: "141595", name: "광교푸른숲도서관" },
19
+ { code: "141596", name: "매여울도서관" },
20
+ { code: "141612", name: "망포글빛도서관" },
21
+ { code: "141085", name: "슬기샘도서관" },
22
+ { code: "141086", name: "지혜샘어린이도서관" },
23
+ { code: "141087", name: "바른샘어린이도서관" },
24
+ { code: "141064", name: "한아름도서관" },
25
+ { code: "141138", name: "반달어린이도서관" },
26
+ { code: "341147", name: "사랑샘도서관" },
27
+ { code: "141107", name: "희망샘도서관" },
30
28
  ];
31
29
 
32
30
  function getLibraryCode(libraryName) {
33
- const found = libraryList.find(lib => (lib.name === libraryName));
34
- return found ? found.code : 'ALL';
31
+ const found = libraryList.find((lib) => lib.name === libraryName);
32
+ return found ? found.code : "";
35
33
  }
36
34
 
37
- function getBookList(json) {
38
- const bl = json.contents ? json.contents.bookList : []
39
- return bl.map(function (book) {
35
+ function getAllLibraryCodes() {
36
+ return libraryList.map((lib) => lib.code).join(",");
37
+ }
38
+
39
+ function stripHtml(str) {
40
+ return str ? str.replace(/<[^>]*>/g, "") : "";
41
+ }
42
+
43
+ function getBookList(data) {
44
+ if (!data.SEARCH_RESULT || !data.SEARCH_RESULT.SEARCH_LIST) {
45
+ return [];
46
+ }
47
+ return data.SEARCH_RESULT.SEARCH_LIST.map(function (book) {
40
48
  return {
41
- title: book.originalTitle,
42
- exist: book.workingStatus === "비치중",
43
- libraryName: book.libName
49
+ title: stripHtml(book.TITLE_INFO || ""),
50
+ exist: book.LOAN_CODE === "OK",
51
+ libraryName: book.LIB_NAME || "",
44
52
  };
45
53
  });
46
54
  }
47
55
 
48
56
  function search(opt, getBook) {
49
- let title = opt.title
50
- let libraryName = opt.libraryName
57
+ let title = opt.title;
58
+ let libraryName = opt.libraryName;
51
59
 
52
60
  if (!title) {
53
61
  if (getBook) {
54
- getBook({msg: 'Need a book name'});
62
+ getBook({ msg: "Need a book name" });
55
63
  }
56
64
  return;
57
65
  }
58
66
 
59
- const lcode = getLibraryCode(libraryName)
60
- // const etitle = encodeURIComponent(title)
61
- req.post({
62
- url: 'https://www.suwonlib.go.kr:8443/api/search',
63
- timeout: 20000,
64
- headers: {
65
- "User-Agent": 'User-Agent:Mozilla/5.0 (X11 Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36'
66
- },
67
- form: {
68
- article: "SCORE",
69
- display: "1000",
70
- manageCode: lcode,
71
- order: "DESC",
72
- page: "1",
73
- pubFormCode: "MO",
74
- searchKeyword: title,
75
- }
76
- }, function (err, res, body) {
77
- if (err || (res && res.statusCode !== 200)) {
78
- let msg = '';
67
+ const lcode = libraryName
68
+ ? getLibraryCode(libraryName)
69
+ : getAllLibraryCodes();
79
70
 
80
- if (err) {
81
- msg = err;
82
- }
71
+ // Create a cookie jar to maintain session
72
+ const jar = req.jar();
83
73
 
84
- if (res && res.statusCode) {
85
- msg = msg + " " + res.statusCode;
74
+ // First, visit the search page to initialize session
75
+ req.get(
76
+ {
77
+ url: "https://search.suwonlib.go.kr/search",
78
+ jar: jar,
79
+ timeout: 20000,
80
+ headers: {
81
+ "User-Agent":
82
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
83
+ },
84
+ },
85
+ function (err1, res1, body1) {
86
+ if (err1) {
87
+ if (getBook) {
88
+ getBook({ msg: err1.toString() });
89
+ }
90
+ return;
86
91
  }
87
92
 
88
- if (getBook) {
89
- getBook({msg: msg});
90
- }
91
- } else {
92
- const booklist = getBookList(JSON.parse(body))
93
- getBook(null, {
94
- totalBookCount: booklist.length,
95
- booklist,
96
- });
97
- }
98
- });
93
+ // Now make the API call with the session cookies
94
+ // Requires 'ajax: true' header and full parameter set including empty facet parameters
95
+ req.post(
96
+ {
97
+ url: "https://search.suwonlib.go.kr/getSearchResult/normal",
98
+ jar: jar,
99
+ timeout: 20000,
100
+ headers: {
101
+ "User-Agent":
102
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
103
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
104
+ Referer: "https://search.suwonlib.go.kr/search",
105
+ "X-Requested-With": "XMLHttpRequest",
106
+ ajax: "true",
107
+ },
108
+ form: {
109
+ searchTxt: title,
110
+ kCid: "",
111
+ kdcValue: "",
112
+ searchKind: "book",
113
+ manageCode: lcode,
114
+ isInnerSearch: "F",
115
+ innerSearchTxt: "",
116
+ keywordSearch: false,
117
+ displayNo: "1000",
118
+ orderbyItem: "ACCURACY_SORT",
119
+ orderby: "DESC",
120
+ pageNo: "1",
121
+ facetLib: "",
122
+ facetLibName: "",
123
+ facetAuthor: "",
124
+ facetPublisher: "",
125
+ facetPubYear: "",
126
+ facetSubject: "",
127
+ facetSubjectName: "",
128
+ facetMedia: "",
129
+ facetMediaName: "",
130
+ },
131
+ },
132
+ function (err, res, body) {
133
+ if (err || (res && res.statusCode !== 200)) {
134
+ let msg = "";
135
+
136
+ if (err) {
137
+ msg = err;
138
+ }
139
+
140
+ if (res && res.statusCode) {
141
+ msg = msg + " " + res.statusCode;
142
+ }
143
+
144
+ if (getBook) {
145
+ getBook({ msg: msg });
146
+ }
147
+ } else {
148
+ try {
149
+ const data = JSON.parse(body);
150
+ const booklist = getBookList(data);
151
+ const totalCount =
152
+ data.SEARCH_RESULT && data.SEARCH_RESULT.SEARCH_COUNT
153
+ ? data.SEARCH_RESULT.SEARCH_COUNT
154
+ : booklist.length;
155
+ getBook(null, {
156
+ totalBookCount: totalCount,
157
+ booklist,
158
+ });
159
+ } catch (e) {
160
+ getBook({ msg: "Failed to parse response: " + e.message });
161
+ }
162
+ }
163
+ },
164
+ );
165
+ },
166
+ );
99
167
  }
100
168
 
101
169
  module.exports = {
102
170
  search,
103
- getLibraryNames: function() {
171
+ getLibraryNames: function () {
104
172
  return getLibraryNames(libraryList);
105
- }
173
+ },
106
174
  };
@@ -4,7 +4,7 @@ const assert = require('assert');
4
4
  describe('dongnelibrary test', function () {
5
5
  describe('search in four libraries', function () {
6
6
  this.timeout(20000);
7
- const libNameArray = ['여주', '성남', '판교', '영통'];
7
+ const libNameArray = ['여주', '성남', '판교', '선경'];
8
8
  it('-l ' + libNameArray.join(','), function (done) {
9
9
  dl.search({
10
10
  title: '자바스크립트',
@@ -13,18 +13,18 @@ describe('dongnelibrary test', function () {
13
13
  if (err) {
14
14
  assert.fail(err.msg);
15
15
  }
16
- console.log(book.libraryName + ' "' + book.title + '"');
17
- book.booklist.forEach(function (book) {
18
- console.log((book.exist?' ✓ ':' ') +' '+ book.title);
19
- });
16
+ // console.log(book.libraryName + ' "' + book.title + '"');
17
+ // book.booklist.forEach(function (book) {
18
+ // console.log((book.exist?' ✓ ':' ') +' '+ book.title);
19
+ // });
20
20
  assert.notEqual(book.booklist.length, 0);
21
21
  }, function (err, books) {
22
22
  if (err) {
23
23
  assert.fail(err.msg);
24
24
  }
25
- console.log(books.length + ' 개의 도서관을 검색했습니다.');
25
+ // console.log(books.length + ' 개의 도서관을 검색했습니다.');
26
26
  assert.equal(books.length, 4);
27
- console.log('books: ' + JSON.stringify(books, null, 2));
27
+ // console.log('books: ' + JSON.stringify(books, null, 2));
28
28
  done();
29
29
  });
30
30
  });
package/test/gg.spec.js CHANGED
@@ -2,7 +2,7 @@ const assert = require('assert').strict
2
2
  const lib = require('../src/library/gg');
3
3
  const util = require('../src/util.js');
4
4
 
5
- describe('경기도 도서관 (제한시간 20초)', function () {
5
+ describe('경기교육도서관 (제한시간 20초)', function () {
6
6
  this.timeout(20000);
7
7
 
8
8
  it('Show library list', function (done) {
@@ -4,11 +4,41 @@ const util = require('../src/util.js');
4
4
 
5
5
  describe('군포시 도서관 (제한시간 20초)', function () {
6
6
  this.timeout(20000);
7
+
7
8
  it('Show library list', function (done) {
8
9
  assert.ok(lib.getLibraryNames().length > 1);
9
10
  done();
10
11
  });
11
12
 
13
+ it('Use empty book title', function (done) {
14
+ lib.search({
15
+ title: '',
16
+ libraryName: '대야도서관',
17
+ startPage: 1
18
+ }, function (err) {
19
+ if(err) {
20
+ assert.ok(err.msg === 'Need a book name');
21
+ } else {
22
+ assert.fail('Need a error msg')
23
+ }
24
+ done();
25
+ });
26
+ });
27
+
28
+ it('Use invalid book title', function (done) {
29
+ lib.search({
30
+ title: 'zyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz',
31
+ libraryName: '경기평택교육도서관',
32
+ startPage: 1
33
+ }, function (err, book) {
34
+ if(err) {
35
+ assert.fail('must have an empty booklist');
36
+ }
37
+ assert.equal(book.booklist.length, 0)
38
+ done();
39
+ });
40
+ });
41
+
12
42
  it('Show book list of one library', function (done) {
13
43
  lib.search({
14
44
  title: 'javascript',
@@ -48,4 +78,5 @@ describe('군포시 도서관 (제한시간 20초)', function () {
48
78
  done();
49
79
  });
50
80
  });
81
+
51
82
  });
@@ -4,11 +4,41 @@ const util = require('../src/util.js');
4
4
 
5
5
  describe('화성시 도서관 (제한시간 20초)', function () {
6
6
  this.timeout(20000);
7
+
7
8
  it('Show library list', function (done) {
8
9
  assert.ok(lib.getLibraryNames().length > 1);
9
10
  done();
10
11
  });
11
12
 
13
+ it('Use empty book title', function (done) {
14
+ lib.search({
15
+ title: '',
16
+ libraryName: '병점도서관',
17
+ startPage: 1
18
+ }, function (err) {
19
+ if(err) {
20
+ assert.ok(err.msg === 'Need a book name');
21
+ } else {
22
+ assert.fail('Need a error msg')
23
+ }
24
+ done();
25
+ });
26
+ });
27
+
28
+ it('Use invalid book title', function (done) {
29
+ lib.search({
30
+ title: 'zyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz',
31
+ libraryName: '병점도서관',
32
+ startPage: 1
33
+ }, function (err, book) {
34
+ if(err) {
35
+ assert.fail('must have an empty booklist');
36
+ }
37
+ assert.equal(book.booklist.length, 0)
38
+ done();
39
+ });
40
+ });
41
+
12
42
  it('Show book list of one library', function (done) {
13
43
  lib.search({
14
44
  title: 'javascript',
@@ -48,4 +78,5 @@ describe('화성시 도서관 (제한시간 20초)', function () {
48
78
  done();
49
79
  });
50
80
  });
81
+
51
82
  });
@@ -4,17 +4,47 @@ const util = require('../src/util.js');
4
4
 
5
5
  describe('성남시 도서관 (제한시간 20초)', function () {
6
6
  this.timeout(20000);
7
+
7
8
  it('Show library list', function (done) {
8
9
  assert.ok(lib.getLibraryNames().length > 1);
9
10
  done();
10
11
  });
11
12
 
13
+ it('Use empty book title', function (done) {
14
+ lib.search({
15
+ title: '',
16
+ libraryName: '중앙도서관',
17
+ startPage: 1
18
+ }, function (err) {
19
+ if(err) {
20
+ assert.ok(err.msg === 'Need a book name');
21
+ } else {
22
+ assert.fail('Need a error msg')
23
+ }
24
+ done();
25
+ });
26
+ });
27
+
28
+ it('Use invalid book title', function (done) {
29
+ lib.search({
30
+ title: 'zyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz',
31
+ libraryName: '중앙도서관',
32
+ startPage: 1
33
+ }, function (err, book) {
34
+ if(err) {
35
+ assert.fail('must have an empty booklist');
36
+ }
37
+ assert.equal(book.booklist.length, 0)
38
+ done();
39
+ });
40
+ });
41
+
12
42
  it('Show book list of one library', function (done) {
13
43
  lib.search({
14
44
  title: 'javascript',
15
45
  libraryName: '중앙도서관',
16
46
  startPage: 1
17
- }, function (err, book) {
47
+ }, function (err, book) {
18
48
  if(err) {
19
49
  assert.fail(err.msg);
20
50
  } else {
@@ -3,16 +3,46 @@ const lib = require('../src/library/suwon');
3
3
  const util = require('../src/util.js');
4
4
 
5
5
  describe('수원시 도서관 (제한시간 25초)', function () {
6
- this.timeout(25000);
6
+ this.timeout(35000);
7
+
7
8
  it('Show library list', function (done) {
8
9
  assert.ok(lib.getLibraryNames().length > 1);
9
10
  done();
10
11
  });
11
12
 
13
+ it('Use empty book title', function (done) {
14
+ lib.search({
15
+ title: '',
16
+ libraryName: '중앙도서관',
17
+ startPage: 1
18
+ }, function (err) {
19
+ if(err) {
20
+ assert.ok(err.msg === 'Need a book name');
21
+ } else {
22
+ assert.fail('Need a error msg')
23
+ }
24
+ done();
25
+ });
26
+ });
27
+
28
+ it('Use invalid book title', function (done) {
29
+ lib.search({
30
+ title: 'zyxwvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvwxyz',
31
+ libraryName: '중앙도서관',
32
+ startPage: 1
33
+ }, function (err, book) {
34
+ if(err) {
35
+ assert.fail('must have an empty booklist');
36
+ }
37
+ assert.equal(book.booklist.length, 0)
38
+ done();
39
+ });
40
+ });
41
+
12
42
  it('Show book list of one library', function (done) {
13
43
  lib.search({
14
44
  title: 'javascript',
15
- libraryName: '수원시중앙도서관',
45
+ libraryName: '중앙도서관',
16
46
  startPage: 1
17
47
  }, function (err, book) {
18
48
  if(err) {
@@ -29,7 +59,6 @@ describe('수원시 도서관 (제한시간 25초)', function () {
29
59
  });
30
60
  });
31
61
 
32
-
33
62
  it('Show book list of all libraries', function (done) {
34
63
  lib.search({
35
64
  title: 'javascript',
package/aa.html DELETED
@@ -1,27 +0,0 @@
1
- MA 남양
2
- MB 태안
3
- MC 삼괴
4
- MD 병점
5
- ME 샘내
6
- MF 두빛나래
7
- MG 봉담
8
- MH 둥지나래
9
- MI 목동이음터
10
- MJ 기아행복마루
11
- MK 동탄복합문화센터
12
- ML 송산
13
- MM 정남
14
- MN 비봉
15
- MO 진안
16
- MP 중앙이음터
17
- MQ 양감
18
- MR 다원이음터
19
- MS 송린이음터
20
- MT 팔탄
21
- MU 마도
22
- MV 봉담커피앤북
23
- MW 왕배푸른숲
24
- MX 노을빛
25
- MY 서연이음터
26
- MZ 호연이음터
27
- TA 늘봄이음터
package/files.txt DELETED
@@ -1,14 +0,0 @@
1
- ./src
2
- ./src/util.js
3
- ####./src/jquery.min.js
4
- ./src/library
5
- ./src/library/hscity.js
6
- ./src/library/gg.js
7
- ./src/library/suwon.js
8
- ./src/library/snlib.js
9
- ./src/library/gunpo.js
10
- ./src/library/osan.js
11
- ./src/dongnelibrary.js
12
- ./src/example.js
13
- ./src/cli.js
14
-
package/gg.sh DELETED
@@ -1,43 +0,0 @@
1
- http GET 'https://www.osanlibrary.go.kr/kolaseek/plus/search/plusSearchResultList.do?searchType=SIMPLE&searchCategory=ALL&searchLibraryArr=MA&searchKey=ALL&searchKeyword=apple'
2
-
3
- https://lib.goe.go.kr/gg/intro/search/index.do?menu_idx=10
4
- viewPage=1
5
- separateShelfCode=
6
- facet_manage_code=
7
- facet_author=
8
- facet_publisher=
9
- facet_pub_year=
10
- facet_subject_code=
11
- facet_media_code=
12
- reSearchTitle=
13
- reSearchAuthor=
14
- reSearchPubler=
15
- reSearchKeyword=
16
- search_text=%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8
17
- book_list=
18
- title=
19
- author=
20
- publer=
21
- keyword=
22
- search_start_date=
23
- search_end_date=
24
- subjectCode=
25
- booktype=BOOKANDNONBOOK
26
- shelfCode=
27
- libraryCodes=MA
28
- _libraryCodes=on
29
- _libraryCodes=on
30
- _libraryCodes=on
31
- _libraryCodes=on
32
- _libraryCodes=on
33
- _libraryCodes=on
34
- _libraryCodes=on
35
- _libraryCodes=on
36
- _libraryCodes=on
37
- _libraryCodes=on
38
- _libraryCodes=on
39
- sortField=NONE
40
- sortType=ASC
41
- rowCount=100#search_result
42
-
43
- https://lib.goe.go.kr/gg/intro/search/index.do?menu_idx=10&viewPage=1&search_text=%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&booktype=BOOKANDNONBOOK&libraryCodes=MA&sortField=NONE&sortType=ASC&rowCount=1000#search_result
package/osan.rest DELETED
@@ -1,127 +0,0 @@
1
- // Examples of some ElasticSearch requests.
2
- #
3
- # osan
4
- https://www.osanlibrary.go.kr
5
- POST /kolaseek/plus/search/plusSearchResultList.do
6
- {
7
- "searchType":"SIMPLE"
8
- "searchCategory":"ALL"
9
- "currentPageNo":"1"
10
- "viewStatus":"IMAGE"
11
- "preSearchKey":"ALL"
12
- "preSearchKeyword":"javascript"
13
- "searchKey":"ALL"
14
- "searchKeyword":"javascript"
15
- "searchLibraryArr":"MA"
16
- "searchSort":"SIMILAR"
17
- "searchOrder":"DESC"
18
- "searchRecordCount":"10"
19
- }
20
-
21
- # https://www.osanlibrary.go.kr/kolaseek/plus/search/plusSearchResultList.do?searchType=SIMPLE&searchCategory=ALL&searchLibraryArr=MA&searchKey=ALL&searchKeyword=javascript
22
- https://www.osanlibrary.go.kr
23
- GET /kolaseek/plus/search/plusSearchResultList.do?searchType=SIMPLE&searchCategory=ALL&searchLibraryArr=MA&searchKey=ALL&searchKeyword=javascript&searchRecordCount=20
24
-
25
- https://hscitylib.or.kr
26
- GET /intro/menu/10008/program/30001/searchResultList.do?searchType=SIMPLE&searchManageCodeArr=MK&viewType=LIST&searchPubFormCode=ALL&currentPageNo=1&preSearchKeyword=javascript&searchKeyword=javascript&searchArticle=SCORE&searchOrder=ASC&searchDisplay=20&searchAggsFromPubYear=2003&searchAggsToPubYear=2022
27
-
28
- https://hscitylib.or.kr/intro/menu/10008/program/30001/searchResultList.do?searchType=SIMPLE&searchManageCodeArr=${lcode}&viewType=LIST&searchPubFormCode=ALL&currentPageNo=1&preSearchKeyword=${etitle}&searchKeyword=${etitle}&searchArticle=SCORE&searchOrder=ASC&searchDisplay=1000
29
- https://hscitylib.or.kr/intro/menu/10008/program/30001/searchResultList.do?searchType=SIMPLE&searchManageCodeArr=MA&viewType=LIST&searchPubFormCode=ALL&currentPageNo=1&preSearchKeyword=javascript&searchKeyword=javascript&searchArticle=SCORE&searchOrder=ASC&searchDisplay=1000
30
-
31
- https://hscitylib.or.kr/intro/menu/10008/program/30001/searchResultList.do?searchType=SIMPLE&searchManageCodeArr=MK&searchKeyword=javascript
32
-
33
-
34
- https://www.gunpolib.go.kr
35
- GET /pyxis-api/1/collections/1/search?all=k%7Ca%7Cjavascript&abc=&max=1000&offset=0&rq=
36
-
37
-
38
- #
39
- # View nodes status
40
- #
41
- http://localhost:9200
42
- GET /_cat/nodes?v
43
-
44
- #
45
- # Add a new doc to the new 'testindex' and 'testtype'
46
- #
47
- http://localhost:9200
48
-
49
- POST /testindex/testtype
50
- {
51
- "name": "some name",
52
- "value": "some value",
53
- "date": "2015-01-01"
54
- }
55
-
56
- #
57
- # Search 'testindex'
58
- #
59
- http://localhost:9200
60
- GET /testindex/_search?pretty
61
-
62
- #
63
- # View mapping.
64
- #
65
- http://localhost:9200
66
- GET /testindex/testtype/_mapping?pretty
67
-
68
- #
69
- # Bulk-add new docs to 'testindex' using an external data file.
70
- #
71
- http://localhost:9200
72
-
73
- POST /testindex/_bulk?pretty
74
- @data.sample.json
75
-
76
- #
77
- # Lite search
78
- #
79
- http://localhost:9200
80
- GET /testindex/testtype/_search?pretty
81
- q=+name:FOO +value:(FOO BAR)
82
-
83
- # or
84
- http://localhost:9200
85
- GET /testindex/testtype/_search
86
- pretty&
87
- q=+name:FOO +value:(FOO BAR)
88
-
89
- #
90
- # Full-body search
91
- #
92
- http://localhost:9200
93
-
94
- POST /testindex/_search?pretty
95
- {
96
- "query": {
97
- "filtered": {
98
- "filter": {
99
- "range": {
100
- "date": {
101
- "gte": "2015-01-06",
102
- "lte": "2015-01-08"
103
- }
104
- }
105
- },
106
- "query": {
107
- "match": {
108
- "value": "FOO"
109
- }
110
- }
111
- }
112
- }
113
- }
114
-
115
- #
116
- # Delete 'testindex'
117
- #
118
- http://localhost:9200
119
- DELETE /testindex?pretty
120
-
121
- #
122
- # Check for 'testindex' existence
123
- #
124
- http://localhost:9200
125
- HEAD /testindex
126
-
127
-
package/osan.sh DELETED
@@ -1,3 +0,0 @@
1
- http POST https://www.osanlibrary.go.kr/kolaseek/plus/search/plusSearchResultList.do \
2
-
3
-