본문 바로가기
기록이 하고싶어서/PM 생존기 : 기획과 개발

자바스크립트로 구글 기사 100개 크롤링하기 (VSC, JS, .xlsx)

by 김찬란 2024. 11. 8.

 

서비스 분석하면서 꽤 많은 기사를 찾아보게 된다.

특히 과제를 진행할 때는 매주 몇 십 개의 기사를 찾고 읽고 정리하다 보니 자연그럽게 쉽고 빠르게 기사를 모으는 방법을 찾게 되었다.

그때마다 '크롤링을 해볼까?' 하는 생각이 들었지만 쉽게 설명해주는 글들을 막상 따라해보면 이렇게 조금 바꿔보고 싶고, 이게 추가되면 좋겠고 하는 것들이 있어서 n차례 시도해보다가 결국 내가 그냥 간단한 버전으로 GPT랑 만들었다.

 

나는 원래 갖고있던 VSC를 이용했고 javascript 로 코드를 짰다. 

확실히 아직 파이썬보다는 자바스크립트가 눈에 익어서 편하다.

처음에 다른 능력자들의 코드로 만들 때는 엑셀로 결과물을 저장했는데 중간에 오류가 생겨도 파일을 열어봐야 결과 확인이 가능해서 .txt 파일로 만들고 바로바로 확인했다. 결과물 담기는 거 보고 엑셀로 바꿔달라 했는데 이것도 초반에 잘 안됐는데 결국은 GPT와의 대화를 (ㅋㅋㅋ) 통해 값을 뽑을 수 있었따!!!

 

결론

크롤링은 [기사 제목, summary, 날짜, url]을 가져온다.

날짜 기준 내림차순(최근->과거) 로 정렬하고 최근부터 100개 기사를 긁어온다.

결과물은 자동으로 [yyyymmdd(오늘)-검색어.xlsx] 형식으로 폴더에 저장된다.

 

 

완전 처음. VSC도 처음 깔아보는 경우

1. vsc(visual studio code) - node.js

아마 vsc를 깔고 코드를 넣고 실행한다고 모두 실행되는 건 아닐 수 있다.

node.js 나 기타 등등을 깔아줘야 했던 것 같은데 

나는 이미 깔려 있어서 뭘 해야 최소조건 충족인지를 잘 모르겠네

일단 vsc 설치하고 node.js 설치하는 건 필수 맞음

VSC는 최신으로 설치하면 되고

node는 LTS를 설치하면 된다.

 

Visual Studio Code 설치

 

Node.js 설치

2. code runner 확장자 설치 

vsc를 열고 맨 처음 할 일은 이미지처럼 좌측 아이콘을 눌러 code runner를 설치하는 것

이미지는 이미 설치된 환경이라 그냥 설치 누르면 됨

설치 완료되면 다시 좌측 맨 위 아이콘을 누른다.

 

3. 원하는 위치에 폴더 생성

일단 바탕화면이든 원하는 위치에 원하는 이름의 폴더를 생성하고 열기를 눌어 vsc에서 진입한다.

 

4. 새로운 파일 만들기 (파일명.js)

이미지처럼 노란색이 안나올 수 있음(저건 별도의 설치 필요. 그냥 꾸미는 용임)

 

5. Contrl + ~ 로 터미널 열기(영문 상태)

터미널을 여는데 이때 터미널에 폴더명이 아까 내가 새로 만든 폴더명이 제대로 나와야 vsc에서 경로가 맞게 찾아진 것임

만약 터미널에서 확인되는 위치가 아까 내가 폴더 만들었던(또는 작업할 위치로 정한) 위치가 아닐 경우 

대충 파일-열기 들어가서 다시 원하는 위치를 열어주자

 

 

 

Node 환경 갖췄다

6. 대충 된 것 같으니 냅다 라이브러리 설치

터미널 에 아래 코드 붙여넣고 Enter 눌러 실행

npm install puppeteer xlsx-populate moment

 

기다리고 기다리다보면

대충 이렇게 뜨면 정상 설치된 것임!

(혹시 몰라 설명하면 웹 크롤링을 도와주는 Puppeteer, xlsx 확장자인 엑셀파일을 만들어주는 xlsx-populate, 오늘날짜를 가져오는 moment 를 한번에 설치함)

 

 

7. 이제 본격적으로 코드를 복붙할거임

아래 코드를 복사해서 crawling.js 파일에 붙여넣고 저장(control/⌘ + s)

const puppeteer = require("puppeteer");
const xlsxPopulate = require("xlsx-populate");
const moment = require("moment");

async function scrapeGoogleNews(searchQuery, numOfArticles) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  const articles = [];

  // 첫 번째 페이지부터 시작하여 여러 페이지를 탐색
  let pageNumber = 1;
  while (articles.length < numOfArticles) {
    await page.goto(
      `https://www.google.com/search?q=${searchQuery}&tbm=nws&start=${
        (pageNumber - 1) * 10
      }`
    );

    const newArticles = await page.evaluate(() => {
      const articleElements = document.querySelectorAll(".SoaBEf");
      const articles = [];
      articleElements.forEach((articleElement) => {
        const title = articleElement
          .querySelector(".n0jPhd")
          .textContent.trim();
        const summary = articleElement
          .querySelector(".GI74Re")
          .textContent.trim();
        const upload = articleElement
          .querySelector(".OSrXXb")
          .textContent.trim();
        const url = articleElement.querySelector("a").getAttribute("href");
        articles.push({ title, summary, upload, url });
      });
      return articles;
    });

    // 새로 가져온 기사를 기존 기사 배열에 추가하고 다음 페이지로 이동
    articles.push(...newArticles);
    pageNumber++;

    // 기사를 모두 가져왔거나 페이지 끝에 도달한 경우 루프 종료
    if (newArticles.length === 0) {
      break;
    }
  }

  // 기사를 모두 가져온 후 브라우저를 닫음
  await browser.close();

  return articles.slice(0, numOfArticles);
}

async function main() {
  const searchQuery = "검색어";
  const numOfArticles = 100;
  const articles = await scrapeGoogleNews(searchQuery, numOfArticles);

  // 정렬: Upload 내림차순 (최근->오래)
  articles.sort((a, b) => (a.upload < b.upload ? 1 : -1));

  // 결과물 콘솔에 출력 (5줄까지만)
  console.log("검색 결과 (5줄까지만 출력):");
  for (let i = 0; i < Math.min(5, articles.length); i++) {
    console.log(`Title: ${articles[i].title}`);
    console.log(`Summary: ${articles[i].summary}`);
    console.log(`Upload: ${articles[i].upload}`);
    console.log(`URL: ${articles[i].url}`);
    console.log("---");
  }

  // .xlsx 파일로 저장, 파일명은 '오늘날짜-검색어.xlsx'
  const currentDate = moment().format("YYYYMMDD");
  const fileName = `${currentDate}-${searchQuery}.xlsx`;

  const workbook = await xlsxPopulate.fromBlankAsync();
  const sheet = workbook.sheet(0);

  // 헤더 추가
  sheet.cell("A1").value("Title");
  sheet.cell("B1").value("Summary");
  sheet.cell("C1").value("Upload");
  sheet.cell("D1").value("URL");

  // 데이터 추가
  articles.forEach((article, index) => {
    const row = index + 2;
    sheet.cell(`A${row}`).value(article.title);
    sheet.cell(`B${row}`).value(article.summary);
    sheet.cell(`C${row}`).value(article.upload);
    sheet.cell(`D${row}`).value(article.url);
  });

  // .xlsx 파일로 저장
  await workbook.toFileAsync(fileName);
  console.log(`뉴스 기사가 ${fileName} 파일로 저장되었습니다.`);
}

main();

 

 

8. 저장 했다면 vsc의 55번째 줄의 쌍따옴표 안의 "검색어"를 원하는 키워드로 바꿔줌

검색어 아래 숫자 100을 변경하면 그만큼의 기사를 긁어온다.

 

9. VSC 우측상단에 실행을 누른다.

터미널창을 열어두었다면 아래와 같이 메시지 콘솔이 뜨면 성공이다.

 

파일 위치로 가서 문서를 확인하면 성공!

 

 


 

 

파일을 구글 스프레드시트로 열어 확인하니 정상적으로 100개의 기사를 긁어온 것이 확인된다.