pal-crawl 1.6.1 → 1.8.0

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
@@ -20,9 +20,16 @@
20
20
  - [Configuration](#configuration)
21
21
  - [Base Types](#base-types)
22
22
  - [Methods](#methods)
23
- - [get](#get)
24
- - [getContentHTML](#getcontenthtml--promisestring)
23
+ - [get](#get--promiseitable-data)
24
+ - [getContentHTML](#getcontenthtmlid-string--promisestring)
25
25
  - [getContent](#getcontentid-string--promiseicontentdata)
26
+ - [getDone](#getdone--promiseitable-data)
27
+ - [getDoneContentHTML](#getdonecontenthtmlid-string--promisestring)
28
+ - [getDoneContent](#getdonecontentid-string--promiseicontentdata)
29
+ - [Screenshot APIs](#screenshot-apis)
30
+ - [search / searchDone](#searchquery-isearchquery--promiseisearchresult)
31
+ - [getPage / getDonePage](#getpagepageindex-number-pageunit-number--promiseitable-data)
32
+ - [getAllPages / getAllDonePages](#getallpagesquery-isearchquery-options-ibulkoptions--asyncgeneratorisearchresult)
26
33
  - [Examples](#examples)
27
34
  - [License](#license)
28
35
 
@@ -52,9 +59,21 @@ interface PalCrawlConfig {
52
59
  timeout?: number; // HTTP 요청 타임아웃 (밀리초, 기본값: 10000)
53
60
  retryCount?: number; // 재시도 횟수 (기본값: 3)
54
61
  customHeaders?: Record<string, string>; // 사용자 정의 헤더
62
+ screenshot?: ScreenshotOptions; // 헤드리스 크로뮴 스크린샷 옵션
63
+ }
64
+
65
+ interface ScreenshotOptions {
66
+ enabled?: boolean; // 스크린샷 기능 활성화 여부 (기본값: false)
67
+ fullPage?: boolean; // 전체 페이지 캡처 여부 (기본값: true)
68
+ width?: number; // 뷰포트 너비 (기본값: 1920)
69
+ height?: number; // 뷰포트 높이 (기본값: 1080)
70
+ format?: 'png' | 'jpeg'; // 이미지 포맷 (기본값: 'png')
71
+ quality?: number; // jpeg 품질 0~100 (기본값: 80)
55
72
  }
56
73
  ```
57
74
 
75
+ 스크린샷 기능은 기본적으로 비활성화되어 있으며, `screenshot.enabled: true`일 때만 동작합니다.
76
+
58
77
  ### 기본 사용법
59
78
 
60
79
  ```typescript
@@ -119,11 +138,57 @@ interface IContentData {
119
138
  }
120
139
  ```
121
140
 
141
+ `ISearchResult`는 검색/페이지 조회 결과를 나타내는 인터페이스입니다.
142
+
143
+ ```typescript
144
+ interface ISearchResult {
145
+ total: number; // 전체 건수
146
+ totalPages: number; // 전체 페이지 수
147
+ currentPage: number; // 현재 페이지
148
+ items: ITableData[]; // 현재 페이지 항목 목록
149
+ }
150
+ ```
151
+
152
+ `ISearchQuery`는 검색 필터 옵션을 나타내는 인터페이스입니다.
153
+
154
+ ```typescript
155
+ interface ISearchQuery {
156
+ pageIndex?: number; // 페이지 번호 (기본값: 1)
157
+ pageUnit?: number; // 페이지당 항목 수 (5~9, 20, 30, 50, 100; 기본값: 10)
158
+ committeeId?: string; // 소관위원회 ID (예: '9700006' = 법제사법위원회)
159
+ billName?: string; // 법안명 검색어
160
+ represent?: string; // 대표발의자 검색어
161
+ proposers?: string; // 공동발의자 검색어 (쉼표 구분 AND 조건)
162
+ ppslRsonMnCn?: string; // 제안이유 검색어
163
+ sortCol?:
164
+ | 'BILL_NO'
165
+ | 'BILL_NAME'
166
+ | 'CURR_COMMITTEE'
167
+ | 'OPN_CNT'
168
+ | 'LGSLT_PA_RG_DT';
169
+ sortGbn?: 'DESC' | 'ASC';
170
+ // 종료된 입법예고 전용:
171
+ fromAge?: number; // 시작 대수 (예: 21)
172
+ toAge?: number; // 종료 대수 (예: 22)
173
+ billNo?: string; // 의안번호 검색어
174
+ }
175
+ ```
176
+
177
+ `IBulkOptions`는 대량 수집 시 동작을 제어하는 인터페이스입니다.
178
+
179
+ ```typescript
180
+ interface IBulkOptions {
181
+ delayMs?: number; // 페이지 간 요청 지연 (밀리초, 기본값: 500)
182
+ concurrency?: number; // 동시 요청 수 (기본값: 1)
183
+ maxPages?: number; // 최대 수집 페이지 수 (기본값: 전체)
184
+ }
185
+ ```
186
+
122
187
  ---
123
188
 
124
189
  ## Methods
125
190
 
126
- ### get() => Promise<ITableData[]>
191
+ ### get() => Promise\<ITableData[]>
127
192
 
128
193
  `get` 메서드는 진행 중인 입법 예고 데이터를 가져옵니다.
129
194
 
@@ -140,7 +205,7 @@ console.log(table);
140
205
 
141
206
  ### getContentHTML(id: string) => Promise\<string>
142
207
 
143
- `getContentHTML` 메서드는 특정 의안 ID의 본문 페이지 HTML 원문을 가져옵니다.
208
+ `getContentHTML` 메서드는 진행 중인 입법 예고 중 특정 의안 ID의 본문 페이지 HTML 원문을 가져옵니다.
144
209
 
145
210
  ```typescript
146
211
  import { PalCrawl } from 'pal-crawl';
@@ -179,6 +244,223 @@ console.log(content);
179
244
  // }
180
245
  ```
181
246
 
247
+ ### getDone() => Promise\<ITableData[]>
248
+
249
+ `getDone` 메서드는 종료된 입법 예고 데이터를 가져옵니다.
250
+
251
+ 반환되는 각 항목에는 `contentId`가 포함되며, 이 값을 `getDoneContentHTML` 또는 `getDoneContent`에 전달해 본문 조회를 바로 수행할 수 있습니다.
252
+
253
+ ```typescript
254
+ import { PalCrawl } from 'pal-crawl';
255
+
256
+ const palCrawl = new PalCrawl();
257
+ const table = await palCrawl.getDone();
258
+
259
+ console.log(table);
260
+ ```
261
+
262
+ ### getDoneContentHTML(id: string) => Promise\<string>
263
+
264
+ `getDoneContentHTML` 메서드는 종료된 입법 예고 중 특정 의안 ID의 본문 페이지 HTML 원문을 가져옵니다.
265
+
266
+ ```typescript
267
+ import { PalCrawl } from 'pal-crawl';
268
+
269
+ const palCrawl = new PalCrawl();
270
+ const table = await palCrawl.getDone();
271
+
272
+ const id = table[0]?.contentId;
273
+ if (id) {
274
+ const contentHtml = await palCrawl.getDoneContentHTML(id);
275
+ console.log(contentHtml);
276
+ }
277
+ ```
278
+
279
+ ### getDoneContent(id: string) => Promise\<IContentData>
280
+
281
+ `getDoneContent` 메서드는 종료된 입법 예고 중 특정 의안 ID의 본문 페이지를 파싱해 JSON 객체(`IContentData`)로 반환합니다.
282
+
283
+ ```typescript
284
+ import { PalCrawl } from 'pal-crawl';
285
+
286
+ const palCrawl = new PalCrawl();
287
+ const content = await palCrawl.getDoneContent(
288
+ 'PRC_S2R6N0M3K2J3K1J5K3S8R3P4O3P5X6',
289
+ );
290
+
291
+ console.log(content);
292
+ // {
293
+ // title: '[2218503] 전자장치 부착 등에 관한 법률 일부개정법률안 (김기현의원 등 10인)',
294
+ // proposalReason: '...',
295
+ // billNumber: '2218503',
296
+ // proposer: '김기현의원 등 10인',
297
+ // ...
298
+ // }
299
+ ```
300
+
301
+ ---
302
+
303
+ ### Screenshot APIs
304
+
305
+ 헤드리스 크로뮴(Puppeteer) 기반으로 웹페이지 스크린샷을 `Buffer`로 받을 수 있습니다.
306
+
307
+ ### initBrowser() => Promise\<void>
308
+
309
+ 브라우저 인스턴스를 미리 초기화합니다. 스크린샷 호출 시 자동 초기화되므로 선택적으로 사용하면 됩니다.
310
+
311
+ ### closeBrowser() => Promise\<void>
312
+
313
+ 열려 있는 브라우저 인스턴스를 종료합니다. 스크린샷 작업 후 호출을 권장합니다.
314
+
315
+ ### takeScreenshot(url: string) => Promise\<Buffer>
316
+
317
+ 임의 URL을 열어 스크린샷 이미지 버퍼를 반환합니다.
318
+
319
+ ### getPalScreenshot() => Promise\<Buffer>
320
+
321
+ 진행 중 입법예고 목록 페이지 스크린샷 버퍼를 반환합니다.
322
+
323
+ ### getContentScreenshot(id: string) => Promise\<Buffer>
324
+
325
+ 진행 중 입법예고 본문 페이지(의안 ID 기준) 스크린샷 버퍼를 반환합니다.
326
+
327
+ ### getDoneScreenshot() => Promise\<Buffer>
328
+
329
+ 종료된 입법예고 목록 페이지 스크린샷 버퍼를 반환합니다.
330
+
331
+ ### getDoneContentScreenshot(id: string) => Promise\<Buffer>
332
+
333
+ 종료된 입법예고 본문 페이지(의안 ID 기준) 스크린샷 버퍼를 반환합니다.
334
+
335
+ ### updateScreenshotConfig(config: Partial\<ScreenshotOptions>) => void
336
+
337
+ 런타임에 스크린샷 옵션을 변경합니다.
338
+
339
+ ```typescript
340
+ import { PalCrawl } from 'pal-crawl';
341
+
342
+ const palCrawl = new PalCrawl({
343
+ screenshot: {
344
+ enabled: true,
345
+ format: 'png',
346
+ fullPage: true,
347
+ },
348
+ });
349
+
350
+ palCrawl.updateScreenshotConfig({ format: 'jpeg', quality: 85 });
351
+ ```
352
+
353
+ ---
354
+
355
+ ### search(query?: ISearchQuery) => Promise\<ISearchResult>
356
+
357
+ `search` 메서드는 진행 중인 입법 예고를 조건에 맞게 검색합니다. 검색 결과에는 총 건수, 페이지 정보, 항목 목록이 포함됩니다.
358
+
359
+ ```typescript
360
+ import { PalCrawl } from 'pal-crawl';
361
+
362
+ const palCrawl = new PalCrawl();
363
+
364
+ // 기본 조회 (1페이지 10건)
365
+ const result = await palCrawl.search();
366
+ console.log(result.total, result.totalPages, result.items);
367
+
368
+ // 법안명 필터 + 정렬
369
+ const filtered = await palCrawl.search({
370
+ billName: '도로교통법',
371
+ sortCol: 'BILL_NAME',
372
+ sortGbn: 'ASC',
373
+ pageUnit: 20,
374
+ });
375
+ ```
376
+
377
+ ### searchDone(query?: ISearchQuery) => Promise\<ISearchResult>
378
+
379
+ `searchDone` 메서드는 종료된 입법 예고를 조건에 맞게 검색합니다. `fromAge`, `toAge`, `billNo` 필터를 추가로 지원합니다.
380
+
381
+ ```typescript
382
+ import { PalCrawl } from 'pal-crawl';
383
+
384
+ const palCrawl = new PalCrawl();
385
+
386
+ // 22대 종료 입법예고만 조회
387
+ const result = await palCrawl.searchDone({ fromAge: 22, toAge: 22 });
388
+ console.log(result.total, result.items);
389
+ ```
390
+
391
+ ---
392
+
393
+ ### getPage(pageIndex: number, pageUnit?: number) => Promise\<ITableData[]>
394
+
395
+ `getPage` 메서드는 진행 중인 입법 예고에서 특정 페이지의 항목을 가져옵니다.
396
+
397
+ ```typescript
398
+ import { PalCrawl } from 'pal-crawl';
399
+
400
+ const palCrawl = new PalCrawl();
401
+
402
+ const page1 = await palCrawl.getPage(1); // 1페이지 (기본 10건)
403
+ const page2 = await palCrawl.getPage(2, 20); // 2페이지, 20건씩
404
+ ```
405
+
406
+ ### getDonePage(pageIndex: number, pageUnit?: number) => Promise\<ITableData[]>
407
+
408
+ `getDonePage` 메서드는 종료된 입법 예고에서 특정 페이지의 항목을 가져옵니다.
409
+
410
+ ```typescript
411
+ import { PalCrawl } from 'pal-crawl';
412
+
413
+ const palCrawl = new PalCrawl();
414
+ const page3 = await palCrawl.getDonePage(3);
415
+ ```
416
+
417
+ ---
418
+
419
+ ### getAllPages(query?: ISearchQuery, options?: IBulkOptions) => AsyncGenerator\<ISearchResult>
420
+
421
+ `getAllPages` 메서드는 진행 중인 입법 예고를 여러 페이지에 걸쳐 순차적으로 수집하는 비동기 제너레이터입니다. `for await...of` 루프로 한 페이지씩 처리할 수 있습니다.
422
+
423
+ - `delayMs` (기본값: 500): 페이지 요청 사이의 지연 시간(ms). 서버 부하를 줄이기 위해 설정하세요.
424
+ - `concurrency` (기본값: 1): 동시에 요청할 페이지 수.
425
+ - `maxPages` (기본값: 전체 페이지): 수집할 최대 페이지 수.
426
+
427
+ ```typescript
428
+ import { PalCrawl } from 'pal-crawl';
429
+
430
+ const palCrawl = new PalCrawl();
431
+
432
+ // 처음 5 페이지를 순서대로 수집
433
+ for await (const page of palCrawl.getAllPages(
434
+ {},
435
+ { maxPages: 5, delayMs: 300 },
436
+ )) {
437
+ console.log(
438
+ `페이지 ${page.currentPage}/${page.totalPages}: ${page.items.length}건`,
439
+ );
440
+ for (const item of page.items) {
441
+ console.log(item.subject);
442
+ }
443
+ }
444
+ ```
445
+
446
+ ### getAllDonePages(query?: ISearchQuery, options?: IBulkOptions) => AsyncGenerator\<ISearchResult>
447
+
448
+ `getAllDonePages`는 종료된 입법 예고에 대한 대량 수집 제너레이터입니다.
449
+
450
+ ```typescript
451
+ import { PalCrawl } from 'pal-crawl';
452
+
453
+ const palCrawl = new PalCrawl();
454
+
455
+ // 22대 종료 입법예고 전체 수집 (concurrency=2, 지연 200ms)
456
+ for await (const page of palCrawl.getAllDonePages(
457
+ { fromAge: 22, toAge: 22 },
458
+ { concurrency: 2, delayMs: 200 },
459
+ )) {
460
+ console.log(`${page.currentPage}/${page.totalPages}: ${page.items.length}건`);
461
+ }
462
+ ```
463
+
182
464
  ---
183
465
 
184
466
  ## Examples
@@ -243,6 +525,109 @@ const palCrawl = new PalCrawl(config);
243
525
  const data = await palCrawl.get();
244
526
  ```
245
527
 
528
+ ### 종료된 입법예고 목록에서 본문 가져오기
529
+
530
+ ```typescript
531
+ import { PalCrawl } from 'pal-crawl';
532
+
533
+ const palCrawl = new PalCrawl();
534
+ const table = await palCrawl.getDone();
535
+
536
+ const first = table.find((item) => item.contentId);
537
+ if (first?.contentId) {
538
+ const content = await palCrawl.getDoneContent(first.contentId);
539
+ console.log(content.title);
540
+ console.log(content.proposalReason);
541
+ console.log(content.billNumber);
542
+ console.log(content.noticePeriod);
543
+ }
544
+ ```
545
+
546
+ ### 검색 필터를 사용해 법안 조회하기
547
+
548
+ ```typescript
549
+ import { PalCrawl } from 'pal-crawl';
550
+
551
+ const palCrawl = new PalCrawl();
552
+
553
+ // 법안명 검색
554
+ const result = await palCrawl.search({ billName: '도로교통법', pageUnit: 20 });
555
+ console.log(`총 ${result.total}건 (${result.totalPages}페이지)`);
556
+ result.items.forEach((item) => console.log(item.subject));
557
+
558
+ // 22대 종료 입법예고에서 의안번호로 검색
559
+ const done = await palCrawl.searchDone({
560
+ fromAge: 22,
561
+ toAge: 22,
562
+ billNo: '2218',
563
+ });
564
+ done.items.forEach((item) => console.log(item.subject));
565
+ ```
566
+
567
+ ### 대량 수집 (getAllPages / getAllDonePages)
568
+
569
+ ```typescript
570
+ import { PalCrawl, type ITableData } from 'pal-crawl';
571
+
572
+ const palCrawl = new PalCrawl();
573
+ const allItems: ITableData[] = [];
574
+
575
+ // 진행 중인 입법예고 전체 페이지 수집 (500ms 지연)
576
+ for await (const page of palCrawl.getAllPages({}, { delayMs: 500 })) {
577
+ allItems.push(...page.items);
578
+ console.log(`수집 중: ${page.currentPage}/${page.totalPages} 페이지`);
579
+ }
580
+
581
+ console.log(`전체 ${allItems.length}건 수집 완료`);
582
+ ```
583
+
584
+ ```typescript
585
+ import { PalCrawl } from 'pal-crawl';
586
+
587
+ const palCrawl = new PalCrawl();
588
+
589
+ // 처음 10 페이지만, 동시 2개 요청으로 빠르게 수집
590
+ for await (const page of palCrawl.getAllDonePages(
591
+ { sortCol: 'LGSLT_PA_RG_DT', sortGbn: 'DESC' },
592
+ { maxPages: 10, concurrency: 2, delayMs: 200 },
593
+ )) {
594
+ console.log(
595
+ `페이지 ${page.currentPage}: ${page.items.map((i) => i.subject).join(', ')}`,
596
+ );
597
+ }
598
+ ```
599
+
600
+ ### 법률안 본문 스크린샷 버퍼 받기 (선택적 헤드리스 크로뮴)
601
+
602
+ ```typescript
603
+ import fs from 'node:fs';
604
+ import { PalCrawl } from 'pal-crawl';
605
+
606
+ const palCrawl = new PalCrawl({
607
+ screenshot: {
608
+ enabled: true,
609
+ fullPage: true,
610
+ width: 1920,
611
+ height: 1080,
612
+ format: 'png',
613
+ },
614
+ });
615
+
616
+ try {
617
+ const table = await palCrawl.get();
618
+ const first = table.find((item) => item.contentId);
619
+
620
+ if (first?.contentId) {
621
+ const imageBuffer = await palCrawl.getContentScreenshot(first.contentId);
622
+ fs.writeFileSync('bill-content.png', imageBuffer);
623
+ }
624
+ } finally {
625
+ await palCrawl.closeBrowser();
626
+ }
627
+ ```
628
+
629
+ `takeScreenshot`, `getPalScreenshot`, `getContentScreenshot`, `getDoneScreenshot`, `getDoneContentScreenshot`는 모두 `Buffer`를 반환합니다.
630
+
246
631
  ### 에러 처리
247
632
 
248
633
  ```typescript
package/dist/config.d.ts CHANGED
@@ -2,5 +2,7 @@ export declare enum Config {
2
2
  DOMAIN = "https://pal.assembly.go.kr",
3
3
  LIST_URL = "/napal/lgsltpa/lgsltpaOngoing/list.do",
4
4
  CONTENT_URL = "/napal/lgsltpa/lgsltpaOngoing/view.do",
5
+ DONE_LIST_URL = "/napal/lgsltpa/lgsltpaDone/list.do",
6
+ DONE_CONTENT_URL = "/napal/lgsltpa/lgsltpaDone/view.do",
5
7
  UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
6
8
  }
package/dist/config.js CHANGED
@@ -6,6 +6,8 @@ var Config;
6
6
  Config["DOMAIN"] = "https://pal.assembly.go.kr";
7
7
  Config["LIST_URL"] = "/napal/lgsltpa/lgsltpaOngoing/list.do";
8
8
  Config["CONTENT_URL"] = "/napal/lgsltpa/lgsltpaOngoing/view.do";
9
+ Config["DONE_LIST_URL"] = "/napal/lgsltpa/lgsltpaDone/list.do";
10
+ Config["DONE_CONTENT_URL"] = "/napal/lgsltpa/lgsltpaDone/view.do";
9
11
  Config["UserAgent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
10
12
  })(Config || (exports.Config = Config = {}));
11
13
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;AAAA,IAAY,MAKX;AALD,WAAY,MAAM;IAChB,+CAAqC,CAAA;IACrC,4DAAkD,CAAA;IAClD,+DAAqD,CAAA;IACrD,uIAA6H,CAAA;AAC/H,CAAC,EALW,MAAM,sBAAN,MAAM,QAKjB"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;AAAA,IAAY,MAOX;AAPD,WAAY,MAAM;IAChB,+CAAqC,CAAA;IACrC,4DAAkD,CAAA;IAClD,+DAAqD,CAAA;IACrD,8DAAoD,CAAA;IACpD,iEAAuD,CAAA;IACvD,uIAA6H,CAAA;AAC/H,CAAC,EAPW,MAAM,sBAAN,MAAM,QAOjB"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- export { PalCrawl, type ITableData, type IAttachment, type IContentData, type PalCrawlConfig, } from './pal';
1
+ export { PalCrawl, type ITableData, type IAttachment, type IContentData, type ISearchResult, type ISearchQuery, type IBulkOptions, type PalCrawlConfig, type ScreenshotOptions, } from './pal';
2
+ export { PalParser } from './parser';
2
3
  export { Config } from './config';
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Config = exports.PalCrawl = void 0;
3
+ exports.Config = exports.PalParser = exports.PalCrawl = void 0;
4
4
  var pal_1 = require("./pal");
5
5
  Object.defineProperty(exports, "PalCrawl", { enumerable: true, get: function () { return pal_1.PalCrawl; } });
6
+ var parser_1 = require("./parser");
7
+ Object.defineProperty(exports, "PalParser", { enumerable: true, get: function () { return parser_1.PalParser; } });
6
8
  var config_1 = require("./config");
7
9
  Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return config_1.Config; } });
8
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6BAMe;AALb,+FAAA,QAAQ,OAAA;AAMV,mCAAkC;AAAzB,gGAAA,MAAM,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6BAUe;AATb,+FAAA,QAAQ,OAAA;AAUV,mCAAqC;AAA5B,mGAAA,SAAS,OAAA;AAClB,mCAAkC;AAAzB,gGAAA,MAAM,OAAA"}
package/dist/pal.d.ts CHANGED
@@ -1,45 +1,108 @@
1
+ import { type ITableData, type IContentData, type ISearchResult } from './parser';
2
+ export type { IAttachment, ITableData, IContentData, ISearchResult, } from './parser';
3
+ export interface ScreenshotOptions {
4
+ enabled?: boolean;
5
+ fullPage?: boolean;
6
+ width?: number;
7
+ height?: number;
8
+ format?: 'png' | 'jpeg';
9
+ quality?: number;
10
+ }
1
11
  export interface PalCrawlConfig {
2
12
  userAgent?: string;
3
13
  timeout?: number;
4
14
  retryCount?: number;
5
15
  customHeaders?: Record<string, string>;
16
+ screenshot?: ScreenshotOptions;
6
17
  }
7
- export interface IAttachment {
8
- pdfFile: string | null;
9
- hwpFile: string | null;
10
- }
11
- export interface ITableData {
12
- num: number;
13
- subject: string;
14
- proposerCategory: string;
15
- committee: string;
16
- numComments: number;
17
- link: string;
18
- contentId: string | null;
19
- attachments: IAttachment;
18
+ export interface ISearchQuery {
19
+ pageIndex?: number;
20
+ pageUnit?: number;
21
+ committeeId?: string;
22
+ billName?: string;
23
+ represent?: string;
24
+ proposers?: string;
25
+ ppslRsonMnCn?: string;
26
+ sortCol?: 'BILL_NO' | 'BILL_NAME' | 'CURR_COMMITTEE' | 'OPN_CNT' | 'LGSLT_PA_RG_DT';
27
+ sortGbn?: 'DESC' | 'ASC';
28
+ fromAge?: number;
29
+ toAge?: number;
30
+ billNo?: string;
20
31
  }
21
- export interface IContentData {
22
- title: string;
23
- proposalReason: string | null;
24
- billNumber: string | null;
25
- proposer: string | null;
26
- proposalDate: string | null;
27
- committee: string | null;
28
- referralDate: string | null;
29
- noticePeriod: string | null;
30
- proposalSession: string | null;
32
+ export interface IBulkOptions {
33
+ delayMs?: number;
34
+ concurrency?: number;
35
+ maxPages?: number;
31
36
  }
32
37
  export declare class PalCrawl {
33
38
  private readonly httpClient;
39
+ private readonly parser;
40
+ private browser;
41
+ private screenshotConfig;
34
42
  constructor(config?: PalCrawlConfig);
43
+ /**
44
+ * Initialize the Puppeteer browser instance.
45
+ * Call this before taking screenshots if not already initialized.
46
+ */
47
+ initBrowser(): Promise<void>;
48
+ /**
49
+ * Close the Puppeteer browser instance.
50
+ * Call this when done to free up resources.
51
+ */
52
+ closeBrowser(): Promise<void>;
53
+ /**
54
+ * Take a screenshot of a URL and return it as a Buffer.
55
+ * Initializes browser if not already done.
56
+ */
57
+ takeScreenshot(urlStr: string): Promise<Buffer>;
58
+ /**
59
+ * Get a screenshot of the bill list page.
60
+ */
61
+ getPalScreenshot(): Promise<Buffer>;
62
+ /**
63
+ * Get a screenshot of a specific bill content page.
64
+ */
65
+ getContentScreenshot(id: string): Promise<Buffer>;
66
+ /**
67
+ * Get a screenshot of the done bills list page.
68
+ */
69
+ getDoneScreenshot(): Promise<Buffer>;
70
+ /**
71
+ * Get a screenshot of a specific done bill content page.
72
+ */
73
+ getDoneContentScreenshot(id: string): Promise<Buffer>;
74
+ /**
75
+ * Update screenshot configuration.
76
+ */
77
+ updateScreenshotConfig(config: Partial<ScreenshotOptions>): void;
78
+ private buildListUrl;
35
79
  getPalHTML(): Promise<string>;
36
- private extractContentId;
37
80
  parseTable(html: string): ITableData[];
38
- private normalizeText;
39
- private extractTableCellText;
40
- private parseBillInfoTable;
41
81
  parseContent(html: string): IContentData;
42
82
  get(): Promise<ITableData[]>;
43
83
  getContentHTML(id: string): Promise<string>;
44
84
  getContent(id: string): Promise<IContentData>;
85
+ getDoneHTML(): Promise<string>;
86
+ getDone(): Promise<ITableData[]>;
87
+ getDoneContentHTML(id: string): Promise<string>;
88
+ getDoneContent(id: string): Promise<IContentData>;
89
+ /** Fetch ongoing notices with optional filters and return parsed result with metadata. */
90
+ search(query?: ISearchQuery): Promise<ISearchResult>;
91
+ /** Fetch done notices with optional filters and return parsed result with metadata. */
92
+ searchDone(query?: ISearchQuery): Promise<ISearchResult>;
93
+ /** Fetch a specific page of ongoing notices. */
94
+ getPage(pageIndex: number, pageUnit?: number): Promise<ITableData[]>;
95
+ /** Fetch a specific page of done notices. */
96
+ getDonePage(pageIndex: number, pageUnit?: number): Promise<ITableData[]>;
97
+ private _getAllPagesImpl;
98
+ /**
99
+ * Async generator that yields every page of ongoing notices.
100
+ * Respects `IBulkOptions` for rate-limiting and concurrency.
101
+ */
102
+ getAllPages(query?: Omit<ISearchQuery, 'pageIndex'>, options?: IBulkOptions): AsyncGenerator<ISearchResult>;
103
+ /**
104
+ * Async generator that yields every page of done notices.
105
+ * Respects `IBulkOptions` for rate-limiting and concurrency.
106
+ */
107
+ getAllDonePages(query?: Omit<ISearchQuery, 'pageIndex'>, options?: IBulkOptions): AsyncGenerator<ISearchResult>;
45
108
  }