https://loy124.tistory.com/290
윗 글에 이어서 진행한다.
첫 피드 가져오기
^=로 진행하면 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 가 섞여서 나온다
이를 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 > node crawling' 카테고리의 다른 글
노드 크롤링 - 인스타그램 크롤링하기 (0) | 2020.08.19 |
---|---|
노드 크롤링 - 페이스북 로그인, 로그아웃 하기 (0) | 2020.08.16 |
노드 크롤링 - 인피니티 스크롤 사이트(unsplash) 크롤링하기 (3) | 2020.08.16 |
노드 크롤링 - 브라우저 사이즈 조절 및 스크린샷 찍기 (0) | 2020.08.16 |
노드 크롤링 - axios를 활용해서 이미지를 다운로드 하기 (0) | 2020.08.16 |