본문 바로가기

Node.js/node crawling

노드 크롤링 - 페이스북 이미지 가져오기, 좋아요 구현하기, DB 저장

반응형

https://loy124.tistory.com/290

 

노드 크롤링 - 페이스북 로그인, 로그아웃 하기

const puppeteer = require("puppeteer"); const crawler = async() => { try{ const browser = await puppeteer.launch({headless: false, args:['--window-size=1920,1080']}); const page = await browser.newP..

loy124.tistory.com

윗 글에 이어서 진행한다.

 

첫 피드 가져오기

 

 

 

 

 

 

^=로 진행하면 id가 hyperfeed_story로 시작되는 값

$=로 진행하면 id가 hyperfeed_story로 끝나는 값

*=로 진행하면 hyperfeed+story가 포함되는 경우

로 값들을 가져올 수 있게 된다.

 

[id^=hyperfeed_story]

 

 

 

첫피드는 해당과 방식으로 가져 올 수 있다.

document.querySelector("[id^=hyperfeed_story_id]:first-child");

 

게시글Id 가져오기

 

배열 마지막 부분을 가져오는 코드이다 

hyperfeed_story와 _id_~~~로 되어있기 때문에

_로 구분된 맨 마지막부분들을 가져오면 postId를 가져올 수 있다.

document.querySelector("[id^=hyperfeed_story_id]:first-child").split("_").slice(-1)[0];

 

작성자 이름 가져오기

 

fwb fcg 형식으로 되어있다

이안의 a 태그를 출력하면

 

해당방식으로 a._wpv 가 섞여서 나온다 

Vue.js Korea가 a._wpv이다 

이를 first-of-type을 활용해서 출력한다

 

first-child는 모든 자식요소중에서 맨앞에 위치하는 자식 요소를 모두 선택

first-of-type은 모든 자식요소중에서 맨 처음으로 등장하는 특정 타입의 요소를 모두 선택

 

을 통해  이름을 가져올수있다

document.querySelectorAll(".fwb.fcg a:first-of-type")

 

 

 

이미지 가져오기

 

 

이미지 태그는 mtm이 포함되어있다 

 

해당 태그를  출력해보면 

 

 

mtm말고도 mtm에 무언가가 섞인형태로 가져와진다. 따라서

 

 

 

방식으로 해결해 주었다.

document.querySelectorAll("[class=mtm] img");

 

 

 

 

 

좋아요 

 

좋아요가 눌러있을 때

 

 

좋아요가 해제되어 있을 때

 

 

_3_16 일때 좋아요가 눌러있는 상태고

_3_16 이 없으면 좋아요가 눌러져 있지 않은 상태이다.

 

 

좋아요를 누를때마다 정상적으로 값을 받아온다.

 

 

 

완성된 코드 (게시글 10개 좋아요 누르기)

 

const puppeteer = require("puppeteer");
const dotenv = require("dotenv");

const db = require("./models");
dotenv.config();

const crawler = async () => {
  try {
    await db.sequelize.sync();
    const browser = await puppeteer.launch({
      headless: false,
      args: ["--window-size=1920,1080", "--disable-notifications"],
    });
    const page = await browser.newPage();
    await page.setUserAgent(
      "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36"
    );
    await page.setViewport({
      width: 1080,
      height: 1080,
    });
    await page.goto("https://facebook.com");
    const email = process.env.EMAIL;
    const password = process.env.PASSWORD;
    await page.type("#email", email);
    await page.waitFor(1000);
    await page.type("#pass", password);
    await page.waitFor(1000);
    await page.click("button[type=submit]");
    await page.waitFor(1000);

    await page.waitForSelector("._2md");
    await page.click("._2md");
    await page.waitFor(1000);
    await page.waitForSelector("[id^=hyperfeed_story_id]:first-child");
    let result = [];
    while (result.length < 10) {
      await page.waitFor(3000);
      const newPost = await page.evaluate(() => {
        const firstFeed = document.querySelector(
          "[id^=hyperfeed_story_id]:first-child"
        );
        const name =
          firstFeed.querySelector(".fwb.fcg a:first-of-type") &&
          firstFeed.querySelector(".fwb.fcg a:first-of-type").textContent;
        const content =
          firstFeed.querySelector(".userContent") &&
          firstFeed.querySelector(".userContent").textContent;
        // 이미지를 들고오려고 할때
        // document.querySelectorAll(".mtm") 을 하면
        // .mtm.`12`가 출력이되는데
        // document.querySelectorAll("[class=mtm]") 을하면 class가 딱 mtm인 경우만 출력이 된다.
        const postId = firstFeed.id.split("_").slice(-1)[0]; //배열의 마지막 고르는 코드 ;
        const img =
          firstFeed.querySelector("[class=mtm] img") &&
          firstFeed.querySelector("[class=mtm] img").src;
        return {
          name,
          img,
          content,
          postId,
        };
      });
      console.log(newPost);

      // _3_16 일때 좋아요가 눌러있는 상태고
      // _3_16 이 없으면 좋아요가 눌러져 있지 않은 상태이다.
      await page.waitFor(1000);
      await page.evaluate(() => {
        // 광고는 좋아요 하지 않도록 진행하기
        const likeButton = document.querySelector("._6a-y");
        const liked = document.querySelector("._3_16");
        //좋아요가 눌러져있지 않은 경우에만
        if (!liked) {
          likeButton.click();
        }
        // 광고인데 좋아요가 눌러져있는경우 해제
      });
      await page.waitFor(1000);
      await page.evaluate(() => {
        // 크롤링해서 좋아요 했으니 피드 지우기
        const firstFeed = document.querySelector(
          "[id^=hyperfeed_story_id]:first-child"
        );
        firstFeed.parentNode.removeChild(firstFeed);
      });
      await page.waitFor(1000);

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

crawler();

 

 

 

크롤링이 정상적으로 잘 되는것을 확인 할 수 있다.

 

 

 

 

 

DB 연동하기

 

 

 

npm i -g sequelize-cli

 

sequelize-cli 글로벌 설치후

 

sequlize init

해당 메서드를 통해 폴더구조를 생성해준다

 

 

 

DB접속 주소

 

config.json

 

{
  "development": {
    "username": "root",
    "password": "root",
    "database": "crawler",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}

 

models/index.js

const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config.json')[env];
const db = {};

const sequelize = new Sequelize(config.database, config.username, config.password, config);

db.Facebook = require("./facebook")(sequelize, Sequelize);

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

 

테이블 구조 생성

facebook.js

const { sequelize, Sequelize } = require("./index");

module.exports = (sequelize, Sequelize) => {
  return sequelize.define("facebook", {
    postId: {
      type: Sequelize.STRING(30),
      allowNull: false,
      unique : true

    },
    content:{
        type: Sequelize.TEXT
    },
    image: {
      type: Sequelize.TEXT
    },
    writer:{
        type : Sequelize.STRING(30),
        
    }
  });
};

 

 

 

DB저장 코드 

 

index.js

const puppeteer = require("puppeteer");
const dotenv = require("dotenv");

const db = require("./models");
dotenv.config();

const crawler = async () => {
  db.Facebook.sync();
  try {
    await db.sequelize.sync();
    const browser = await puppeteer.launch({
      headless: false,
      args: ["--window-size=1920,1080", "--disable-notifications"],
    });
    const page = await browser.newPage();
    await page.setUserAgent(
      "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36"
    );
    await page.setViewport({
      width: 1080,
      height: 1080,
    });
    await page.goto("https://facebook.com");
    const email = process.env.EMAIL;
    const password = process.env.PASSWORD;
    await page.type("#email", email);
    await page.waitFor(1000);
    await page.type("#pass", password);
    await page.waitFor(1000);
    await page.click("button[type=submit]");
    await page.waitFor(1000);

    await page.waitForSelector("._2md");
    await page.click("._2md");
    await page.waitFor(1000);
    await page.waitForSelector("[id^=hyperfeed_story_id]:first-child");
    let result = [];
    while (result.length < 10) {
      await page.waitFor(3000);
      const newPost = await page.evaluate(() => {
        window.scrollTo(0, 0);
        const firstFeed = document.querySelector(
          "[id^=hyperfeed_story_id]:first-child"
        );
        const name =
          firstFeed.querySelector(".fwb.fcg a:first-of-type") &&
          firstFeed.querySelector(".fwb.fcg a:first-of-type").textContent;
        const content =
          firstFeed.querySelector(".userContent") &&
          firstFeed.querySelector(".userContent").textContent;
        // 이미지를 들고오려고 할때
        // document.querySelectorAll(".mtm") 을 하면
        // .mtm.`12`가 출력이되는데
        // document.querySelectorAll("[class=mtm]") 을하면 class가 딱 mtm인 경우만 출력이 된다.
        const postId = firstFeed.id.split("_").slice(-1)[0]; //배열의 마지막 고르는 코드 ;
        const img =
          firstFeed.querySelector("[class=mtm] img") &&
          firstFeed.querySelector("[class=mtm] img").src;
        return {
          name,
          img,
          content,
          postId,
        };
      });
      console.log(newPost);

      // _3_16 일때 좋아요가 눌러있는 상태고
      // _3_16 이 없으면 좋아요가 눌러져 있지 않은 상태이다.
      await page.waitFor(1000);
      await page.evaluate(() => {
        // 광고는 좋아요 하지 않도록 진행하기
        const likeButton = document.querySelector("._6a-y");
        const liked = document.querySelector("._3_16");
        //좋아요가 눌러져있지 않은 경우에만
        if (!liked) {
          likeButton.click();
        }
        // 광고인데 좋아요가 눌러져있는경우 해제
      });
      await page.waitFor(1000);
      await page.evaluate(() => {
        // 크롤링해서 좋아요 했으니 피드 지우기
        const firstFeed = document.querySelector(
          "[id^=hyperfeed_story_id]:first-child"
        );
        firstFeed.parentNode.removeChild(firstFeed);
      });
      await page.waitFor(1000);

      // 존재하지 않는 경우에만 DB에 넣는다
      const exist = await db.Facebook.findOne({
        where: {
          postId: newPost.postId,
        },
      });
      if (!exist) {
        result.push(newPost);
      }
      await page.evaluate(() => {
        window.scrollBy(0, 200);
      })
    }
    console.log(result);
    // db에 저장
    await Promise.all(
      result.map((r) => {
        return db.Facebook.create({
          postId: r.postId,
          content: r.content,
          image: r.img,
          writer: r.name,
        });
      })
    );
    
    await page.close();
    await browser.close();
    await db.sequelize.close();
  } catch (err) {
    console.log(err);
  } finally {
  }
};

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

 

반응형