정부지원사업 공고 수집 데몬(gov-scraper) 추가
- government/ Node 데몬: Open API 우선 + HTML 보조 + 디스커버리 전략 - Strategy 패턴 소스 어댑터: KStartupApiSource(공공데이터 Open API), GenericHtmlSource(config 기반) - sundol 3단계 폴백 크롤러(cheerio→Jina→Playwright CDP) Node 재구현, sundol-chrome(9222) 재사용 - Oracle thick 모드(Instant Client + sso 지갑) 접속, gov_source/gov_opportunity 적재(중복제거) - K-Startup 29,017건 + 중기부(mss) 30건 적재 검증, PM2 gov-daemon 등록(60분 주기) - 기업마당(bizinfo)은 자체 crtfcKey 발급 대기 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
62
government/src/sources/base.js
Normal file
62
government/src/sources/base.js
Normal file
@@ -0,0 +1,62 @@
|
||||
// OpportunitySource — Strategy 인터페이스.
|
||||
// 소스(사이트)별 어댑터는 이 클래스를 상속해 list()/fetchDetail() 을 구현한다.
|
||||
import { crawl } from '../crawler/crawler.js';
|
||||
|
||||
/**
|
||||
* 공고 목록 항목 형태:
|
||||
* {
|
||||
* externalId: string, // 소스 고유 키 (필수, dedup)
|
||||
* title: string, // 제목 (필수)
|
||||
* agency?: string, // 소관/주관기관
|
||||
* category?: string, // 지원분야
|
||||
* target?: string, // 지원대상
|
||||
* applyStart?: Date, // 접수 시작
|
||||
* applyEnd?: Date, // 접수 마감
|
||||
* detailUrl?: string, // 상세 페이지 URL
|
||||
* raw?: object, // 원본 데이터(JSON 저장)
|
||||
* }
|
||||
*/
|
||||
export class OpportunitySource {
|
||||
/** @param {{code:string,name:string,baseUrl?:string,type:'API'|'HTML',config?:object}} meta */
|
||||
constructor(meta) {
|
||||
if (!meta.code || !meta.name || !meta.type) {
|
||||
throw new Error('OpportunitySource meta 에 code/name/type 필수');
|
||||
}
|
||||
this.code = meta.code;
|
||||
this.name = meta.name;
|
||||
this.baseUrl = meta.baseUrl || null;
|
||||
this.type = meta.type;
|
||||
this.config = meta.config || {};
|
||||
}
|
||||
|
||||
meta() {
|
||||
return {
|
||||
code: this.code,
|
||||
name: this.name,
|
||||
baseUrl: this.baseUrl,
|
||||
type: this.type,
|
||||
config: this.config,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 공고 목록을 수집한다. 하위 클래스에서 반드시 구현.
|
||||
* @returns {Promise<Array>}
|
||||
*/
|
||||
async list() {
|
||||
throw new Error(`${this.code}: list() 미구현`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 상세 본문을 수집한다. 기본 구현은 detailUrl 을 3단계 폴백 크롤러로 긁는다.
|
||||
* API 처럼 본문이 이미 raw 에 있는 소스는 이 메서드를 오버라이드한다.
|
||||
* @param {{id:string, externalId:string, detailUrl:string, raw:object|null}} row
|
||||
* @returns {Promise<string>} 본문 텍스트
|
||||
*/
|
||||
async fetchDetail(row) {
|
||||
if (!row.detailUrl) {
|
||||
throw new Error(`${this.code}/${row.externalId}: detailUrl 없음 — 상세 수집 불가`);
|
||||
}
|
||||
return crawl(row.detailUrl);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user