All Posts

[심심풀이] 크롤링 라이브러리 Cheerio 가지고 놀기

주말에 그동안 못본 웹툰 정주행하다가 시작한 크롤링 스크래핑 하면서 놀기…

결론만 말하면…

자바의 Jsoup, 파이썬의 Beautiful Soup으로 하던 스크래핑을 자바스크립트로 하는 건데, Cheerio라는 라이브러리를 가지고 놀아보는 뭐 그런 글이다.


네XX 요일 웹툰을 거의 다 챙겨보던 때가 있었다.

특정 요일에는 챙겨보던 웹툰이 1~9위를 다 하고 있었던 시기도 있었다. 어렸을때부터 만화를 좋아했었는데, 웹툰 퀄리티가 말도 안되게 솟구치면서 많은 사람들이 쿠키를 구워가며 웹툰을 즐기고 있다.

가끔 정신없이 바쁜 기간에는 웹툰을 못보고 몰아보기를 하게 되는데, 그럴때 다X웹툰의 정주행 기능이 네XX웹툰에도 있으면 좋겠다라는 생각은 했었는데…

주말에 못본 웹툰이나 볼까하다가 귀찮아서, 정주행 기능이나 만들어볼까 하다가 웹툰 소장툴을 만들어 봤다.

(은행사, 카드사, 배달앱 3사 스크래핑 모듈을 만들어대던 시기에는 생각의 흐름대로 술술했던 스크래핑 기술을 되새겨 볼 겸사겸사 오랜만에 스크래핑 모듈이나 만들어보자 하고 움직였는데, 이미지 링크 모아서 한번에 다운받는 뭐 그런 간단한 기능이지만, Referer 설정만 해주면 별 문제 없이 잘 해소됬다. 너무 쉽게 끝나버려서 Cheer.io 사용법이나 간단하게 설명하는데 그칠 듯하다.)

웹툰의 저작권은 작가분들께 있습니다.

지적 재산에 대한 존중이 개발자로서의 기본 소양이 아닐까 싶다!

네XX 웹툰 소장툴 기본 프로세스

  1. 네XX 웹툰 검색에서 원하는 웹툰을 검색한다.
  2. 해당 웹툰이 존재 한다면, 최신 회차의 번호를 확인한다.
  3. 첫회부터 마지막 회차까지 모든 페이지에 들어가서 웹툰 이미지를 다운로드 한다.(회차별로 파일 순서대로)
  4. (불필요) 네XX 웹툰은 이미지를 임의로 잘라놓았으니 그 이미지들을 모아서 하나의 긴 이미지로 만들어준다.

스크래핑 툴

  1. Fiddler mac인 경우, Charles
  2. Wire Shark
  3. 그리고 Chrome or Firefox
  4. 추가로 Postman

일반적인 스크래핑 방식에 대해 간단하게 설명하면,

내가 이해하는 웹 스크래핑(크롤링은 스크래핑의 한 방식) 웹 상의 정보를 수집해서 원하는 형태로 가공하는 것이다. 이를 사람이 하는게 아니라 bot / module을 써서 하도록 하는 것이기에, 웹 브라우징을 하는 과정을 코드로 작업해주면 된다.

누구든 접속해서 정보를 열람할 수 있는 웹사이트라 하더라도 마구잡이로 접속이 가능하지 않을 수 있다. 만약 로그인을 통해서 접속해야만 하는 사이트라면 로그인을 하는 기능을 구현해줘야할 것이고, 특정 ip만 접속을 허용한다면 특정 ip에서 왔다라는 표식을 넣어주면 될 것이다.

즉, 정보를 수집하고 싶은 타겟 페이지의 구조를 파악하고 거기에서 필요한 정보들만 수집해내서 원하는 형태로 가공하면 됨 여기서는 네XX웹툰의 검색페이지, 목록페이지, 회차별 페이지가 타켓이고 각 페이지마다 원하는 정보는 다음과 같다.

검색 페이지 : 검색어에 해당하는 웹툰 목록 페이지 링크 목록 페이지 : 최신 회차 정보 및 웹툰 회차 페이지 링크 회차별 페이지 : 웹툰 이미지 링크

일단 브라우저로 한번 둘러보시라~

전체 코드는 github에서 확인 가능하다.

  1. 검색어로 조회

    // node index.js 신의탑 식으로 arg로 검색어를 입력하도록 함
    let webtoonName = process.argv[2];
  2. 검색결과의 첫번째 a 태그의 href 추출

    const webtoonHref = $("#content > div:nth-child(2) > ul > li:nth-child(1) > h5 > a").attr('href');
    let webtoonUrl = naverWebtoonDomain + webtoonHref;
  3. 2에서 추출한 url로 접속해서 최신 회차의 페이지 번호 및 회차별 페이지 url 추출

  const lastNoUrl = $("#content > table > tbody > tr:nth-child(2) > td.title > a").attr('href');
  let lastNo = lastNoUrl.split('no=')[1].replace(/[^0-9]/g, '');
  1. 1회부터 최신 회차 페이지 각각 들어가서 웹툰 이미지 저장(동시 접속해서 이미지들을 긁으면 이미지 서버가 막으므로 동기로 요청)
  webtoonUrl = webtoonUrl.replace('list.nhn', 'detail.nhn');

  let array = new Array(+lastNo);
  for(let i=1; i<=array.length; i++){
      await saveImagesByChapter(webtoonUrl, i);
      console.log(`chapter: ${i} Done`);
  }
  1. 주석처리된 부분을 보면 회차의 이미지를 배열로 받아서 세로로 머지하는 기능이 있는데, 파일 용량만 커지고 별로인듯, 차라리 웹툰을 읽을 수 있는 페이지를 따로 만들어서 정주행 기능을 만들어주는게 훨씬 나을듯

    let images = [];
    let img = await mergeImg(images, { direction: true });
    img.write('1.jpg', ()=>{console.log('done')});

또 욕구뿜뿜이 일어나면 다른 웹툰들도 스크래핑 모듈을 만들어보고 정리해 올려봐야겠다.