Docs | A- | A+ | -/-
https://www.technologyreview.kr/feed/ RSS를 읽어 SQLite3 데이터베이스 ~/docs/mit-reviws.db의 mit_tech_reviews 테이블에 저장하는 Node.js 스크립트 archive-mit-tech-review-kr.js를 작성했다.
실행 방식은 다음과 같다.
node archive-mit-tech-review-kr.js
cron으로 주기 실행할 예정이므로 중복 저장 방지가 필요했고, 동일 기사 판별 기준은 url로 잡았다.
초기 계획은 아래 기준으로 세웠다.
rss-parser를 사용node:sqlite 사용url 컬럼에 UNIQUE 제약 추가기존 프로젝트를 확인한 결과:
aggregate-blog-rss.js에서 rss-parser 사용 중v24.13.0node:sqlite 사용 가능archive-mit-tech-review-kr.js를 새로 작성했다.
주요 동작:
https://www.technologyreview.kr/feed/~/docs/mit-reviws.db~/docs가 없으면 자동 생성mit_tech_reviews 테이블 생성title, link, description, content, pubDate 저장url 충돌 시 무시 또는 갱신되지 않도록 중복 방지node:sqlite API 오해처음 구현에서 db.transaction()을 사용했는데 실행 시 아래 오류가 발생했다.
오류: db.transaction is not a function
수정 내용:
db.transaction() 사용을 제거BEGIN, COMMIT, ROLLBACK를 직접 실행하는 수동 트랜잭션으로 변경수정 후 스크립트는 정상 실행되었다.
실행 결과:
RSS 항목 10개 확인
신규 저장 10개
건너뜀 0개
DB 파일 생성 확인:
/home/ubuntu/docs/mit-reviws.db재실행 결과:
RSS 항목 10개 확인
신규 저장 0개
건너뜀 10개
이 시점까지는 중복 방지는 정상 동작했다.
description과 content가 동일하게 저장됨사용자 확인 요청으로 DB 값과 RSS 파싱 결과를 점검했다.
확인 결과:
description 길이와 content 길이가 동일content:encoded에 들어 있음rss-parser 기본 content 필드는 짧은 소개 HTML에 가까웠고 description과 같은 값으로 들어오고 있었음실측 예시:
descriptionLength: 575contentLength: 575content:encodedLength: 6429원인:
item.content를 content 컬럼에 저장하고 있었음item['content:encoded'] 또는 커스텀 매핑 필드에 있었음content 저장 로직 수정수정 내용:
['content:encoded', 'encodedContent']로 변경content 컬럼에는 item.encodedContent || item['content:encoded'] || item.content를 저장신규 저장, 기존 갱신, 건너뜀으로 구분재동기화 후 확인 결과:
description과 content는 더 이상 동일하지 않음description 길이 575, content 길이 6429 확인id 스키마 수정이후 요구사항 변경에 따라 id를 자동 증가 정수가 아니라 RSS guid에서 추출하도록 변경했다.
예시:
위 경우 id = 47801
수정 내용:
extractGuidId() 함수 추가guid에서 정규식으로 ?p=n 또는 &p=n 패턴의 숫자 추출id INTEGER PRIMARY KEY로 테이블 스키마 변경id를 직접 저장title 또는 url뿐 아니라 id도 필수값으로 검사사용자 요청에 따라 기존 mit_tech_reviews 테이블을 삭제하고 다시 생성한 후 RSS를 다시 채워 넣었다.
실행한 작업:
DROP TABLE IF EXISTS mit_tech_reviewsCREATE TABLE최종 스키마:
CREATE TABLE mit_tech_reviews (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
url TEXT NOT NULL UNIQUE,
description TEXT,
content TEXT,
pub_date TEXT
)
재적재 결과:
RSS 항목 10개 확인
신규 저장 10개
기존 갱신 0개
건너뜀 0개
샘플 검증:
47801 -> 노벨경제학상 수상자 아세모글루 MIT 교수가 우려하는 세 가지 AI 이슈47765 -> 콘센트 연결만으로 전기 생산…미국서 확산되는 ‘플러그인 태양광’47751 -> [머스크 대 올트먼 소송 2주차] 오픈AI의 반격…시본 질리스, 머스크의 올트먼 스카우트 시도 사실 폭로재실행 검증:
RSS 항목 10개 확인
신규 저장 0개
기존 갱신 0개
건너뜀 10개
최종적으로 archive-mit-tech-review-kr.js는 아래 요구사항을 만족한다.
https://www.technologyreview.kr/feed/ RSS 수집~/docs/mit-reviws.db 자동 생성mit_tech_reviews 테이블 자동 생성id는 RSS guid의 p 파라미터 숫자 사용title, url, description, content, pub_date 저장content는 전체 본문 HTML 저장url은 UNIQUE로 중복 방지node:sqlite는 현재 실행 환경에서 experimental warning을 출력하지만 실제 동작에는 문제 없었다.mit-reviws.db 철자를 그대로 사용했다.이후 추가 요청으로 RSS 피드에 없는 기존 MIT 테크놀로지 리뷰 기사들도 보관 DB에 넣기 위해 Ocilab 보관 페이지를 크롤링하는 별도 스크립트를 작성했다.
대상 목록 페이지:
https://ocilab.mywire.org/articles/rss_list.php?page=1&sort=published_date&order=DESC&search=&items=100
수집 범위:
page=1부터 page=5까지 순회rss_view.php?id=... 상세 링크 추출mit_tech_reviews 테이블에 추가 저장작업 도중 요구사항이 다음과 같이 조정됐다.
~/docs가 아니라 ~/data/mit-reviws.db 사용id는 MIT 워드프레스 ?p= 값이 아니라 Ocilab 상세 페이지 URL인 https://ocilab.mywire.org/articles/rss_view.php?id=...의 id 값 사용description과 content도 Ocilab 상세 페이지에서만 추출이에 맞춰 기존 방향을 수정했다.
새 파일:
archive-mit-tech-review-kr-ocilab.js주요 동작:
tbody tr에서 rss_view.php?id=... 링크 수집#view, #info 영역 파싱사용한 필드 매핑:
id: Ocilab rss_view.php?id=...의 숫자title: #view h1url: #info 영역의 원본 URL 링크description: #view blockquote HTMLcontent: #view 전체 HTML에서 제목 h1 제거 후 저장pub_date: #info의 발행일: 문자열을 ISO 형태로 변환작업 중 기존 RSS 스크립트 archive-mit-tech-review-kr.js의 DB 경로가 한때 ~/docs로 바뀌어 있었는데, 이후 사용자 요청에 따라 다시 ~/data/mit-reviws.db로 되돌렸다.
즉 최종적으로 두 스크립트 모두 같은 DB를 사용한다.
archive-mit-tech-review-kr.jsarchive-mit-tech-review-kr-ocilab.js처음 Ocilab 스크립트를 실행했을 때 아래 오류가 발생했다.
오류: attempt to write a readonly database
확인 결과:
ubuntu:ubuntu 소유/home/ubuntu/data 디렉터리는 www-data:www-data 소유디렉터리 권한을 바꾸지 않고 우회하기 위해 두 스크립트에서 DB 연결 직후 아래 pragma를 적용했다.
PRAGMA journal_mode=MEMORY;
PRAGMA temp_store=MEMORY;
이후 쓰기 테스트와 실제 적재가 정상 동작했다.
처음 실행에서는 일부 기존 RSS 적재 데이터와 Ocilab 데이터가 섞여 있어 결과가 중간 단계로 나타났다.
중간 실행 예시:
목록 항목 434개 확인
신규 저장 96개
기존 갱신 10개
건너뜀 328개
이후 url 기준 기존 행의 id도 Ocilab ID로 갱신하도록 업데이트 로직을 보강했다.
예를 들어 같은 URL의 기사라도 초기 RSS 적재 당시에는 워드프레스 p 값이 id였지만, 최종적으로는 아래처럼 Ocilab 목록 ID로 통일했다.
체외수정의 미래…AI·로봇·유전자 기술이 바꾸는 생식의학 -> id = 454노벨경제학상 수상자 아세모글루 MIT 교수가 우려하는 세 가지 AI 이슈 -> id = 453콘센트 연결만으로 전기 생산…미국서 확산되는 ‘플러그인 태양광’ -> id = 452최종 재실행 결과:
목록 항목 434개 확인
신규 저장 0개
기존 갱신 0개
건너뜀 434개
DB 최종 상태:
434454, 453, 452, 451, 450, 449, 448, 447, 446, 445즉 Ocilab 1~5페이지의 기사들이 모두 mit_tech_reviews 테이블에 들어갔고, 재실행 시 중복 없이 안정적으로 동작한다.
최종적으로 Ocilab 이력 수집 스크립트는 아래 요구사항을 만족한다.
rss_list.php?page=1..5 순회rss_view.php?id=... 상세 페이지 크롤링id는 Ocilab 상세 페이지의 id 사용mit_tech_reviews 테이블에 저장url 기준 중복 방지