puppeteer를 활용하여 크롤링 해보기 (2)

·2022년 6월 19일
1

크롤링

목록 보기
3/6

오늘은 어제 고민하던 것들을 모두 해결하고 새로운 고민이 생겼다.

해결한 고민

무한 스크롤

원티드는 페이지네이션이 아니라 무한스크롤이 구현되어있어서 스크롤을 아래까지 쭉 내려야만 해당되는 채용 카드의 정보를 가져올 수 있었다.

그래서 자기가 알아서 스크롤을 내리는 코드를 구현했어야했는데

솔직히 이건 진짜 모르겠어서 검색을 했고 코드를 찾아서 붙여넣었다.
https://stackoverflow.com/questions/51529332/puppeteer-scroll-down-until-you-cant-anymore

  let lastHeight = await page.evaluate("document.body.scrollHeight");

  while (true) {
    await page.evaluate("window.scrollTo(0, document.body.scrollHeight)");
    await page.waitForTimeout(2000); // sleep a bit
    let newHeight = await page.evaluate("document.body.scrollHeight");
    if (newHeight === lastHeight) {
      break;
    }
    lastHeight = newHeight;
  }

어떤 의미를 가지고 있는지는 알아야할 것 같아서 확인을 해보니
웹 페이지의 끝이 될 때까지 스크롤을 내려라. 가 이 코드의 명령어다.

상단에 크롤링을 이야기하는 소프트웨어 제어가 떠있는 모습

스크롤이 알아서 내려가는 것을 확인해볼 수 있다.

새로운 탭 열기

중앙 페이지에서 각 카드에 들어가서 정보를 긁어와야했기 때문에

  • 창을 이동한 다음에 뒤로 돌아오거나
  • 새로운 탭을 만든 다음에 그 탭을 꺼야하는 과정이 필요했다.

근데 창을 껏다가 뒤로 돌아가는건 정말 비효율적인 것 같아서 이리저리 찾아봤는데

마우스 휠로 클릭을 유도해가지고 새로운 탭이 생기게 하는 옵션이 있었다(신기해라)
https://stackoverflow.com/questions/59575748/puppeteer-how-to-click-element-so-it-opens-in-new-tab

 let options = { button: "middle" }; //<- 이거가 휠로 누르는 

    await framePage.click(
      `#__next > div.JobList_cn__t_THp > div > div > div.List_List_container__JnQMS > ul > li:nth-child(${i}) > div > a`,
      options
    );

이렇게 새로운 탭을 만들어내는 것은 문제가 없었는데, 정보를 가져오는 것이 문제가 됐다.

페이지를 넘어가서 정보를 가져와야하는데 어떻게...가져오나요...?

또 검색했다..^^ (선배 개발자들의 삽질은 나의 행복)
https://stackoverflow.com/questions/62746325/switching-to-a-new-tab-using-browser-pages-in-puppeteer

const [tab1, tab2, tab3] = await browser.pages();
    await tab3.bringToFront();
    await tab3.waitForNavigation();

퍼펫티어를 활용하면 나만 그런지는 몰라도(...)

아무것도 없는 공백 창 1개, 내가 goto()로 생성한 창 1개로 총 2개가 뜨게 된다.

여기서 새로운 탭을 생성할 경우에는 1개가 더 떠서 총 3개가 뜨는데
이것을 구조분해할당으로 분리해서 각각에 접근을 할 수 있다는 것을 알게 되었다.

그리고 bringToFront()는 페이지를 이동하는 메소드고
waitForNavigation()는 페이지를 기다려주는 메소드다.

정보를 가져와야하니 페이지를 이동하고, 구성이 될 때까지 기다린다고 생각하면 될 것 같다(아마두)

그리고 빗줄이 그어져있던 waitFor도 해결했다.

  const qualification = await tab3.waitForSelector(
      `#__next > div.JobDetail_cn__WezJh > div.JobDetail_contentWrapper__DQDB6 > div.JobDetail_relativeWrapper__F9DT5 > div > div.JobContent_descriptionWrapper__SM4UD > section > p:nth-child(5)`
    );

    const qualificationData = await tab3.evaluate(
      (qualification) => qualification.textContent,
      qualification
    );

waitForSelector이라는 메소드를 통하여 같은 동작을 가능캐 하였다.

사실 2개로 분리되어있는 것을 $eval로 고칠 수 있는 것 같은데
얘가 낮은 확률로 오류(?)를 뱉길래 정확하게 확인이 되지 않아서 주석처리만 해놓았다.

그리고 정보를 다 긁었으면 닫고 반복문이 돌아가게 되는 구조로 만들었다.


크롤링 수도코드

결국은 이러한 구조를 가지고 움직인다고 생각하면 된다.

  1. 스크롤을 맨 아래까지 다 내려버린다.
  2. 맨 처음 카드를 새로운 탭으로 생성한다.
  3. 새로운 탭에 있는 정보를 다 긁어온다.
  4. 탭을 닫는다.
  5. 2부터 4까지 다시 카드가 존재하는 개수만큼 반복한다.

이제 1-4까지는 완성이 되었으니 5번의 존재하는 카드 개수를 확인만 하면 된다.

그리고 최종적으로 엘라스틱서치에 데이터를 집어넣어서 검색기능이 가능하도록 구현을 하면 되는데
아마도 프론트 한명 붙잡고 미니프로젝트처럼 해야할 것 같다는 생각이 든다.

그리고 데이터를 바로 엘라스틱서치에 집어넣을지, 아니면 MySQL이나 MongoDB에 집어넣었다가 Logstash로 넘길지 고민하고 있다.

뭐가 좋을지는 생각을 좀 해봐야할 것 같다...

끝!

nestjs 정리해야하는데 으으!

profile
물류 서비스 Backend Software Developer

1개의 댓글

comment-user-thumbnail
2023년 9월 24일

저도 스크롤 해서 데이터를 가져오는 로직이 필요했는데 블로그 글을 보고 바로 해결이 되었어요! ㅎㅎ 감사합니다~

답글 달기