fix(festival-bot): 한글 URL 디코딩으로 Gemini url_context 실패 해결
퍼센트 인코딩된 긴 한글 URL(%EC%BA%90...)을 Gemini가 도구 호출 시
잘못 복사해 fetch가 실패('URL 불일치')하고 no_event로 기록되던 문제
(캐리비안 베이 글 누락 원인). 스크래퍼가 URL을 한글로 디코딩해 전달하고
source_url 매칭도 디코딩 기준으로 정규화. 기존 크롤로그 24건 디코딩
마이그레이션 + 캐리비안 베이 재처리 → 7/4 일정 생성 확인.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
f0eae805e6
commit
5c31977411
2 changed files with 19 additions and 6 deletions
|
|
@ -4,7 +4,7 @@
|
|||
* 수집하여 행사 일정 자동 생성. Gemini 한 번의 호출로 두 타입을 함께 추출(type 필드로 구분).
|
||||
*/
|
||||
import fp from 'fastify-plugin';
|
||||
import { fetchSearchPostUrls } from './scraper.js';
|
||||
import { fetchSearchPostUrls, decodeUrl } from './scraper.js';
|
||||
import { extractFestivalsFromUrls } from './gemini.js';
|
||||
import { createEventSchedule, searchKakaoPlace, kakaoToVenue } from '../event.js';
|
||||
import { CATEGORY_IDS } from '../../config/index.js';
|
||||
|
|
@ -206,10 +206,10 @@ async function festivalBotPlugin(fastify, opts) {
|
|||
throw err; // scheduler의 consecutiveErrors 처리로 전달
|
||||
}
|
||||
|
||||
// 결과를 source_url별로 그룹화
|
||||
// 결과를 source_url별로 그룹화 (인코딩 차이 흡수를 위해 디코딩 기준으로 매칭)
|
||||
const bySource = new Map();
|
||||
for (const f of festivals) {
|
||||
const src = f.source_url || '';
|
||||
const src = decodeUrl(f.source_url || '');
|
||||
if (!bySource.has(src)) bySource.set(src, []);
|
||||
bySource.get(src).push(f);
|
||||
}
|
||||
|
|
@ -226,7 +226,7 @@ async function festivalBotPlugin(fastify, opts) {
|
|||
|
||||
// 4. 배치의 모든 URL을 크롤 로그에 기록
|
||||
for (const url of batch) {
|
||||
const matched = bySource.get(url) || [];
|
||||
const matched = bySource.get(decodeUrl(url)) || [];
|
||||
const status = matched.length > 0 ? 'processed' : 'no_event';
|
||||
await logCrawled(url, status, matched.length);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,11 +30,24 @@ async function fetchWithTimeout(url, timeout = FETCH_TIMEOUT) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 퍼센트 인코딩된 한글 URL을 디코딩 (실패 시 원본 유지)
|
||||
* Gemini가 긴 %인코딩 시퀀스를 도구 호출 시 잘못 복사해 fetch가 실패하는 문제 방지.
|
||||
* 한글 그대로 전달하면 모델이 정확히 복사하고 url_context가 알아서 인코딩해 가져옴.
|
||||
*/
|
||||
export function decodeUrl(url) {
|
||||
try {
|
||||
return decodeURIComponent(url);
|
||||
} catch {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML에서 게시글(/entry/) URL 추출
|
||||
* @param {string} html - 검색 페이지 HTML
|
||||
* @param {string} origin - 사이트 origin (예: https://memogipost.tistory.com)
|
||||
* @returns {string[]} 절대 URL 배열
|
||||
* @returns {string[]} 절대 URL 배열 (한글 디코딩됨)
|
||||
*/
|
||||
export function extractEntryUrls(html, origin) {
|
||||
const urls = new Set();
|
||||
|
|
@ -48,7 +61,7 @@ export function extractEntryUrls(html, origin) {
|
|||
}
|
||||
// 쿼리스트링/해시 제거
|
||||
url = url.split('#')[0].split('?')[0];
|
||||
urls.add(url);
|
||||
urls.add(decodeUrl(url));
|
||||
}
|
||||
return [...urls];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue