소개
지난 1차 개발기에서 URL 요약과 AI 검색 기능을 탑재한 현우봇을 소개했었죠. 그런데 막상 스터디원들과 사용하다 보니 아쉬운 점들이 하나둘 보이기 시작했습니다.
"이건 왜 요약이 안돼요?"
"현우봇이 왜 자고 있죠?"
특히 매주 스터디 전날, 일일이 지피터스 사이트에 들어가서 우리 스터디 사례글을 확인하는 것도 좋지만 사례글 링크를 한 번에 가져와 정리해 주면 좋겠다 싶었어요.
그래서 결심했습니다. "현우봇아, 너가 알아서 사례글을 정리해줘!"
진행 방법
1. 지피터스 사례글 자동 수 집의 도전
처음엔 단순하게 생각했습니다. "그냥 지피터스 사이트 크롤링하면 되겠지?"
python
# 처음 시도한 순진한 접근
async def crawl_gpters(url):
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# ... 이렇게 하면 될 줄 알았죠 😅
하지만 현실은... 빈 페이지만 돌아왔습니다. 그래서 Selenium으로 크롤링에 성공은 했지만, 속도가 너무 느렸습니다.
단순히 "최근 7일"로 하면 스터디 시간에 따라 어긋날 수 있었습니다. 그래서 각 스터디의 요일과 시간을 고려한 로직을 구현했죠.
python
@classmethod
def calculate_study_date_range(cls, study_name: str) -> Tuple[date, date]:
"""스터디 일정 기반 날짜 범위 계산"""
study_info = STUDY_INFO.get(study_name)
today = cls.get_today()
study_day = study_info["day_of_week"]
# 오늘이 스터디날인지, 스터디 시간 전인지 확인
# 지난 스터디 다음날부터 다음 스터디까지의 사례글 수집
아무튼 지난 일요일 저녁에 느린 스터디 사례글 정리봇을 공개했는데, 관계자 도움으로 지피터스 홈페이지에서 해당 정보만 빠르게 제공받을 수 있었고, 사례글을 정리해 주는 봇을 완성했습니다. 짜잔~
2. URL 요약이 안되던 사이트도 요약해랏!! (크롤링 고도화 : 3단계 폴백 시스템)
기존에 요약이 안 되던 사이트들(조선일보, Streamlit 앱 등)을 위해 3단계 폴백 시스템을 구현했습니다.
python
async def get_dynamic_content_async(url: str) -> str:
# 1단계: Firecrawl API (가장 빠름)
if not bypass_methods or "firecrawl" in bypass_methods:
try:
content = await ContentExtractor._get_content_with_firecrawl_async(url)
if content: return content
except: pass
# 2단계: ScrapingBee API (안정적)
if not bypass_methods or "scrapingbee" in bypass_methods:
try:
content = await ContentExtractor._get_content_with_scrapingbee(url)
if content: return content
except: pass
# 3단계: Playwright (최후의 수단)
if PLAYWRIGHT_AVAILABLE:
try:
content = await ContentExtractor._get_content_with_playwright(url)
if content: return content
except: pass
특히 조선일보는 광고와 팝업이 많아서 특별 처리가 필요했습니다:
python
# 리소스 로딩 차단 (이미지, 폰트, 비디오 등)
await page.route('**/*.{png,jpg,jpeg,gif,svg,pdf,woff,woff2,ttf,mp4,webm,ogg}',
lambda route: route.abort())
# 봇 감지 방지 스크립트
await page.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
""")
3. 이미지 OCR 기능 추가 - 진행중(90% 완성)
"이 이미지에 뭐라고 써있어?"라는 질문을 받을 때마다 답답했던 현우봇. 이제는 이미지도 이해합니다!
javascript
// 메신저봇R 코드에서 이미지 감지
if (imageDB && imageDB.getImageBase64 && imageDB.getImageBase64()) {
var base64Image = imageDB.getImageBase64();
ImageSessions.saveImage(sender, base64Image);
replier.reply("이미지가 수신되었습니다. 1분 이내에 '//'로 시작하는 메시지를 보내면 이미지와 함께 처리됩니다.");
}
4. 코드 리팩토링: 모듈화의 여정
처음엔 main.py 하나에 3000줄이 넘는 코드가 있었습니다. 기능을 추가할 때마다 스크롤의 압박이... 😵
# Before (v1.2)
main.py (3000+ lines)
# After (v1.3)
main.py (800 lines)
├── youtube_service.py # YouTube 관련 기능
├── scraping_service.py # 웹 스크래핑 기능
├── gpters_service.py # GPTers API 기능
├── config/
│ ├── newspaper_configs.py
│ └── dynamic_sites.py
└── utils/
└── api_status_checker.py
환경변수도 .env 파일로 분리했습니다:
bash
GEMINI_API_KEY=your_api_key
PERPLEXITY_API_KEY=your_api_key
GPTERS_API_TOKEN=your_api_token
SCRAPINGBEE_API_KEY=your_api_key
FIRECRAWL_API_KEY=your_api_key
결과와 배운 점
성공적인 결과들
스터디글 자동 정리: 이제 카톡방에서 "스터디글"만 입력하면 자동으로 정리됩니다!
# 16기 CTO 사례글 목록 (11.21. ~ 11.28. / 9개) ## 작성인원(9명): 이사라, WOORIM LEE, 김현우... 1. 이사라 / 윈드서프 너 뭐 돼? (feat. 동그란토마토) https://www.gpters.org/...
향상된 크롤링 성공률: 조선일보 95%, Streamlit 앱 90%, GPTers 100% 성공!
이미지 이해 능력: 스크린샷, 차트, 텍스트 이미지 모두 분석 가능
시행착오와 교훈
API 우선 접근: 크롤링보다 API가 있다면 무조건 API를 사용하자
폴백의 중요성: 한 가지 방법에만 의존하지 말고 여러 대안을 준비
모듈화는 필수: 기능이 늘어나면 모듈화로 유지보수 용이하게~
봇 감지 대응: User-Agent 랜덤화, 지연 시간, 브라우저 지문 위장 등
앞으로의 계획
8시만 되면 조간뉴스 자동으로 슉~: n8n과 연결