본문 바로가기

Node.js/node crawling

노드 크롤링 - 인스타그램 크롤링하기

반응형

 

먼저 facebook 크롤링을 진행을 위해 아이디를 가입했기때문에 인스타그램 또한 facebook아이디로 진행을 위해 

facebook으로 로그인창을 받아서 클릭을 진행해준다.

 

 

document.querySelector(".KPnG0").click();

 

 

로그인 유지하기

 

userDataDir을 사용하면 로그인에 대한 정보를 기억 할 수 있다.

  const browser = await puppeteer.launch({
      headless: false,
      args: ["--window-size=1920,1080", "--disable-notifications"],
      userDataDir: 'C:\Users\dhsdb\AppData\Local\Google\Chrome\User Data'
    });

 

 

로그인 여부 판단하기

로그인이 되어있으면 내 아이디 주소를 기반으로 href="/bot135791/" 이 나와있다

 

 

 

 

 

 

 

게시글 가져오기

article로 구성이 되어있다.

 

document.querySelector("article:first-of-type")

 

작성자 ID 가져오기

 

 

 

 

document.querySelector("a.sqdOP")

 

 

 

게시글 ID 가져오기

a태그 식으로 되어있다.

document.qeurySelector(".c-Yi7")

 

 

이미지 가져오기

 

 

document.querySelector("article:first-of-type").querySelector(".KL4Bh img")

 

 

게시글 Content 가져오기 

document.querySelector("article .QzzMF:first-of-type")

 

1차적으로 가져와보기 

 

const puppeteer = require("puppeteer");
const dotenv = require("dotenv");
dotenv.config();
const crawler = async () => {
  try {
    const browser = await puppeteer.launch({
      headless: false,
      args: ["--window-size=1920,1080", "--disable-notifications"],
      userDataDir: "C:UsersdhsdbAppDataLocalGoogleChromeUser Data",
    });
    const page = await browser.newPage();
    page.setViewport({
      width: 1080,
      height: 1080,
    });
    await page.goto("https://instagram.com");

    // 로그인이 되었을때
    if (await page.$('a[href="/bot135791/"]')) {
      console.log("이미 로그인이 되어있습니다");
      // 로그인이 안됬을때
    } else {
      // 페이스북으로 로그인 버튼
      await page.waitForSelector(".KPnG0");
      await page.click(".KPnG0");
      await page.waitForNavigation(); //facebook 로그인으로 넘어가는것을 대기
      await page.waitForSelector("#email");
      await page.type("#email", process.env.EMAIL);
      await page.waitFor(1000);
      await page.type("#pass", process.env.PASSWORD);
      await page.waitFor(1000);
      await page.waitForSelector("#loginbutton");
      await page.click("#loginbutton");
      await page.waitForNavigation(); //instagram으로 넘어가는것을 대기
      console.log("로그인을 완료 하였습니다 ");
    }
    await page.waitForSelector("article:first-of-type");
    
    const newPost = await page.evaluate(() => {
      // 더보기를 눌러서 진행해준다
      if(document.querySelector("button.sXUSN")){
        document.querySelector("button.sXUSN").click();
      }
      //게시글 가져오기 
      const article = document.querySelector("article:first-of-type");
      console.log(article);
      const postId =
        document.querySelector(".c-Yi7") &&
        document.querySelector(".c-Yi7").href;
      console.log(postId);
      const name =
        article.querySelector("a.sqdOP") &&
        article.querySelector("a.sqdOP").textContent;
      const image =
        article.querySelector(".KL4Bh img") &&
        article.querySelector(".KL4Bh img").src;
      console.log(image);
      const content =
        article.querySelector(".QzzMF:first-of-type") &&
        article.querySelector(".QzzMF:first-of-type").textContent;

      return {
        postId,
        name,
        image,
        content,
      };
    });

    console.log(newPost);
  } catch (err) {
    console.log(err);
  }
};

crawler();

 

 

 

인스타는 스크롤을 내리면 위에있는 article을 없애버리고 하단에 새 article을 추가하고 하는

react-virtualized 기술이 적용되어있다. (메모리를 아낀다)

 

그래서 aricle이 처음엔 8개까지 늘어나다가 그 이후로는 article이 추가및 삭제가 되서 8개씩 유지한다.

 

 

먼저 스크롤 부분들을 위해 해당 부분들을 제거해준다.

 

 

제거해주고 스크롤을 돌린 코드

const puppeteer = require("puppeteer");
const dotenv = require("dotenv");
dotenv.config();
const crawler = async () => {
  try {
    const browser = await puppeteer.launch({
      headless: false,
      args: ["--window-size=1920,1080", "--disable-notifications"],
      userDataDir: "C:UsersdhsdbAppDataLocalGoogleChromeUser Data",
    });
    const page = await browser.newPage();
    page.setViewport({
      width: 1080,
      height: 1080,
    });
    await page.goto("https://instagram.com");

    // 로그인이 되었을때
    if (await page.$('a[href="/bot135791/"]')) {
      console.log("이미 로그인이 되어있습니다");
      // 로그인이 안됬을때
    } else {
      // 페이스북으로 로그인 버튼
      await page.waitForSelector(".KPnG0");
      await page.click(".KPnG0");
      await page.waitForNavigation(); //facebook 로그인으로 넘어가는것을 대기
      await page.waitForSelector("#email");
      await page.type("#email", process.env.EMAIL);
      await page.waitFor(1000);
      await page.type("#pass", process.env.PASSWORD);
      await page.waitFor(1000);
      await page.waitForSelector("#loginbutton");
      await page.click("#loginbutton");
      await page.waitForNavigation(); //instagram으로 넘어가는것을 대기
      console.log("로그인을 완료 하였습니다 ");
    }
    await page.waitForSelector("article:first-of-type");

    //스토리 부분, 전화번호 추가, 프로필 사진추가 삭제하기
    await page.evaluate(() => {
      const story = document.querySelector(".zGtbP");
      const start = document.querySelector("._2eEhX");
      //삭제
      if (story) {
        story.parentNode.removeChild(story);
      }
      if (start) {
        start.parentNode.removeChild(start);
      }
      const recommended = document.querySelector(".vboSt");
      if (recommended) {
        recommended.parentNode.removeChild(recommended);
      }
    });
    await page.waitFor(1000);
    let result = [];
    let prevPostId = "";
    while (result.length < 10) {
      // 회원님을 위한 추천 삭제해주기

      const newPost = await page.evaluate(() => {
        // 더보기를 눌러서 진행해준다
        if (document.querySelector("button.sXUSN")) {
          document.querySelector("button.sXUSN").click();
        }
        //게시글 가져오기
        const article = document.querySelector("article:first-of-type");
        console.log(article);
        const postId =
          document.querySelector(".c-Yi7") &&
          document.querySelector(".c-Yi7").href;
        console.log(postId);
        const name =
          article.querySelector("a.sqdOP") &&
          article.querySelector("a.sqdOP").textContent;
        const image =
          article.querySelector(".KL4Bh img") &&
          article.querySelector(".KL4Bh img").src;
        console.log(image);
        const content =
          article.querySelector(".QzzMF:first-of-type") &&
          article.querySelector(".QzzMF:first-of-type").textContent;

        return {
          postId,
          name,
          image,
          content,
        };
      });
      // 새 게시글의 postId가 이전 postId와 같지 않으면
      // 배열에 넣고 해당 포스트 아이디를 검증한다
      // 중복으로 데이터가 들어가는것을 방지
      if (newPost.postId !== prevPostId) {
        if (!(result.find((v) => v.postId === newPost.postId))) {
          result.push(newPost);
        }
      }
      prevPostId = newPost.postId;
      await page.evaluate(() => {
        window.scrollBy(0, 900);
      });
    }
    console.log(result);
  } catch (err) {
    console.log(err);
  }
};

crawler();

 

게시글들을 가져오는데 성공하였다.

 

 

 

 

 

좋아요 구현하기 

 

 

해당 코드를 통해 좋아요 기능까지 구현하였다.

 await page.evaluate(() => {
        // 좋아요 눌러주기
        const fill = document.querySelector(".fr66n button svg");
        if(fill.getAttribute("fill") === "#262626"){
          document.querySelector(".fr66n button").click();
        }
      });

 

const puppeteer = require("puppeteer");
const dotenv = require("dotenv");
dotenv.config();
const crawler = async () => {
  try {
    const browser = await puppeteer.launch({
      headless: false,
      args: ["--window-size=1920,1080", "--disable-notifications"],
      userDataDir: "C:UsersdhsdbAppDataLocalGoogleChromeUser Data",
    });
    const page = await browser.newPage();
    page.setViewport({
      width: 1080,
      height: 1080,
    });
    await page.goto("https://instagram.com");

    // 로그인이 되었을때
    if (await page.$('a[href="/bot135791/"]')) {
      console.log("이미 로그인이 되어있습니다");
      // 로그인이 안됬을때
    } else {
      // 페이스북으로 로그인 버튼
      await page.waitForSelector(".KPnG0");
      await page.click(".KPnG0");
      await page.waitForNavigation(); //facebook 로그인으로 넘어가는것을 대기
      await page.waitForSelector("#email");
      await page.type("#email", process.env.EMAIL);
      await page.waitFor(1000);
      await page.type("#pass", process.env.PASSWORD);
      await page.waitFor(1000);
      await page.waitForSelector("#loginbutton");
      await page.click("#loginbutton");
      await page.waitForNavigation(); //instagram으로 넘어가는것을 대기
      console.log("로그인을 완료 하였습니다 ");
    }
    await page.waitForSelector("article:first-of-type");

    //스토리 부분, 전화번호 추가, 프로필 사진추가 삭제하기
    await page.evaluate(() => {
      const story = document.querySelector(".zGtbP");
      const start = document.querySelector("._2eEhX");
      //삭제
      if (story) {
        story.parentNode.removeChild(story);
      }
      if (start) {
        start.parentNode.removeChild(start);
      }
      const recommended = document.querySelector(".vboSt");
      if (recommended) {
        recommended.parentNode.removeChild(recommended);
      }
    });
    await page.waitFor(1000);
    let result = [];
    let prevPostId = "";
    while (result.length < 10) {
      // 회원님을 위한 추천 삭제해주기

      const newPost = await page.evaluate(() => {
        // 더보기를 눌러서 진행해준다
        if (document.querySelector("button.sXUSN")) {
          document.querySelector("button.sXUSN").click();
        }
        //게시글 가져오기
        const article = document.querySelector("article:first-of-type");
        console.log(article);
        const postId =
          document.querySelector(".c-Yi7") &&
          document.querySelector(".c-Yi7").href;
        console.log(postId);
        const name =
          article.querySelector("a.sqdOP") &&
          article.querySelector("a.sqdOP").textContent;
        const image =
          article.querySelector(".KL4Bh img") &&
          article.querySelector(".KL4Bh img").src;
        console.log(image);
        const content =
          article.querySelector(".QzzMF:first-of-type") &&
          article.querySelector(".QzzMF:first-of-type").textContent;

        return {
          postId,
          name,
          image,
          content,
        };
      });

      await page.evaluate(() => {
        // 좋아요 눌러주기
        const fill = document.querySelector(".fr66n button svg");
        if(fill.getAttribute("fill") === "#262626"){
          document.querySelector(".fr66n button").click();
        }
      });
      await page.waitFor(1000);
      // 새 게시글의 postId가 이전 postId와 같지 않으면
      // 배열에 넣고 해당 포스트 아이디를 검증한다
      // 중복으로 데이터가 들어가는것을 방지
      if (newPost.postId !== prevPostId) {
        if (!(result.find((v) => v.postId === newPost.postId))) {
          result.push(newPost);
        }
      }
      prevPostId = newPost.postId;
      await page.evaluate(() => {
        window.scrollBy(0, 900);
      });
    }
    console.log(result);
  } catch (err) {
    console.log(err);
  }
};

crawler();

본 글은 아래 인프런 강의를 듣고 작성된 내용입니다

https://www.inflearn.com/course/%ED%81%AC%EB%A1%A4%EB%A7%81

 

Node.js로 웹 크롤링하기 - 인프런 | 강의

네이버, 아마존, 트위터, 유튜브, 페이스북, 인스타그램, unsplash.com 등의 사이트를 크롤링하며 실전에 적용해봅니다., Node.js로 웹 크롤링하기 Node.js와 Puppeteer를 활용해 웹 사이트를 크롤링하여 원

www.inflearn.com

 

반응형