728x90
반응형

이전 포스팅에서 express의 미들웨어와 라우터 차이점에 대해서

 

미들웨어는 서버와 클라이언트 간의 중간 매개 역할을 담당합니다

그래서 서버 api 요청할 경우 미들웨어에서 공통업무를 할 수 있게 합니다

에러 페이지, 존재하지 않는 페이지 등

 

조금 더 상세한 정보를 알고 싶으시면 아래의 링크를 확인해주시길 바랍니다

 

 

 

2022.09.16 - [IT_Web/Nodejs] - Node express 서버의 미들웨어, 라우터 개념 제대로 알기 및 next함수 활용하기

 

Node express 서버의 미들웨어, 라우터 개념 제대로 알기 및 next함수 활용하기

이전 포스팅에서 express 서버 미들웨어 적용에 대해서 미들웨어가 무엇이며, 미들웨어를 어떻게 활용하는지에 대해서 알아보았습니다 미들웨어를 사용함에 있어 라우팅이 호출될 때 공통적으로

tantangerine.tistory.com

 


 

Express 라우터 url 파라미터 및 쿼리스트링 컨트롤 하기

 

라우터에서 서버 api요청 시 url의 파라미터와 쿼리 스트링을 제어할 수 있습니다

 

이 방법은 페이지를 이동하거나

서버에 요청하여 통신을 할 때 필요한 값을 전달할 때 자주 사용합니다

 

 


 

파라미터(parameters)란

 

번역하면 매개 변수라고 하며

url의 주소에 어떠한 특정한 값을 변수에 담아 유동적으로 

변화하는 값을 사용합니다

그래서 아래와 같이 '/top/:page'를 사용하면

/top/ 이후의 값은 변수에 담겨 라우터에서 사용이 가능합니다

 

 


 

쿼리스트링(Query String)

 

Query만 번역하면 의문,

String 컴퓨터 자료형으로 해석하면 문자를 의미합니다

그래서 이 두 가지를 합치면 의문 문자라고 해서

'/top/:page?id=컴퓨터&name=삼성'

위의 방식으로 의문(?)부터 시작된 문자를 쿼리 스트링이라고 합니다

 

 


코드 분석

 

그럼 코드를 보면서 나머지를 설명하겠습니다

아래와 같이 코드를 작성하고 npm start를 시작합니다

 

npm init, npm i express, npm i -d nodemon 등 설치는 

이전 포스팅을 찾아보시면 나와있으니 확인 부탁드립니다

 

 

const express = require('express');

const app = express();
app.set('port', process.env.PORT || 3000);

app.get('/top/:page', (req, res, next) => {
  console.log('top params: ', req.params)
  console.log('top queryString: ', req.query)
  res.send(`top: ${req.params.page} / id: ${req.query.id} name: ${req.query.name}`)
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 

 

 

위와 같이 실행하면 아래와 같이 이미지가 브라우저 및 콘솔 창에 노출됩니다

http://localhost:3000/top/head를 주소창에 입력했을 경우 

console.log함수에 노출되는 정보를 확인할 수 있습니다 

 

 

 

express_파라미터_쿼리스트링_1
express_파라미터_쿼리스트링 브라우저 확인

 

 

req.params, req.query로 접근해서 파라미터와 쿼리스트링을 가져올 수 있습니다

그리고 undefined가 발생하는 값을

브라우저 주소창에 http://localhost:3000/top/head?id=컴퓨터&name=삼성

입력하게 된다면 id, name 값이 할당될 것입니다

그 값은 아래의 이미지에서 확인할 수 있습니다

 

 

express_파라미터_쿼리스트링_2
express_파라미터_쿼리스트링 데이터

 

 

위의 데이터를 확인하고 아래와 같이 노출할 수 있는

코드를 작성할 수 있습니다

 

 

express_파라미터_쿼리스트링 브라우저 노출확인

 

 


이렇게 오늘은 파라미터와 쿼리스트링을 활용하는 방법을 알아보았습니다

 

그렇게 어렵지 않으며 nodeexpress 말고도 자바에서도 비슷한 방식으로

사용하고 있는 것만 알고 계시면 될 것 같습니다

 

하루에 1포스팅을 실천하기 위해 

공부도 나름 동기부여도 되는 것 같습니다

 

그럼 같이 파이팅 하시고 힘내세요!

우리의 IT 대모험 멀고도 머니깐요

 

 

728x90
반응형
728x90
반응형

이전 포스팅에서 express 서버 미들웨어 적용에 대해서

 

미들웨어가 무엇이며, 미들웨어를 어떻게 활용하는지에 대해서 알아보았습니다

미들웨어를 사용함에 있어 라우팅이 호출될 때 공통적으로 처리하거나

에러 미들웨어를 만들어 에러 발생 시 특정 함수를 실행하는 등

공통적인 부분을 담당하고 있는 미들웨어를 적용해보았습니다

 

더 상세한 정보를 알고 싶으시다면 아래의 링크를 확인해보세요

 

 

2022.09.15 - [IT_Web/Nodejs] - Node에서 express 서버 미들웨어 적용하기 그리고 middleware를 쓰는 이유가 뭘까??

 

Node에서 express 서버 미들웨어 적용하기 그리고 middleware를 쓰는 이유가 뭘까??

이전 포스팅 express 패키지 활용하여 서버 구축 및 html 서빙에 대해서 express에서 html 서빙 방법은 정말 간단합니다 express 내에서 html관련 패키지를 사용하고 있어 개발자들은 단지 메서드를 호출

tantangerine.tistory.com

 

 

express에서 미들웨어와 라우터 개념 제대로 알기

 

다른 개발자 분들은 use함수가 미들웨어가 아니라

get 함수 등(이외에도 많은 것들이 있습니다 post 등)

함수 내의 콜백 함수가 미들웨어라고 말하시는 분들이 있습니다

 

충분히 그렇게 생각할 수도 있지만

제 생각은 조금 다릅니다

 

미들웨어는 use 함수이며

라우터는 get 함수가 맞다고 생각합니다

 

그 이유는 미들웨어 격인 use 함수는 

라우터 함수가 호출될 때를 감지해서 우선 실행되는 함수입니다

클라이언트와 서버 간에 중간 매개 역할을 하여 컨트롤이 가능합니다

그래서 use 함수 자체가 미들웨어라고 할 수 있습니다

 

그리고 next 함수는 라우터와 미들웨어에서 편의성을 위한 기능일 뿐이라는 것입니다

 

그리고 엄연히 따지면 get 함수도 라우터가 아닌 서버 api 호출 문으로도 해석이 가능합니다

하지만 지금 현재 브라우저 주소 url서버의 연동하여 페이지를 전송받기 때문

라우터라고 부르는 것이 더 합리적일 것입니다 

 

 

 

express 서버 미들웨어와 라우터에 있는 콜백 함수 next 활용법

 

콜백 함수 next 함수를 호출하면

 

최우선 호출되는 것은 자신의 또 다른 콜백 함수가 호출됩니다

하지만 자신의 콜백 함수를 호출하지 않고 다음 미들웨어나 라우터 함수를 호출하기 위해서는 

next('route')로 인자 값을 'route'로 입력하는 것입니다

 

하지만 이때 미들웨어인 use함수는 다음 미들웨어나 라우터 함수를 호출하는 것이 아닌

자신의 콜백 함수를 다시 호출한다는 점이 미들웨어와 라우터가 다른 기능입니다

 

* 미들웨어에서 next('route')를 호출하면 자기 콜백 함수를 우선 호출하고

그다음 라우터를 실행하게 됩니다 

 

 

그럼 아래의 코드를 실행해 봅시다

 

 

const express = require('express');
const path = require('path');

const app = express();
app.set('port', process.env.PORT || 3000);

app.use((req, res, next) => {
  try {
    console.log('express server: use ::: 1')
    next('route')
  } catch (error) {
    console.log('express server: error ::: use ::: 1', error)
    next(error)
  }
}, (req, res, next) => {
  console.log('express server: use ::: 2')
   next()
})

app.get('/', (req, res, next) => {
  try {
    console.log('express server: main ::: 1')
    if(true){
      next('route')
    } else {
      next()
    }
  } catch (error) {
    console.log('express server: error ::: main ::: 1', error)
    next(error)
  }
}, (req, res, next) => {
  console.log('express server: main ::: 2')
  next()
})

app.get('/', (req, res, next) => {
  try {
    res.json({ page: 'main'})
    console.log('express server: main ::: 3')
  } catch (error) {
    console.log('express server: error ::: main :: 3', error)
    next(error)
  }
});

app.get('/top', (req, res, next) => {
  try {
    console.log('express server: top ::: 3')
    res.json({ page: 'top'})
  } catch (error) {
    console.log('express server: error ::: top', error)
    next(error)
  }
});

app.use((req, res, next) => { 
  res.status(404).send('존재하지 않는 페이지 입니다(404)')
});

app.use((error, req, res, next) => {
  res.send('error 발생')
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 

 

그럼 아래처럼 화면이 노출됩니다

 

미들웨어에서 next()를 사용해서 use ::: 1, use ::: 2 함수가 실행되며

그다음 main ::: 1 함수가 실행되고

main ::: 2는 건너뛰고

 main ::: 3 함수가 실행됩니다

 

 

 

 

이렇게 미들웨어와 라우터 제어하는 함수를 알아보았습니다

next함수 활용법과 라우터와 미들웨어 둘의 차이점을 알아보았습니다

 

라우터와 미들웨어에 존재하는 next함수에 대해 이야기하고 넘어가야겠습니다

express 공부한 지 얼마 안 되었지만

 

그렇게 좋은지 알 수가 없었습니다

 

next함수만 보더라도 라우터 get함수의 콜백 함수 next와 

미들웨어 use함수의 콜백 함수 next는 활용방식이 너무나도 다릅니다

 

use함수 내에서는 next('route') 통하지 않고

자기 내부의 콜백 함수를 다시 호출하게 됩니다

 

이렇게 동일한 네이밍을 가진 함수인자 값으로만

기능이 구분되는 것은 좋지 않은 방식입니다

 

그리고 use 함수의 에러 미들웨어 기능

동일한 use 함수에서 매개변수 인자 값의 개수 4개면

에러 미들웨어의 기능을 구현하게 된다는 것입니다

 

이 경우 인자 값을 누락하게 된다면 에러가 발생하거나 

전혀 생각지도 않은 기능이 구현된다는 것입니다 

 

애초에 use 함수가 아닌 확실하게 구분할 수 있는 네이밍이었다면

더 좋을 것 같다같다는 생각이었습니다

 

 


 

 

express 서버를 공부하면서

모듈을 조합해서 만든 라이브러리라는 것을 느꼈습니다

하지만 이런 것도 공부가 되리라 생각합니다

 

여러 언어를 배우면서 생각의 전환을 하거나

약간 내가 생각지도 못한 것들을 생각하게 한다는 것이지요

그러면서 저의 실력도 늘어나는 것이 아닐까 합니다

 

그럼 어려운 공부 조금 더 힘내셔서

우리의 IT 대모험을 헤쳐나가시길 기대합니다

 

그럼 다음 포스팅도 기대해주세요!

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅 express 패키지 활용하여 서버 구축 및 html 서빙에 대해서

 

express에서 html 서빙 방법은 정말 간단합니다

express 내에서 html관련 패키지를 사용하고 있어

개발자들은 단지 메서드를 호출하는 것만으로 html 서빙을 할 수 있습니다

 

조금 더 자세한 내용을 알고 싶으시다면 아래의 링크를 클릭하시길 바랍니다

 

2022.09.14 - [IT_Web/Nodejs] - Node활용해서 express 서버로 HTML 노출 및 서빙하기

 

Node활용해서 express 서버로 HTML 노출 및 서빙하기

이전 글 Npm이 무엇이며 활용법에 대해서 node에서 npm을 빼고는 말을 할 수 없을 정도로 중요한 부분을 차지합니다 그 이유는 많은 사람들이 개발한 오픈 소스를 관리해 주기 때문이지요 npm을 잘

tantangerine.tistory.com

 

 

지금 글은 이전 포스팅과 이어지는 부분이 있으니

이전 포스팅이  궁금하시거나 필요한 부분이 있으면 위의 링크를 클릭하시길 바랍니다

 


 

express 서버 미들웨어 적용하기

 

일단 미들웨어란 무엇일까요??

 

사전적 의미로는 양 쪽을 연결하여 데이터를 주고받을 수 있도록

중간 매개 역할을 하는 소프트웨어라고 할 수 있습니다

 

 express 서버의 미들웨어는 중간 매개 역할이 핵심이라고 할 수 있습니다

아래의 app.get() 메서드를 라우팅이라고 합니다

 

라우팅을 할 경우 에러가 발생할 경우

특정 api url이 호출될 경우

존재하지 않은 api url 호출될 경우

 

특정 함수를 실행,

에러 페이지를 노출,

특정 페이지를 노출 등

 

여러 가지의 공통 작업을 할 수 있습니다

 

 

express 서버 미들웨어 적용하기

 

아래의 코드를 보시면 app.use()가 정의되어있습니다

이 함수는 라우팅을 호출할 경우 app.use()가 실행합니다

 

즉, 라우팅 get()은 함수의 인자 값이

클라이언트에서 보낸 api 호출 url이 동일한 라우팅이 실행됩니다

 

이때 라우팅이 호출되면 라우팅 get()보다 

우선 use()인 미들웨어가 호출을 감지하여 우선 실행합니다

그리고 next()를 실행하게 되면서 다음 미들웨어나 라우팅이 실행됩니다

 

이때 미들웨어의 위치도 중요합니다

 

기본적으로 실행 순서는 위에서부터 아래까지 실행합니다

그래서 라우팅이 호출될 때마다 미들웨어인 use()최상단부터 하단까지 실행된다고 생각하시면 됩니다

 

하지만 라우팅보다 미들웨어가 아래에 있다면 라우팅이 실행이 끝나는 순간

더 이상 실행하지 않고 종료가 됩니다

 

아래의 코드를 보면서 더 설명을 이어가겠습니다

 

 

 

const express = require('express');
const path = require('path');

const app = express();
app.set('port', process.env.PORT || 3000);

app.use((req, res, next) => {
  console.log('Hello, Express ::: 1');
  next()
},(req, res, next) => {
  console.log('Hello, Express ::: 2');
  next()
},(req, res, next) => {
  console.log('Hello, Express ::: 3');
  next()
})

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, '/index.html'));
});

app.use((req, res) => { 
  res.send('페이지 호출 오류')
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 

 

위의 코드를 작성하고 npm start를 입력하고

인터넷 브라우저 주소창에 "http://localhost:3000/"를 입력하게 되면

 

아래와 같이 커맨드 창이 노출됩니다

 

express_미들웨어_적용
express 라우팅 적용 결과

 

오른쪽에 보시면 최상단에 위치한 미들웨어부터 실행되며

그다음 라우팅이 호출되어 

주소창에 http://localhost:3000/"/"이 부분이 라우팅에 감지되어 위와 같은 결과가 노출됩니다

하지만 여기서 "/top"를 입력하면 아래와 같이 노출됩니다

 

 

express_미들웨어_적용2
express 미들웨어 적용 결과

 

top라는 라우팅 호출 주소가 동일하지 않아

최하단에 위치한 미들웨어가 실행되어 페이지 호출 오류 문구가 노출됩니다

 

이 결과 라우팅이 실행되지 않으면 미들웨어가 반응한다는 것을 알 수 있습니다

 

그리고 미들웨어 또한 호출 url주소를 감지할 수 있습니다

아래와 같이 코드를 변경해보도록 하겠습니다

 

express_미들웨어_적용3
express 미들웨어 호출 url 주소제어

 

위와 같이 코드를 변경하고 실행한 다음

브라우저 주소창 "http://localhost:3000/"를 입력합니다

 

위의 결과와는 다르게 커맨드 창에 최상단 미들웨어가 실행되지 않은 현상을 확인할 수 있습니다

 

express_미들웨어_적용6
express 미들웨어 url 주소 함수 결과

 

브라우저 주소창에 "http://localhost:3000/top"를 입력하면

아래와 같이 커맨드 창이 노출되는 것을 확인할 수 있습니다

 

express_미들웨어_적용4
express 미들웨어 url 주소제어 결과

 


 

 

 

미들웨어 에러 미들웨어 적용하기

 

이번에는 라우팅 내에서 Error가 발생할 경우 

제어하는 에러 미들웨어를 만들어 보겠습니다

 

 

const express = require('express');
const path = require('path');

const app = express();
app.set('port', process.env.PORT || 3000);

app.get('/', (req, res) => {
  try {
    csole.log('Hello, Express ::: 1');
    res.sendFile(path.join(__dirname, '/index.html'));
  } catch (e) {
    console.log('Error, Express ::: 00', e)
    throw new Error(e)
  }
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 

 

위의 코드를 실행하고 브라우저도 이전과 같은 주소를 입력합니다

그럼 아래와 같은 화면을 노출합니다

 

console.log()를 철자가 틀리게 일부러 코드 작성하였습니다

그래서 에러가 발생하였습니다

 

express에서는 기본적으로 에러가 발생하면 아래와 같이 화면을 처리합니다

하지만 아래와 같은 에러 페이지서버의 디렉터리 구조가 상세히 명세되어있습니다

그렇기 때문에 보안상 취약할 수밖에 없기 때문에 직접 처리해야 합니다

 

 

express_에러_미들웨어_적용
express 라우터 에러 발생 페이지

 

에러 미들웨어는 아래와 같이 코드를  최하단에 추가합니다

*app.listen() 위에 추가하셔야 합니다

 

코드 추가할 경우 error와 next매개변수의 위치를 꼭 지켜주셔야

에러 미들웨어를 구현할 수 있습니다

 

 

express_에러_미들웨어_적용2
에러 미들웨어 코드 적용

 

아래와 같이 에러 페이지를 노출됩니다

 

 

express_에러_미들웨어_결과
에러 미들웨어 결과 페이지

 

 


 

미들웨어 http 상태 코드 제어하기

 

아래와 같이 코드를 추가합니다

미들웨어는 next를 추가하는 것을 꼭 확인하세요 

 

 

express_미들웨어_http_상태코드제어_적용
미들웨어 http 상태 코드 제어 코드

 

위와 같이 status(404)라고 호출한다면

아래와 같이 404로 http 상태 코드가 변한 것을 확인할 수 있습니다

미들웨어를 실행하면 에러가 아니기 때문에

위의 코드를 적용하지 않는다면 200번 코드가 표시됩니다

 

express_미들웨어_http_상태코드제어
미들웨어 http 상태 코드 적용 결과

 

 

 

이것을 활용한다면 클라이언트에 모든 http 상태 코드일괄적으로 설정해서 보낼 수 있습니다

 

그렇게 일괄적으로 보내게 되면 개발하는데 어려움이 있을 수 있습니다

 

다만, 운영 단계의 서비스 경우에는

모든 에러가 404로 보내게 되면

해커들에게는 지금 어떤 에러가 발생하는지 알 수 없기 때문입니다

 

보안으로는 좋은 면이 있습니다

 

 


 

 

node에서 express는 아주 많이 사용되고 있으며,

서브 웹 서버로도 많이 사용하고 있습니다

 

SI 개발자라면 node보다는 자바를 많이 사용하며,

최근 react로 개발하는 프로젝트가 많아지면서 

node의 중요성도 조금씩 높아지고

자연스레 express의 서브 웹 서버로써 활용도가

조금씩  높아지고 있습니다

 

SI 개발자라면 어떤 언어라도 공부를 해야겠지요

그러니 흥미가 가는 언어는 아무거나 공부를 하셔도 됩니다!

 

도움 안 되는 공부는 없으니까요

 

그렇게 공부하다 보면 코드를 간결하게 되며 함수적 프로그래밍도

자연스레 몸으로 습득할 수 있을 것입니다

 

그럼 우리의 IT 대모험을 위해서

파이팅 하시길 바랍니다

 

그럼 다음 포스팅에서 뵙겠습니다

728x90
반응형
728x90
반응형

이전 글 Npm이 무엇이며 활용법에 대해서

node에서 npm을 빼고는 말을 할 수 없을 정도로 중요한 부분을 차지합니다

그 이유는 많은 사람들이 개발한 오픈 소스를 관리해 주기 때문이지요

 

npm을 잘 활용한다고 하면 패키지를 어떻게 잘 관리할지에 대해서

조금은 생각을 하고 있는 사람이 아닐까 합니다

 

더 자세한 사항을 알고 싶으시다면 아래의 링크를 클릭하시길 바랍니다 

 

 

2022.09.10 - [IT_Web/Nodejs] - Node에서 중요한 NPM은 무엇이며, 그 활용법을 알아보자

 

Node에서 중요한 NPM은 무엇이며, 그 활용법을 알아보자

이전 포스팅 puppeteer 활용해서 웹크롤링에 대해서 웹크롤링은 업무적으로도 필요한 경우가 있습니다 그리고 배우는 것만으로도 충분히 재미를 느낄 수 있다고 생각합니다 그리고 웹크롤링은 비

tantangerine.tistory.com

 


 

Nodejs 활용해서 express 서버 구축하기

 

우선 처음 프로젝트를 만들 때 무엇을 해야 할지 모를 경우가 많습니다

일단 처음 node를 설치하였다면 프로젝트를 진행할 폴더에서

npm init을 하여 간단한 물음에 답을 합니다

 

그렇게 하면 package.json 생성되고

필요한 패키지를 설치하여 프로젝트를 진행합니다

 

그럼 파일이 생성되어 아래와 같은 구조를 가지게 될 것입니다

(index.html은 제가 미리 만든 파일입니다)

 

express_파일구조
파일 구조

 

그럼 express를 간단히 구현해 보도록 하겠습니다

 

먼저 개발에 유용한 패키지 nodemon을 설치하여 진행하겠습니다 

 

npm i -D nodemon

 

nodemon은 파일을 저장할 때마다 다시 npm start를 하지 않고

소스코드를 반영할 수 있게 하는 패키지입니다

 

아래와 같이 scripts를 설정하시고 진행하시길 바랍니다

 

express_package_파일정보
package.json 파일 정보

 


 

express 패키지 사용하기

 

커맨드 창에서 아래와 같이 패키지를 설치합니다

 

npm i express

 

아래와 같이 express패키지를 불러옵니다

require()node에서 모듈을 불러올 경우 사용할 수 있습니다

 

프로젝트 내에서 모듈을 불러올 수도 있으며,

npm이 관리하는 패키지일 경우에는

패키지명을 인자 값으로 할당해서 패키지를 불러올 수 있습니다

 

아래와 같이 서버를 만들고 

app.set()를 만들어서 port번호를 설정하게 됩니다

 

*env는 추후에 다시 설명해드리겠습니다

 

const express = require('express');
const path = require('path');

const app = express();
app.set('port', process.env.PORT || 3000);

app.get('/', (req, res) => {
  console.log('req: ', req)
  console.log('res: ', res)
  res.send(`Wellcome, Express Server`);
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 


 

express로 html 서빙하기

 

 

그럼 인터넷 브라우저를 열어 주소창에 "http://localhost:3000/" 입력합니다

 

아래와 같이 화면이 노출될 것입니다

왜 이렇게 노출될까요?

 

express_html_노출
express 노출 화면

 

아래의 app.get()는 라우터라는 명칭으로 서버에서 선언하여 사용합니다

이때 api 호출 방식은 get방식, post방식이 있습니다

 

*get방식 post방식의 차이는 추 후에 다시 설명해 드리겠습니다

 

그 라우터는 첫 번째 인자 값에 api 호출받을 주소를 지정하여

get방식을 호출하는 api 주소가 동일할 경우  두 번째 인자 값인 콜백 함수가 실행됩니다 

 

그 콜백 함수에서 (req, res) 두 개의 매개변수가 보일 것입니다

 

이 두 개의 매개변수의 정보들을 보면 화면이 왜 그렇게 노출되는지에 대한

궁금증을 풀 수 있을 것 같습니다 

 

아래의 코드에 제가 console.log를 호출해 놓았습니다

그럼 콘솔 창에 어떤 정보가 있는지 확인해 봅시다

 

 

express_get_변경
express 콘솔 찍어보기

 

 

아래는 req에 대한 정보가 json Object형으로  담겨있습니다

req정보는 클라이언트에서 요청할 때 보낸 정보들이 담겨있는 것을 알 수 있습니다

아래에 statusCode, url, params, query, route 등등 유용한 정보들이 있습니다

 

express_req_정보
express req 정보

 

 

그럼 실제 클라이언트에서는 어떻게 보내고 있는지 한번 확인해볼까요?

F12키를 눌러서 개발자 모드에서 api요청 보낸 정보들이 헤더에 담겨있는 것을 확인할 수 있습니다

 

 

express_클라이언트_요청
express 클라이언트 api요청 상세정보

 

그리고 두 번째 매개변수 res의 정보를 보겠습니다

res는 express에서 자체적으로 사용하는 패키지와 모듈이 있습니다

 

 

당연 req에서 받은 정보들도 res에서도 사용할 수 있도록 저장되어있으며

html을 기본 코드들이 내장되어있어

res.send()에서 매개변수 인자 값으로 문자열을 할당할 경우

그 인자 값의 정보들이 내장된 html 코드와 같이 페이지를 노출하게 됩니다

 

결과 값은 아래와 같습니다

 

express_html_자체서빙하기
express 자체 내장 html 서빙 화면

 

이 것을 이용해서 html 파일을 미리 만들어서 사용할 수 있습니다

위에 제가 미리 만들어 놓은 html은 확인하셨을 것입니다

 

 index.html을 아래와 같이 코드 작업을 합니다

 

 

<html>
<head>
  <meta charset="UTF-8" />
  <title>Express Server</title>
</head>
<body>
  <h1>Express Server</h1>
  <p>환영합니다.</p>
</body>
</html>

 

그리고 아래처럼 res.sendFile()를 활용해서 경로를 적용해서 매서드를 호출합니다

path node의 패키지로 프로젝트의 경로를 찾아 할당하기 유용한 패키지입니다

 

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, '/index.html')); // 변경코드
});

 

코드를 변경했다면 아래와 같이 변경된 화면을 확인할 수 있습니다

 

 

express_html서빙하기
HTML 파일 읽기로 HTML 서빙결과

 

 


 

 

express 서버 구현하는 것을 알아보았습니다

아직 미들웨어, DB 연동, 세분화된 라우터, 세션, 쿠키 활용 등

아직 갈길이 멀기만 합니다

 

블로그에 포스팅을 업로드할 때마다

콘텐츠를 걱정하는 블로거들이 많다고 합니다

하지만 저는 그런 걱정은 별로 안 하는 것 같습니다

그저 제가 공부한 것들이나 알고 있는 것들을 풀기만 하면 되니깐요

 

그래서 요즘은 블로그에 글을 쓰려고 공부하는지 공부하려고 글을 쓰는지

알 수 없을 정도가 되었습니다

 

하지만 그것 또한 저 한태는 좋은 영향을 주는 것 같습니다

동기부여와 더 진취적으로 변해가는 저의 모습을 보니깐요

 

1일 1 포스팅이 왜 어려운지 요즘 많이 생각하게 됩니다

 

앞으로도 꾸준할 수 있도록 노력하겠습니다

그러니 다 같이 파이팅 하시길 바랍니다

 

우리의 IT 대모험은 아직 끝나지 않았으니까요!

 

그럼 다음 글에서 뵙겠습니다

728x90
반응형
728x90
반응형

이전 포스팅 puppeteer 활용해서 웹크롤링에 대해서

웹크롤링은 업무적으로도 필요한 경우가 있습니다

그리고 배우는 것만으로도 충분히 재미를 느낄 수 있다고 생각합니다

그리고 웹크롤링은 비동기식으로 해결해야 해서 Promise를 자주 쓰며 async, await도 자주 활용합니다

그래서 이 개념을 잡는 공부도 가능하니 꼭 한번 공부해보세요

 

더 자세한 사항이 궁금하다면 아래의 링크를 클릭하시면 됩니다

 

 

2022.08.20 - [IT_Web/Nodejs] - nodejs 웹 크롤링을 Puppeteer 및 Promise 활용 완벽 파헤치고 CSV 파일 쓰기

 

nodejs 웹 크롤링을 Puppeteer 및 Promise 활용 완벽 파헤치고 CSV파일 쓰기

이전 포스팅에 대하여 이전 포스팅에서는 간단하게 웹 크롤링을 하는 방법을 알아보았습니다 axios와 cheerio를 사용해서 웹 크롤링을 해보았습니다 하지만 axios는 한계가 있습니다 싱글 페이지로

tantangerine.tistory.com

 

 

npm이란?

Node Package Manager의 약자로 여러 사람들이 만든 모듈을 모아둔 저장소입니다

npm에 저장된 모듈을 프로젝트 내에서 설치를 하여 간편하게 구현이 가능합니다

이러한 모듈들이 오픈 소스 생태계를 만들고 있습니다

이 모듈은 노드로 구성되어 있으며,

그것을 패키지라 명합니다.

 

 

npm 활용 패키지 설치하기

 

npm i [패키지] / npm i -g [패키지]  / npm i -D [패키지]

위 3가지 설치 방법이 있습니다

각기 다른 방법은 저장 위치가 다르며 그 사용법도 조금씩 다르게 구성됩니다

 

첫 번째 방법으로 설치하면 dependencies 저장되어 json 형태로 패키 지명이 입력됩니다

그래서 어떤 패키지가 설치되었는지 확인이 가능합니다

 

-D: 설치된 패키지는 개발에서만 사용될 수 있도록 devDependencies에 분리되어 관리가 됩니다

 

-g: 글로벌 설치를 하면 dependencies와 devDependencies에서 설치된 이력을 찾을 수 없고

사용할 경우에도 npm을 붙여서 사용하기보다는 패키지명만 커맨드 창에 입력하면 사용할 수 있습니다

하지만 패키지를 커맨드 창에서 입력해 사용하는 일은 많이 없습니다

그래서 설치 이력에도 안 남아서 관리인원이 변경되면 유지 보수하기에 많은 어려움이 있기 때문에

최근에는 많이 사용하지 않는 추세입니다 

 

package_json파일
package.json 파일

 

그리고 npm init을 하고 설치를 하면 아래와 같이 파일 구조가 형성됩니다

node_modules는 패키지가 설치되면 그 관련 모듈들이 설치되어

그 파일들의 패키지명이 나열되어있는 것을 확인할 수 있습니다

 

또한

package-lock.json이라는 파일을 확인할 수 있습니다

그 파일은 패키지의 버전들이 고정되어 관리하는 파일

 

package_lock_json파일
package-lock.json파일 형성 구조

 

 

npm에서 패키지 버전 관리 SemVer(유의적 버저닝) 방식에 대하여

 

Major(주 버전), Minor(부 버전), Patch(수 버전)의 형태로 버전이 관리된다

아래와 같이 버전이 관리되어있다면

 

express : "^4.18.1" 

 

Major 버전인 4에 해당하는 위치의 수는

어떠한 수정으로 하위 버전과 호환이 안될 경우 버전이 올라갑니다

 

Minor 버전인 18에 해당하는 위치의 수는

어떠한 수정으로 하위 버전과 호환이 되는 경우 버전이 올라갑니다

 

Patch 버전인 1에 해당하는 위치의 수는

기능에 버그를 해결했을 때 올라갑니다

 

 

 

버전 기호 사용법

 

이쯤 되면 "^" 이러한 기호가 궁금증을 유발합니다

 

^4.18.1: 패키지 업데이트 시 Minor 버전까지만 업데이트됩니다

즉, Major는 고정되고 그 이하 버전만 업데이트됩니다.

 

~4.18.1: 패키지 업데이트 시 Patch 버전까지만 업데이트됩니다.

즉, Minor이상은 고정되고 그 이하 버전만 업데이트됩니다.

 

그리고 <=, >=, <, >는 이상, 이하, 초과, 미만으로 사용할 수 있습니다

하지만 사용하지 않습니다

 

알파/베타/RC 버전이 존재할 수도 있습니다

(1.1.1-alpha.0, 2.0.0-beta.1, 2.0.0-rc.0)

 

설치할 때 버전을 지정해서 설치할 수 있습니다

npm i express@3.6.2

 

@latest는 최신을 의미합니다

그래서 아래와 같이 사용할 수 있습니다

npm i express@latest

 

 

npm 명령어 알아보기

 

npm outdated를 커맨드 창에 입력합니다

 

아래와 같이

현재 버전과 현재 상용 버전 그리고 최신 버전이 함께 노출됩니다

그럼 버전을 확인한 다음에

package.json에 패키지의 버전을 변경하여

npm update를 하게 되면 버전이 반영됩니다

 

npm_outdated
npm outdated 결과

 

버전 관리 방법은 이외에도 npm-check를 사용하면 조금 더 편하게 버전을 관리할 수 있습니다

npm i -g npm-check으로 패키지를 설치합니다

 

npm-check를 입력하면 

아래의 화면처럼 노출이 됩니다

MAJOR UP / NOTUSED? / NEW_VER! / MINOR UP / PKG ERR 등으로

패키지 버전 상태를 체크할 수 있습니다

 

 

npm_check
npm-check 결과 화면

 

그리고 버전을 간편하게 업데이트하는 방법도 있습니다

npm-check -u를 입력하게 되면 아래와 같은 화면이 노출되면서 버전을 관리할 수 있습니다

스페이스 바를 누르면 (*) 패키지가 선택되며 최종적으로 엔터키를 누르면

아래의 상용 버전으로 업데이트가 됩나다

 

npm_check_u
npm-check -u 입력 화면

 

 

 

아래의 버전을 업데이트하는 모습입니다

그리고 업데이트 결과를 보여주며 업데이트가 끝이 나게 됩니다

 

npm_check_installing
npm-check 설치중 화면

 

npm_check_업데이트결과화면
npm-check 업데이트 결과 화면

 

npm uninstall [패키 지명]: 패키지를 삭제하는 명령어입니다

 

npm search 검색어: npm 패키지에서 검색할 수 있는 사항들을 검색할 수 있습니다

 

npm_search_화면
npm search expo 입력 화면

 

npm info [패키 지명]으로 패키지의 정보를 확인할 수 있습니다

패키지를 만든 개발자 메일현재 상용 배포된 버전 상태

현재 패키지와 연관되어 있는 패키지 정보도 알 수 있습니다

키워드로 expo가 보입니다 npm search [keyword]로 검색 정보를 위와 같이 얻을 수 있습니다

 

npm_info_화면노출
npm info expo 입력시 화면노출

 

npm ls는 프로젝트 내에

어떤 패키지를 사용하고 있는지 트리구조로 볼 수 있습니다

 

아래와 같이 노출됩니다

 

npm_ls_화면노출
npm ls 화면 노출

 

이렇게 npm에 대해서 알아보았습니다

프로젝트를 진행하면서 npm에 대해서 제대로 모르고 사용한 것은 아닌가 하고 생각하게 되었습니다

이번 기회에 조금 더 깊이 알아보았으니 더 활용해보아야겠네요 

 

그럼 오늘 추석인데도 불구하고 블로그를 쓰는 저에게 칭찬을 주며

IT 대모험은 끝나지 않음을 확인하였습니다

 

그럼 모두들 즐거운 추석 되시고 

다음 글에서 뵙겠습니다

 

 

728x90
반응형
728x90
반응형

이전 글에서는 웹크롤링에서 간단하게 DB 연동하기에 대해서

 

DB 연동으로 웹크롤링을 보다 효율적으로 사용하는 방법을 알아보았습니다

DB 연동으로 정보를 저장해서 데이터 관리는 물론 중복된 데이터까지

관리가 가능하니 웹 크롤링을 보다 능률적으로 실행할 수 있을 것입니다

 

더 자세히 알고 싶으시다면 아래의 링크를 클릭해주세요

 

 

2022.09.03 - [IT_Web/Nodejs] - 웹 크롤링 puppeteer로 mysql 및 sequelize 활용하여 DB 연동하기

 

웹 크롤링 puppeteer로 mysql 및 sequelize 활용하여 DB연동하기

이전 글에서 간략하게 프록시가 무엇이며 프록시를 활용해서 IP를 변경하는 방법을 알아보았습니다 더 자세한 정보를 알고 싶으시다면 아래의 링크를 클릭하시길 바랍니다. 2022.09.02 - [IT_Web/Nodej

tantangerine.tistory.com

 

 

로그인 유지 및 파일 업로드 알아보기

 

 

오늘은 puppeteer를 활용해서 파일 업로드

로그인 정보를 유지하는 방법을 알아보려고 합니다

 

 

 

그럼 우선 전체 코드를 보시겠습니다

아래의 코드를 보시면 진행이 많이 되었습니다

이전 포스팅을 하나씩 참고하시면 완성하실 수 있으니 관심 있으시다면 확인해보세요

 

 

 

const puppeteer = require('puppeteer');
const writekeyboardList = require('./writekeyboardList');
const callElement = require('./mouseHandleHtml');
const dotenv = require('dotenv'); // npm i dotenv
const db = require('./models');
const fs = require('fs')

dotenv.config();

const setCrawler = async (production, isMouse = false) => {
  await db.sequelize.sync();
  let browser = await puppeteer.launch({
    headless: false,
    args:['--window-size=1920,1080'],
    userDataDir: 'C:\Users\kimsangjin\AppData\Local\Google\Chrome\User Data'
  });
  let page = await browser.newPage();
  await page.setViewport({
    width: 1080,
    height:1080,
  })

  if (!production && isMouse) {
    await callElement(page)
  }

  await page.on('dialog', async (dialog) => { 
    console.log(`dialog`, dialog) 
    console.log(`type: ${dialog.type()} / message: ${dialog.message()}`) 
    if(dialog.type() === 'prompt') {
      await dialog.accept('https://www.tistory.com/')
    }
    if(dialog.type() === 'alert'){
      await dialog.dismiss(); 
    }
    if(dialog.type() === 'confirm'){
      await dialog.accept();
    }
  })

  const setNewProxy = async(crawlerUrl) => {
    const newProxies = await page.evaluate(async() => {
      const ips = Array.from(document.querySelectorAll('tr > td:first-of-type > .spy14')).map((v) => v.innerText)
      const types = Array.from(document.querySelectorAll('tr > td:nth-of-type(2)')).map((v) => v.innerText)
      const anonymity = Array.from(document.querySelectorAll('tr > td:nth-of-type(3)')).map((v) => v.innerText)
      const latencies = Array.from(document.querySelectorAll('tr > td:nth-of-type(6) > .spy1')).map((v) => v.innerText)
      return ips.map((v, i) => {
        return {
          ip: v,
          type: types[i],
          latency: latencies[i],
          anonymity: anonymity[i].toString()
        }
      });
    });
    const fristProxies =  newProxies.filter((v) => v.type.startsWith('HTTP')).sort((p, c) => p.latency - c.latency)
    console.log('fristProxies', fristProxies)
    await Promise.all(fristProxies.map(async (v) => {
      return await db.Proxy.create({
        ip: v.ip,
        type: v.type,
        latency: v.latency,
        anonymity: anonymity.toString()
      });
    }))
    await page.close();
    await browser.close();
    browser = await puppeteer.launch({
      headless: false,
      args:['--window-size=1920,1080', '--disable-notifications', `--proxy-server=${fristProxies[0].ip}`]
    });
    page = await browser.newPage();
    await page.goto(crawlerUrl);
    await page.waitForSelector('.tf_keyword')
    await inputValue('.tf_keyword', '내아이피'),
    await Promise.all([
      buttonClick(['.ico_pctop.btn_search', 'ico_ksearch.btn_ksearch']),
      page.waitForNavigation(),
    ]);
    await setTime(4000)
    await page.close();
    await browser.close();
    await db.sequelize.close()
  }

  const handleWriteKeyboard = async (selector, id) => {
    const writeList = writekeyboardList()
    const idOfList = [...id]
    if(selector){
      await page.click(selector);
      console.log(`idOfList`, idOfList)
      for (const id of idOfList) {
        const inputValue = await writeList.filter(element => element.ShiftLeft === id || element.key === id )
        if (inputValue.length > 0) {
          const code = inputValue[0].code
          const ShiftLeft = inputValue[0].ShiftLeft === id ? 'ShiftLeft' : ''
          if (ShiftLeft) {
            await setTime(selector, 0, 'key')
            await page.keyboard.down(ShiftLeft);
            await setTime(selector, 0, 'key')
            await page.keyboard.press(code);
            await setTime(selector,0, 'key')
            await page.keyboard.up(ShiftLeft);
          } else {
            await page.keyboard.press(code);
          }
          await setTime(selector,0, 'key')
        }
      }
    } else {
      await page.keyboard.press(id);
    }
  }

  const getSelectEl = async(selector) => {
    const selectedEl = await page.evaluate((selector) => {
      console.log('selector', selector)
      const temEl = document.querySelector(selector)
      console.log('selector', temEl)
      return temEl
    }, selector)
        console.log('selector', selectedEl)
    return selectedEl
  }

  const fileUpload = async() => {
    const pathFile = 'C:\Users\kimsangjin\Desktop'
    const fileExitst = await fs.existsSync(`${pathFile}/travis_1.png`)
    if(fileExitst){
      const input = await page.$("#m_attach_file");
      await input.uploadFile(`${pathFile}/freelanceDeveloper.png`)
      return true
    }
    return false
  }

  const inputValue = async (selector, value) => {
    await page.evaluate((selector, value) => {
      document.querySelector(selector).value = value
    }, selector, value)
    await setTime(selector)
  }

  const buttonClick = async (selector, nextSelector = '', type = '') => {
    await page.evaluate((selector) => {
      if (document.querySelector(selector[0])) {
        document.querySelector(selector[0]).click();
      } else {
        document.querySelector(selector[1]).click();
      }
    }, selector)
    if(type === 'login'){
      // waitForRequest(요청 대기), waitForResponse(응답 대기)
      const res = await page.waitForResponse(res => {
        return res.url().includes('graphql')
      })
      const result = await res.json()
      console.log('login', result);
      if(await result.extensions.is_final){
        await setTime(type, 2000)
        await page.keyboard.press('Escape')
      }
    } else if(type === 'logOut'){
      await page.waitForNavigation()
    } else {
      if(nextSelector){
        await page.waitForSelector(nextSelector)
      }
      if(type === 'wait') await setTime(selector)
    }
  }

  const setTime = async (selector = 'normal', waitTime = 0, type= '') => {
    const setTime = await page.evaluate(async(waitTime, type) => {
      let timeList =[]
      if(type === 'key'){
         timeList = await [569, 311, 257, 175, 306, 123, 428, 497, 298, 211, 379, 108, 512, 139, 249];
      } else {
        timeList = await [3975, 4547, 5231, 6397, 7894, 3394, 4206, 5147, 6932, 7430, 3561, 4896, 5877, 6407, 7133];
      }
      let randomTime = await timeList[Math.floor(Math.random() * timeList.length)]
      if (waitTime > 0) randomTime = waitTime
      await new Promise(r => setTimeout(r, randomTime))
      return randomTime
    }, waitTime, type)
    console.log(`waitTime[${selector}]: `, setTime)
    return setTime
  } 


  return { fileUpload, getSelectEl, buttonClick, inputValue, handleWriteKeyboard, setTime, setNewProxy, page, browser }
}


const crawler = async () => {
  try {
    const { getSelectEl, buttonClick, inputValue, handleWriteKeyboard, setTime, setNewProxy, page, browser } = await setCrawler(JSON.parse(process.env.PRODUCTION));
    const id = await process.env.EMAIL;
    const password = await process.env.PASSWORD;
    await page.goto('https://office.hiworks.com/polarium.co.kr/home#loginUrl=mail/webmail/m_list/b0');
    await setTime()
    const isLogin = await getSelectEl('.menu.office')
    if(isLogin){
      console.log('이미 로그인이 되어있습니다.')
      await buttonClick(['.menu.office'], '.main-btn');
    } else {
      await page.waitForSelector('#office_id');
      await setTime()
      await handleWriteKeyboard('#office_id', id)
      await setTime()
      await handleWriteKeyboard('#office_passwd', password)
      await setTime()
      await buttonClick(['.int_jogin'], '.main-btn');
    }
      await page.waitForSelector('.main-btn')
      await buttonClick(['.main-btn'], '#to_addr');
      await page.waitForSelector('#to_addr')
      await inputValue('#to_addr', '김상진');
      await setTime('', 500)
      await page.keyboard.press('Enter');
      await page.waitForSelector('#m_attach_file')
      await fileUpload()
      await setTime('', 500)
      await buttonClick(['.detail_select > a']);
  } catch (e) {
    console.log(e);
  }
}

crawler()

 

로그인 유지 알아보기

먼저 로그인 유지 방법을 알아보도록 하겠습니다

 

 

아래의 userDateDir를 추가하면 간단하게 로그인 유지가 됩니다

하지만 크롬만 가능하니 크롬을 설치하고 아래의 경로대로 설정하시면 간단히 됩니다

 

puppeteer-로그인유지
로그인 유지 코드

 

아래처럼 로그인 유지가 된다면

로그인에만 있는 태그를 선택해서 있으면 로그인이 되었다는 의미로

분기를 하여 코드 작성할 수 있습니다

 

 

 

puppeteer-로그인분기
로그인 코드 분기

 

 

 

 

puppeteer 활용하여 파일 업로드 하기

 

이제는 어느 정도 하실 수 있다고 가정하겠습니다

그래서 여러 함수를 세팅해서 사용하고 있으니 참고하시길 바랍니다

 

로그인 후

fileUpload함수를 아래와 같이 실행합니다

 

 

puppeteer-파일업로드함수호출
파일업로드 실행

 

 

우선 파일 위치가 필요합니다

그 위치에 현재 올리려는 파일이 있는지 존재 여부를 확인하여

있을 때만 파일 업로드를 실행합니다

 

이때 이름은 db에 저장된 파일을 올리도록 하는 것이 좋을 듯합니다

다음에 기회 되면 적용하도록 하고

지금은 특정 파일만 올리도록 하겠습니다

 

 

puppeteer-파일업로드선언문
파일업로드 선언문

 

 

 

 

아래의 이미지를 보면 파일 첨부라는 글이 보이지만

그 밑에 input type='file'로 정의된 것을 확인할 수 있습니다

그것을 위에 처럼 input을 가져와서 .uploadFile()와 경로를 지정해서 호출하면

 

파일이 업로드되는 것을 확인할 수 있습니다

이때 꼭 page.$()활용해서 해야 한다는 점 확인하세요

 

 

 

태그 현재 상황
태그 현재 상황입니다

 

 

 

이렇게 로그인 유지와 파일 업로드를 알아보았습니다

node.js에서 메일 보낼 수 있는 라이브러리가 존재합니다

그러니 그것도 활용하는 것이 좋을 듯합니다

크롤링보다는 라이브러리를 사용하는 것이 간편하겠지요

 

그럼 오늘도 한걸음 나아가셨길 바라며

IT대모험을 즐기며 다음 포스팅에서 찾아뵙겠습니다

그럼 파이팅 하세요!

728x90
반응형
728x90
반응형

이전 글에서 간략하게 프록시가 무엇이며

프록시를 활용해서 IP를 변경하는 방법을 알아보았습니다

 

더 자세한 정보를 알고 싶으시다면 아래의 링크를 클릭하시길 바랍니다.

 

 

2022.09.02 - [IT_Web/Nodejs] - 웹 크롤링 puppeteer 프록시 IP 적용하기 그런데 대체 프록시 란? 뭘까?

 

웹 크롤링 puppeteer 프록시 IP 적용하기 그런데 대체 프록시 란? 뭘까?

이전 글 웹 크롤링 마우스 조작하기에 대해서 태그를 도저히 못 찾겠다면 마우스를 조작하여 직접 마우스 클릭하는 방법을 구현해서 웹크롤링을 합니다 하지만 마우스 조작하는 방법은 대도록

tantangerine.tistory.com

 

 

 

웹 크롤링은 정보를 추출하여 파일을 만들어 업무 효율높는데 목적이 있습니다

하지만 그 정보를 DB에 보관한다면 중복검사와 데이터 관리에 있어 보다 더 효율적으로

관리가 가능합니다

 

그래서 DB 연동은 웹크롤링을 보다 편하게 하기 위해서는 필수 조건이라고 생각합니다

 

그럼 DB연동 방법을 알아보도록 하겠습니다

 

mysql 설치방법은 다른 여러 블로그에 나와있습니다

그러니 생략하고 sequelize 라이브러리를 설치하는 방법을 알아보겠습니다

 

sequelize 라이브러리 설치하기

아래의 코드를 실행시켜 라이브러리를 설치합니다

 

npm i -g sequelize-cli
npm i --save sequelize
sequelize init
npm i mysql2

 

 

4개의 라이브러리를 설치하고 나면 디렉터리에 새로운 폴더가 추가가 되어있을 것입니다

 

config 폴더와 models폴더가 추가됩니다 

 

 

웹크롤링-DB연동
웹크롤링 DB연동

 

 

DB 연결 함수 및 config.json 설정값 변경하기

 

 

models의 index파일은 DB를 연결하는 설정 함수들이 포함되어있습니다

대문자 sequelize와 Sequelize가 다르니 꼭 확인 하시길바랍니다

 

 

웹크롤링-DB연동2
웹크롤링 연동 설정함수

 

 

config.jsonDB 연결을 위한

host주소 및 DB 패스워드, username 등이 정의되어있습니다

 

그래서 위의 이미지와 같이 사용하는 코드들이 작성되어있습니다

 

아래는 DB config파일입니다

 

웹크롤링-config
웹크롤링 config

 

이제 테이블을 만들어야 합니다

하지만 sequelize를 활용해서 models 폴더에 파일을 만들면 

테이블을 만들 수 있습니다

 

 

위의 디렉터리 구조에서 proxy.js가 보일 것입니다

그 파일을 보면 아래와 같이 칼럼이 정의되어있습니다

null값을 허용할지와 컬럼이 저장될 자료형이 어떤 것 인지도 정할 수 돼있으며

테이블마다 id는 자동부여됩니다

 

 

웹크롤링-proxy테이블
웹크롤링 proxy테이블

 

 

DB 조회 문 함수

sequelizeORM방식이라서 sql을 몰라도 DB를 다를 수 있지만 

DB를 조금 더 자세하게 알고 싶다면 sql을 필히 공부해야 합니다

너무 sequelize 믿지 마시고 sql을 조금씩 공부를 하시길 바랍니다

 

그럼 데이터를 조회해보는 

몇 가지 함수를 찾아보았습니다

한번 확인해보시고 더 많은 조회 문을 찾아보시길 바랍니다

 

 

 

await db.sequelize.sync(); //db를 연결하는 함수입니다

await Promise.all(fristProxies.map(async (v) => { // 테이블 insert문
  return await db.Proxy.create({ // Proxy파일이 module.export되어 데이터를 가져온다
    ip: v.ip,
    type: v.type,
    latency: v.latency,
    anonymity: anonymity.toString() //''만으로 문자열이 구성되면 에러가 발생한다
  });								// ""을 꼭 확인해야합니다
}))

const fastestProxy = await db.Proxy.findOne({ // 한개의 행을 가져오기
  order: [['latency', 'ASC']]
});

 

 

이것으로 DB 연동은 끝났습니다

간단하게 DB 연동을 할 수 있으니 꼭 사이드 프로젝트를 만들면서

자기의 역량을 키울 수 있는 계기가 되었으면 합니다

 

이것으로 웹크롤링 준비단계는 모두 끝이 났습니다

아래는 제일 처음 웹크롤링 포스팅입니다

관심 있으신 분들은 한번 읽어보시길 바랍니다

아니면 카테고리를 클릭해서 전체 글을 보셔도 되겠지요?

 

 

2022.08.17 - [IT_Web/Nodejs] - node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

 

node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

node.js 관련은 처음 소개하는 것 같습니다 다음에 node.js로 서버 구축하는 것도 알아볼 테니 많은 관심 부탁드립니다 우선 node 다운로드 링크 주소는 사이드바에 생성해 놓을 테니 확인해 보시길

tantangerine.tistory.com

 

 

그럼 다음 포스팅도 기대해주시길 바랍니다

 

우리의 길고 긴 IT 대모험은 항상 밝기만을 기원하겠습니다

그럼 다음 포스팅에서 만나요~ 

728x90
반응형
728x90
반응형

이전 글 웹 크롤링 마우스 조작하기에 대해서

 

태그를 도저히 못 찾겠다면 마우스를 조작하여

직접 마우스 클릭하는 방법을 구현해서 웹크롤링을 합니다

하지만 마우스 조작하는 방법은 대도록이면 안 사용하는 것이 좋겠지요?

 

더 상세한 정보가 궁금하시다면 아래의 링크를 클릭해주세요

 

 

2022.09.01 - [IT_Web/Nodejs] - 웹 크롤링 마우스 조작해서 클릭하기 태그를 도저히 못 찾겠다면!?

 

웹 크롤링 마우스 조작해서 클릭하기 태그를 도저히 못찾겠다면!?

이전 포스팅 얼럿창 컨트롤하기에 대하여 웹 크롤링 중 갑자기 얼럿창이 노출될 경우 닫지 않고서는 다음 진행이 어렵습니다 그래서 puppeteer는 이벤트 리스너와 같은 기능을 하는 함수를 가지고

tantangerine.tistory.com

 

 

프록시 란 컴퓨터 네트워크에서 다른 서버 상의 자원을 찾는

클라이언트로부터 요청을 받아 중계하는 서버를 말합니다

사용자의 입장에서는 자신의 웹 서핑 기록을 익명 화하기 위해 웹 프록시를 사용하기도 합니다

 

이러한 프록시 서버를 이용하여 우리의 IP를 다른 새로운 IP로 변경을 하여 웹크롤링을 시도합니다

그렇게 되면 익명의 유저로 또 다시 접근이 가능하게 됩니다

 

 

웹 프록시에서 새로운 IP 크롤링하여 웹 페이지 접속하기

 

setNewProxy 함수를 활용해서 다른 IP를 받는 작업을 합니다

 

먼저 나의 IP가 어떤 값으로 설정되어있는지

Daum 홈페이지 검색창 input에 value값을 할당하여

변경해가는 IP주소가 변경되는 과정을 볼 수 있을 것입니다

 

 

const puppeteer = require('puppeteer');
const writekeyboardList = require('./writekeyboardList');
const dotenv = require('dotenv'); // npm i dotenv
dotenv.config();

const setCrawler = async (production, isMouse = false) => {
  let browser = await puppeteer.launch({
    headless: false,
    args:['--window-size=1920,1080']
  });
  let page = await browser.newPage();
  await page.setViewport({
    width: 1080,
    height:1080,
  })
  
  const setNewProxy = async(crawlerUrl) => {
    const newProxies = await page.evaluate(async() => {
      const ips = Array.from(document.querySelectorAll('tr > td:first-of-type > .spy14')).map((v) => v.innerText)
      const types = Array.from(document.querySelectorAll('tr > td:nth-of-type(2)')).map((v) => v.innerText)
      const anonymity = Array.from(document.querySelectorAll('tr > td:nth-of-type(3)')).map((v) => v.innerText)
      const latencies = Array.from(document.querySelectorAll('tr > td:nth-of-type(6) > .spy1')).map((v) => v.innerText)
      return ips.map((v, i) => {
        return {
          ip: v,
          type: types[i],
          latency: latencies[i],
          anonymity: anonymity[i]
        }
      });
    });
    const fristProxies =  newProxies.filter((v) => v.type === 'HTTP' && v.anonymity === 'HIA').sort((p, c) => p.latency - c.latency)[0]
    await page.close();
    await browser.close();
    browser = await puppeteer.launch({
      headless: false,
      args:['--window-size=1920,1080', '--disable-notifications', `--proxy-server=${fristProxies.ip}`]
    });

    page = await browser.newPage();
    await page.goto(crawlerUrl);
    await page.waitForSelector('.tf_keyword')
    await inputValue('.tf_keyword', '내아이피'),
    await Promise.all([
      buttonClick(['.ico_pctop.btn_search', 'ico_ksearch.btn_ksearch']),
      page.waitForNavigation(),
    ]);
  }

  const handleWriteKeyboard = async (selector, id) => {
    const writeList = writekeyboardList()
    const idOfList = [...id]
    if(selector){
      await page.click(selector);
      console.log(`idOfList`, idOfList)
      for (const id of idOfList) {
        const inputValue = await writeList.filter(element => element.ShiftLeft === id || element.key === id )
        if (inputValue.length > 0) {
          const code = inputValue[0].code
          const ShiftLeft = inputValue[0].ShiftLeft === id ? 'ShiftLeft' : ''
          if (ShiftLeft) {
            await page.keyboard.down(ShiftLeft);
            await setTime(selector, 500)
            await page.keyboard.press(code);
            await setTime(selector,500)
            await page.keyboard.up(ShiftLeft);
          } else {
            await page.keyboard.press(code);
          }
          await setTime(selector,500)
        }
      }
    } else {
      await page.keyboard.press(id);
    }
  }

  const inputValue = async (selector, value) => {
    await page.evaluate((selector, value) => {
      document.querySelector(selector).value = value
    }, selector, value)
    await setTime(selector)
  }

  const buttonClick = async (selector, nextSelector, type) => {
    await page.evaluate((selector) => {
      if (document.querySelector(selector[0])) {
        document.querySelector(selector[0]).click();
      } else {
        document.querySelector(selector[1]).click();
      }
    }, selector)
    if(type === 'login'){
      // waitForRequest(요청 대기), waitForResponse(응답 대기)
      const res = await page.waitForResponse(res => {
        return res.url().includes('graphql')
      })
      const result = await res.json()
      console.log('login', result);
      if(await result.extensions.is_final){
        await setTime(type, 2000)
        await page.keyboard.press('Escape')
      }
    } else if(type === 'logOut'){
      await page.waitForNavigation()
    } else {
      if(nextSelector){
        await page.waitForSelector(nextSelector)
      }
      if(type === 'wait') await setTime(selector)
    }
  }

  const setTime = async (selector = 'normal', waitTime = 0) => {
    const setTime = await page.evaluate(async(waitTime) => {
      const timeList = await [3975, 4547, 5231, 6397, 7894, 3394, 4206, 5147, 6932, 7430, 3561, 4896, 5877, 6407, 7133];
      let randomTime = await timeList[Math.floor(Math.random() * timeList.length)]
      if (waitTime > 0) randomTime = waitTime
      await new Promise(r => setTimeout(r, randomTime))
      return randomTime
    }, waitTime)
    console.log(`waitTime[${selector}]: `, setTime)
    return setTime
  } 

  await page.goto('https://www.daum.net/');
  await page.waitForSelector('.tf_keyword')
  await inputValue('.tf_keyword', '내아이피'),
  await Promise.all([
    buttonClick(['.ico_pctop.btn_search']) ,
    page.waitForNavigation(),
  ]);
  
  await page.goto('http://spys.one/free-proxy-list/KR/');


  return { buttonClick, inputValue, handleWriteKeyboard, setTime, setNewProxy, page, browser }
}


const crawler = async () => {
  try {
    const { buttonClick, inputValue, handleWriteKeyboard, setTime, setNewProxy, page, browser } = await setCrawler(JSON.parse(process.env.PRODUCTION));
    const id = await process.env.EMAIL;
    const password = await process.env.PASSWORD;

    await setNewProxy('https://www.daum.net/')

    // await page.close();
    // await browser.close();

  } catch (e) {
    console.log(e);
  }
}

crawler()

 

 

위의 코드가 상당히 길어서 조금 부담될 수 있지만

 

이전 포스팅에서 알아보았던 것들을 재사용한 것이니

궁금하시면 이전 포스팅이나 카테고리를 클릭하시길 바랍니다

 

아래의 코드는 실질적인 크롤링 실행문이 있습니다

단지 setCrawler()가 실행되어 매서드 및 화면 노출을 하는 초기 세팅값을 설정합니다

 

Daum 홈페이지에서 검색창에 내 아이피를 인자 값으로 넣어

value를 할당하고

검색 버튼을 클릭해서 검색을 하여 IP주소를 확인합니다

 

그리고 setNewProxy()호출해서 다른 IP주소를 할당합니다

 

그럼 아래에서 setNewProxy함수에 대해 알아보도록 하겠습니다

 

 

웹크롤링-시작부
웹크롤링 시작부

 

 

 

 

우선 코딩 분석 전에 페이지의 형태를 확인해 봅시다

IP주소 그리고 proxy type, Anonymity, Latency가 보입니다

 

Proxy address는 IP주소이며,

Proxy type 우리가 흔히 알고 있는 http 관련 사항입니다

Anonymity는 ANM, HIA, NOA 이 있으며 이것은 익명성을 보장하는 정도라고 생각하시면 됩니다

HIA가 제일 높은 수치로 익명성을 보장받을 수 있습니다

Latency는 속도를 의미하며 수치가 낮을수록 빠르다고 할 수 있습니다

 

그럼 4가지의 값들을 가져오기 위해 웹크롤링을 진행합니다

 

 

웹크롤링-프록시
웹크롤링 프록시 페이지

 

 

 

4가지 타입의 태그에 접근하여 innerText 값을 가져옵니다

그것을 배열로 만들어서 newProxies 변수에 담아서 filter함수를 적용해서 제일 빠른 IP주소를 가져옵니다

가져온 IP주소를 변수에 담으면 기존 페이지와 브라우저를 닫고

새 브라우저와 새 페이지를 프록시 서버 설정하여 활성화합니다 

 

이때 Daum 홈페이지에 다시 한번 내 아이피를 확인하면

변경된 IP주소를 확인할 수 있습니다

 

그리고 페이지 중간중간 이동할 경우 waitForSelector()을 사용해서 새로운 페이지의 지정된 태그를 기다리게 됩니다

그리고 buttonClick함수가 실행되고 페이지가 이동된다면

Promise.all()을 사용하여 순차적으로 넣어 줍니다

 

* inputValue, buttonClick 함수의 설명은 이전 포스팅을 참고하세요 

 

 

웹크롤링-함수부
웹크롤링 함수부

 

 

이렇게 설정하면 프록시 IP주소를 가져올 수 있습니다

 

한번 직접 코딩을 해서 웹크롤링을 돌려보는 것이 더욱 이해가 빠르다고 생각합니다

그럼 오늘도 이렇게 공부를 하였습니다

 

IT 개발이란 것은 영원히 공부를 해야 하지 않을까? 합니다

새로운 언어들이 나오고 웹의 새로운 환경들을 나오면서

더욱 우리는 공부를 해야 할 듯합니다

 

아직 IT 대모험이 끝나지 않았으니까

앞으로도 힘내시길 바랍니다

 

그럼 다음 포스팅도 기대해주세요

 

 

 

 

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅 얼럿창 컨트롤하기에 대하여

 

웹 크롤링 중 갑자기 얼럿창이 노출될 경우 닫지 않고서는

다음 진행이 어렵습니다

그래서 puppeteer이벤트 리스너와 같은 기능을 하는 함수를 가지고 있습니다 

그 함수는  page.on 이며, 여러 인자 값을 받아 여러가지 이벤트를 핸들링할 수 있습니다

 

더 상세한 정보를 알고 싶으시면 아래의 글을 참고해주시길 바랍니다

 

 

2022.08.31 - [IT_Web/Nodejs] - 웹 크롤링 puppeteer로 alert, prompt, confirm 창 얼럿창 제어 및 컨트롤 하기

 

웹 크롤링 puppeteer로 alert, prompt, confirm 창 얼럿창 제어 및 컨트롤 하기

이전 포스팅 키보드 코드 입력하기에 대해서 puppeteer로 웹 크롤링할 때 키보드를 입력하는 방법을 알아보았습니다 키보드의 자판만큼이나 코드도 엄청 많았습니다 그래서 그 문서를 Array형 Object

tantangerine.tistory.com

 

 

웹 크롤링 puppeteer 활용해서  마우스 조작하기

 

웹 크롤링에서 마우스를 조작하는 이유는 무엇일까요?

간단한 매크로를 만들거나, 태그를 정말 찾기 어려울 때 어쩔 수 없이 사용하게 될 것입니다

마우스를 조작해야 될 경우가 없는 게 좋겠지만 해야 할 경우가 생길 수 있으니

알아두면 편하겠죠?

 

그럼 코드를 보면서 그 방법을 알아보도록 하겠습니다

 

 

const puppeteer = require('puppeteer');
const callElement = require('./mouseHandleHtml');
const dotenv = require('dotenv'); // npm i dotenv
dotenv.config();

const setCrawler = async (production) => {
  const browser = await puppeteer.launch({
    headless: production,
    args:['--window-size=1920,1080']
  });
  const page = await browser.newPage();
  await page.setViewport({
    width: 1080,
    height:1080,
  })
  await page.goto('https://facebook.com');

  if (!production) {
    await callElement(page)
  }

  const setTime = async (selector = 'normal', waitTime = 0) => {
    const setTime = await page.evaluate(async(waitTime) => {
      const timeList = await [3975, 4547, 5231, 6397, 7894, 3394, 4206, 5147, 6932, 7430, 3561, 4896, 5877, 6407, 7133];
      let randomTime = await timeList[Math.floor(Math.random() * timeList.length)]
      if (waitTime > 0) randomTime = waitTime
      await new Promise(r => setTimeout(r, randomTime))
      return randomTime
    }, waitTime)
    console.log(`waitTime[${selector}]: `, setTime)
    return setTime
  } 

  return { setTime, page: page, browser: browser }
}


const crawler = async () => {
  try {
    const production = await process.env.PRODUCTION;
    console.log('production', production, typeof(JSON.parse(production)))
    const { setTime, page, browser } = await setCrawler(JSON.parse(production));

    await setTime('', 2000)
    await page.mouse.move(100, 100);
    await page.mouse.down()
    await setTime('', 2000)
    await page.mouse.move(1000, 400);
    await setTime('', 2000)
    await page.mouse.click(1000, 400);
    await setTime('', 2000)

  } catch (e) {
    console.log(e);
  }
}

crawler()

 

env파일이 아래와 같이 설정되어있다고 합시다

PRODUCTION이 true 될 경우 실 운영 버전으로 개발이 다 끝난 경우에 설정하는 값이라고 합시다

지금은 false로 개발을 진행한다고 설정하고 웹 크롤링 과정을 지켜보며 개발을 하게 됩니다

 

 

웹크롤링-env
웹 크롤링 env 파일

 

production 변수에 envPRODUCTION을 할당합니다

그 설정된 값을 자료형을 boolean으로 변경하고

setCrawler() 함수에 매개변수 인자 값으로 production을 할당합니다

 

 

웹크롤링-env관리
웹 크롤링 env 관리하기

 

빨간 테두리에는 할당된 인자 값인 production을 headles를 false로 할당해서

웹 크롤링하는 과정을 화면으로 지켜볼 수 있게 false로 지정합니다

headless 머리가 없다는 뜻으로 false로 머리가 있다는 뜻이 됩니다

그럼 화면을 볼 수 있다는 것이겠지요?

 

 

 

 

위의 노랑 테두리는 

production이 false이면 if문을 안을 실행하게 됩니다

그래서 callElement()에 page 객체를 인자 값으로 호출하게 됩니다

즉, 운영이 아닌 환경설정값으로 마우스를 노출시킬 수 있는 Element를 생성하는 함수를 호출하게 됩니다

운영이라면 if문을 실행하지 않고 지나치게 될 것입니다

 

callElement는 아래와 같이./mouseHandleHtml 파일에서 함수를 가져오게 되어있습니다

 

웹크롤링-마우스조작
웹크롤링 마우스 조작 노출 element 생성

 

 

아래의 코드로 정의된 callElement함수는 

마우스가 어디에 있는지 화면에서 확인할 수 있는 html, CSS을 생성하게 됩니다

위와 말했듯이 개발에만 생성하게 되고  그 이유는 웹 크롤링하는 과정을 보면서 개발하기 위해서입니다

 

 

module.exports = async function  callElement(page) {
  await page.evaluate(() => {
      (() => {
          const box = document.createElement('div');
          box.classList.add('mouse-helper');
          const styleElement = document.createElement('style');
          styleElement.innerHTML = `.mouse-helper {
              pointer-events: none;
              position: absolute;
              z-index: 1000000000;
              top: 0;
              left: 0;
              width: 20px;
              height: 20px;
              background: rgba(0,0,0,.4);
              border: 1px solid white;
              border-radius: 10px;
              margin-left: -10px;
              margin-top: -10px;
              transition: background .2s, border-radius .2s, border-color .2s;
            }
            .mouse-helper.button-1 {
              transition: none;
              background: rgba(0,0,0,0.9);
            }
            .mouse-helper.button-2 {
              transition: none;
              border-color: rgba(0,0,255,0.9);
            }
            .mouse-helper.button-3 {
              transition: none;
              border-radius: 4px;
            }
            .mouse-helper.button-4 {
              transition: none;
              border-color: rgba(255,0,0,0.9);
            }
            .mouse-helper.button-5 {
              transition: none;
              border-color: rgba(0,255,0,0.9);
            }
            `;
          document.head.appendChild(styleElement);
          document.body.appendChild(box);
          document.addEventListener('mousemove', event => {
            box.style.left = event.pageX + 'px';
            box.style.top = event.pageY + 'px';
            updateButtons(event.buttons);
          }, true);
          document.addEventListener('mousedown', event => {
            updateButtons(event.buttons);
            box.classList.add('button-' + event.which);
          }, true);
          document.addEventListener('mouseup', event => {
            updateButtons(event.buttons);
            box.classList.remove('button-' + event.which);
          }, true);
          function updateButtons(buttons) {
            for (let i = 0; i < 5; i++)
              box.classList.toggle('button-' + i, !!(buttons & (1 << i)));
          }
        })();
    });
}

 

 

 

그리고 실질적으로 웹 크롤링을 실행하는 함수를 보시면

웹 크롤링 화면 설정과 필요 기능 함수를 선언하는 setCrawler()가 있습니다

 

그리고 setTime으로 잠시 대기하여 마우스를 움직이며 드래그하는 모습을 볼 수 있습니다

 

 

 

웹크롤링-마우스조작2
웹크롤링 마우스 조작 코드

 

 

아래의 이미지를 보시면 드래그를 한 후 그 자리 좌표에서 클릭을 하면

드래그가 없어지는 것을 확인할 수 있습니다

 

 

 

 

 

 

 

이렇게 웹 크롤링 마우스 조작 법을 알아보았습니다

그렇게 어렵지 않고 함수 기능만 알고 있다면

간편하게 구현할 수 있다는 것을 알게 되었습니다

 

저는 웹 크롤링을 공부하는 이유는 여러 가지 정보를 아침에 모아서 보려고 합니다

그래서 같이 공부하고 끝이 나면

개인적인 프로젝트를 진행하는 과정도 포스팅을 하도록 하겠습니다

 

후~ 그럼 오늘도 고생하셨습니다

 

공부가 힘들고 어렵겠지만 조금만 힘내시면 

그 시기는 지나갈 것이라 생각합니다

 

난중에는 너무 자연스럽게 코딩하는 자신의 모습을 볼 수 있을 것입니다

그럼 그때까지 파이팅 하시고

 

다음 포스팅도 기대해주세요!

감사합니다

728x90
반응형
728x90
반응형

이전 포스팅 키보드 코드 입력하기에 대해서

puppeteer로 웹 크롤링할 때 키보드를 입력하는 방법을 알아보았습니다

키보드의 자판만큼이나 코드도 엄청 많았습니다

그래서 그 문서를 Array형 Object객체로 만들어 자동으로 입력할 수 있게 만들었습니다

같은 함수를 계속 나열해서 쓰지 않아도 되니 코드도 간결해지고 좋았던 것 같습니다

그리고 무엇보다도 사용자의 유저 최적화를 할 수 있는 장점이 있지요

id를 입력하는 경우 키보드 코드로 입력 안 할 경우에는 한 번의 순간에 ID 값이 입력되니

분명 유저처럼 보이지는 않을 것입니다

 

그래서 봇으로 보이지 않게 하기 위한 방법이니 아주 유용할 것 같습니다

 

더 자세한 사항은 아래의 링크를 클릭해서 확인해주세요

 

 

 

2022.08.30 - [IT_Web/Nodejs] - 웹 크롤링 puppeteer 활용 및 keyboard press 함수로 키보드 코드 입력하기

 

웹 크롤링 puppeteer 활용 및 keyboard press 함수로 키보드 코드 입력하기

이전 포스팅 웹 크롤링 로그인 로그아웃에 대하여 로그인과 로그아웃에 대하여 알아보았습니다 코드를 함수형으로 만들어서 이전 코드보다 간결하게 만들어 보았지요 그리고 웹 크롤링에서는

tantangerine.tistory.com

 

웹 크롤링 puppeteer로 alert, prompt, confirm 창 컨트롤 하기

 

웹 크롤링 도중 alert, confirm, prompt창이 노출되면 아무것도 하지 못하게 된다

이럴 경우 창을 제어하는 함수를 구현해보려고 합니다

아래와 같이 setCrawler 함수에 여러 가지가 세팅되어있다

천천히 분석해보시길 바라며 상세한 설명은 아래에서 하겠습니다

 

const puppeteer = require('puppeteer');
const writekeyboardList = require('./writekeyboardList');
const dotenv = require('dotenv'); // npm i dotenv
dotenv.config();

const setCrawler = async () => {
  const browser = await puppeteer.launch({
    headless: false,
    args:['--window-size=1920,1080']
  });
  const page = await browser.newPage();
  await page.setViewport({
    width: 1080,
    height:1080,
  })
  await page.goto('https://facebook.com');

  await page.on('dialog', async (dialog) => { 
    console.log(`type: ${dialog.type()} / message: ${dialog.message()}`) 
    if(dialog.type() === 'prompt') {
      await dialog.accept('https://www.tistory.com/')
    }
    if(dialog.type() === 'alert'){
      await dialog.dismiss(); 
    }
    if(dialog.type() === 'confirm'){
      if (dialog.message() === '확인을 누르시면 tistory홈페이지로 이동합니다.') {
        await dialog.accept();
      } else {
        await dialog.dismiss();
      }
    }
  })

.... 이하 생략 이전 포스팅 참고 바랍니다.

 

 

 

아래의 코드는 실질적으로 웹 크롤링을 실행하는 함수들이 선언되어있습니다.

보시면 setCrawler()가 보이시죠? 브라우저를 띄우고 새 탭 화면을 노출시키고 화면의 비율을 맞추는 등

기본적인 환경설정을 하고 나서 선언된 함수들을 return 해서 사용하고 있습니다

 

오늘 포스팅에서는 사용하지는 않지만 이전 포스팅을 참고해주세요

 

그럼 본론으로 alert, confirm, prompt 컨트롤하는 함수를 선언해서 확인해보겠습니다

우선 한 개씩 하기 때문에 나머지는 주석을 하면서 진행해 보도록 하겠습니다

 

const crawler = async () => {
  try {
    const { buttonClick, inputValue, handleWriteInuput, setTime, page, browser } = await setCrawler();
    await page.evaluate(async() => {
      alert('alert창을 닫으면 티스토리 홈페이지로 이동합니다.')
      location.href = 'http://www.tistory.com'
    })

    await page.evaluate(() => {
      if (confirm('확인을 누르시면 tistory홈페이지로 이동합니다.')) {
        location.href = 'http://www.tistory.com'
      }
    })

    await page.evaluate(() => {
      const promptData = prompt('url 주소를 입력하세요.')
      location.href = promptData
    })

  } catch (e) {
    console.log(e);
  }
}

crawler()

 

 

아래와 같이 코드가 구현되어있습니다

먼저 빨간 테두리에 page.on()가 있습니다

이 함수는 EventListener로서 어떠한 이벤트 가 발생할 때 감지해서 

이벤트가 발생한 함수에 영향을 주는 함수입니다

 

매개변수('dialog', function)로 받아 콜백 함수에서 실행을 이어갑니다

그래서 콜백 함수 내에서 dialog에 대해 type, message를 분기로 사용해서

 

 

웹크롤링-이벤트리스너
웹크롤링 이벤트 리스너 함수

 

 

alert은 창이 뜨면 그냥 바로 창을 바로 제거하는 함수를 사용합니다

alert창이 사라지고 다음 단계를 진행할 수 있게 되며,

선언된 alert함수 그 이후의 선언된 함수가 실행되어

 

웨크롤링-alert제어
웹크롤링 alert창 제어

 

 

confirm창이 승낙할 경우 confirm함수 true, 거절할 경우 false 반환되게

dialog.accept()와 dialog.dismiss()를 호출하여 confirm()가 선언된 곳에 값을 전달합니다

 

웹크롤링-confirm제어
웹크롤링 confirm 창 제어

 

 

prompt창은 input이 활성화되면서 글자를 입력할 수 있으며,

그 창에 노출되고 dialog.accept() 인자 값으로 'https://www.tistory.com'로 할당하게 되면

홈페이지가 이동하게 됩니다

 

 

웹크롤링-prompt제어
웹크롤링 prompt창 제어

 

 

 

오늘은 여기까지 웹크롤링 중 EventListener를 사용하는 page.on()를 선언해서

alert, confirm, prompt 활성화될 경우 제어하는 방법을 알아보았습니다

 

얼럿 창이 갑자기 활성화된다면 웹크롤링이 갑자기 멈출 수도 있기 때문에

이벤트 리스너가 꼭 필요할 것입니다

 

오늘도 이렇게 한걸음 나아갔다고 생각합니다

우리의 IT대모험은 아직 남았으니 파이팅 하자고요~!!

 

그럼 다음 포스팅도 기대해주세요~

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅 웹 크롤링 로그인 로그아웃에 대하여

로그인과 로그아웃에 대하여 알아보았습니다

코드를 함수형으로 만들어서 이전 코드보다 간결하게 만들어 보았지요

그리고 웹 크롤링에서는 무엇보다

태그를 찾는것중요하여 같이 알아보았습니다

 

좀 더 상세히 알고 싶으시다면 아래의 링크를 클릭하시길 바랍니다

 

 

2022.08.29 - [IT_Web/Nodejs] - 웹 크롤링 로그인, 로그아웃 그리고 waitForResponse 응답대기 활용하기 및 태그찾기, 태그 값 할당

 

웹 크롤링 로그인, 로그아웃 그리고 waitForResponse 응답대기 활용하기 및 태그찾기, 태그 값 할당

이전 포스팅에서 웹 크롤링 인피니트 스크롤링이 무엇인가 알아보았으며, 인피니트 스크롤링이 적용된 페이지에 대해서 이미지를 다운하고 파일로 만드는 방법을 알아보았습니다 인스타그램,

tantangerine.tistory.com

 

 

앞서 웹 크롤링에서 중요한것은 태그를 찾는것이라 했습니다

하지만 중요한것이 하나 더있습니다

그것은 바로 비동기입니다

하지만 비동기에서도 순ㅍ차적으로 실행할지 비순차적으로 할것인지가 중요합니다

웹 크롤링할 경우 순차적으로 진행할 경우가 많기 때문에 비동기식이며 순차적인 방법이 중요합니다

 

이전 포스팅에서는 로그인할경우 아이디를 inputvalue에 할당하여 한번에 아이디의 문자열이 모두 들어갔습니다

하지만 그렇게 된다면 유저가아닌 봇으로 의심을 받아 제재를 받을 수 있습니다 

그래서 이 부분을 함수형으로 만들어 간결하게 만들어봅시다

 

아래 handleWriteInput 함수를 만들어서 process.env.EMAIL을 id로 받아서 id를 확장 연산자를 사용해서

배열로 만들어서 로그인창에서 한글자씩 시간을 두고 글자를 입력하게됩니다

좀 더 자세한것은 아래에서 확인해보겠습니다

 

 

const puppeteer = require('puppeteer');
const writekeyboardList = require('./writekeyboardList');
const dotenv = require('dotenv'); // npm i dotenv
dotenv.config();

const setCrawler = async () => {
  const browser = await puppeteer.launch({
    headless: false,
    args:['--window-size=1920,1080']
  });
  const page = await browser.newPage();
  await page.setViewport({
    width: 1080,
    height:1080,
  })

  const handleWriteInuput = async (selector, id) => {
    const writeList = writekeyboardList()
    const idOfList = [...id]
    await page.click(selector);
    console.log(`idOfList`, idOfList)
      for (const id of idOfList) {
        const inputValue = await writeList.filter(element => element.ShiftLeft === id || element.key === id )
        if (inputValue.length > 0) {
          const code = inputValue[0].code
          const ShiftLeft = inputValue[0].ShiftLeft === id ? 'ShiftLeft' : ''
          if (ShiftLeft) {
            await page.keyboard.down(ShiftLeft);
            await setTime(selector, 500)
            await page.keyboard.press(code);
            await setTime(selector,500)
            await page.keyboard.up(ShiftLeft);
          } else {
            await page.keyboard.press(code);
          }
          await setTime(selector,500)
        }
      }
  }

  const inputValue = async (selector, value) => {
    await page.evaluate((selector, value) => {
      document.querySelector(selector).value = value
    }, selector, value)
    await setTime(selector)
  }

  const buttonClick = async (selector, nextSelector, type) => {
    await page.evaluate((selector) => {
      document.querySelector(selector).click();
    }, selector)
    if(type === 'login'){
      // waitForRequest(요청 대기), waitForResponse(응답 대기)
      const res = await page.waitForResponse(res => {
        return res.url().includes('graphql')
      })
      const result = await res.json()
      console.log('login', result);
      if(await result.extensions.is_final){
        await setTime(type, 2000)
        await page.keyboard.press('Escape')
      }
    } else if(type === 'logOut'){
      await page.waitForNavigation()
    } else {
      if(nextSelector){
        await page.waitForSelector(nextSelector)
      }
      await setTime(selector)
    }
  }

  const setTime = async (selector = 'normal', waitTime = 0) => {
    const setTime = await page.evaluate(async(waitTime) => {
      const timeList = await [3975, 4547, 5231, 6397, 7894, 3394, 4206, 5147, 6932, 7430, 3561, 4896, 5877, 6407, 7133];
      let randomTime = await timeList[Math.floor(Math.random() * timeList.length)]
      if (waitTime > 0) randomTime = waitTime
      await new Promise(r => setTimeout(r, randomTime))
      return randomTime
    }, waitTime)
    console.log(`waitTime[${selector}]: `, setTime)
    return setTime
  } 
  return { buttonClick, inputValue, handleWriteInuput, setTime, page: page, browser: browser }
}


const crawler = async () => {
  try {
    const { buttonClick, inputValue, handleWriteInuput, setTime, page, browser } = await setCrawler();
    await page.goto('https://facebook.com');

    const id = await process.env.EMAIL;
    const password = await process.env.PASSWORD;
    
    // 로그인
    //await inputValue('#email', id);
    handleWriteInuput('#email', id)
    await inputValue('#pass', password);
    await buttonClick('[name="login"]', '', 'login');
   

    // 로그아웃
    await buttonClick('.i8zpp7h3', '.i1n1lj7b.mmwt03ec');
    await buttonClick('.i1n1lj7b.mmwt03ec > div:last-child > div', '', 'logOut')
    await page.close();
    await browser.close();
  } catch (e) {
    console.log(e);
  }
}

crawler()

 

 

아래의 함수를 확인하면 각자의 역할들이 존재합니다

아래에서 차근차근 설명해 보겠습니다

 

웹크롤링-키보드코드
키보드 코드 사용하여 입력하기 함수형!

 

 

먼저 빨강 테두리의 함수는 키보드 코드 리스트가 담겨져 있으며,

require()를 사용해서 파일에 접근해서 함수를 사용할 수 있게 합니다

 

 

웹크롤링-nodejs
웹크롤링 키보드 코드 파일

 

 

파일에는 아래와 같은 함수가 export되고 있습니다

code가 위의 keyboard.press()의 매개변수 인자값이 되며

Shift를 누른 상태 값은 ShiftLeft의 값이 대체되어 값이 입력된다

 

 

 

 

module.exports = function writekeyboardList() {
  const keyboard = [
    {'keyCode': 48, 'code': 'Digit0', 'ShiftLeft': ')', 'key': '0'},
    {'keyCode': 49, 'code': 'Digit1', 'ShiftLeft': '!', 'key': '1'},
    {'keyCode': 50, 'code': 'Digit2', 'ShiftLeft': '@', 'key': '2'},
    {'keyCode': 51, 'code': 'Digit3', 'ShiftLeft': '#', 'key': '3'},
    {'keyCode': 52, 'code': 'Digit4', 'ShiftLeft': '$', 'key': '4'},
    {'keyCode': 53, 'code': 'Digit5', 'ShiftLeft': '%', 'key': '5'},
    {'keyCode': 54, 'code': 'Digit6', 'ShiftLeft': '^', 'key': '6'},
    {'keyCode': 55, 'code': 'Digit7', 'ShiftLeft': '&', 'key': '7'},
    {'keyCode': 56, 'code': 'Digit8', 'ShiftLeft': '*', 'key': '8'},
    {'keyCode': 57, 'code': 'Digit9', 'ShiftLeft': '\(', 'key': '9'},
    {'keyCode': 65, 'code': 'KeyA', 'ShiftLeft': 'A', 'key': 'a'},
    {'keyCode': 66, 'code': 'KeyB', 'ShiftLeft': 'B', 'key': 'b'},
    {'keyCode': 67, 'code': 'KeyC', 'ShiftLeft': 'C', 'key': 'c'},
    {'keyCode': 68, 'code': 'KeyD', 'ShiftLeft': 'D', 'key': 'd'},
    {'keyCode': 69, 'code': 'KeyE', 'ShiftLeft': 'E', 'key': 'e'},
    {'keyCode': 70, 'code': 'KeyF', 'ShiftLeft': 'F', 'key': 'f'},
    {'keyCode': 71, 'code': 'KeyG', 'ShiftLeft': 'G', 'key': 'g'},
    {'keyCode': 72, 'code': 'KeyH', 'ShiftLeft': 'H', 'key': 'h'},
    {'keyCode': 73, 'code': 'KeyI', 'ShiftLeft': 'I', 'key': 'i'},
    {'keyCode': 74, 'code': 'KeyJ', 'ShiftLeft': 'J', 'key': 'j'},
    {'keyCode': 75, 'code': 'KeyK', 'ShiftLeft': 'K', 'key': 'k'},
    {'keyCode': 76, 'code': 'KeyL', 'ShiftLeft': 'L', 'key': 'l'},
    {'keyCode': 77, 'code': 'KeyM', 'ShiftLeft': 'M', 'key': 'm'},
    {'keyCode': 78, 'code': 'KeyN', 'ShiftLeft': 'N', 'key': 'n'},
    {'keyCode': 79, 'code': 'KeyO', 'ShiftLeft': 'O', 'key': 'o'},
    {'keyCode': 80, 'code': 'KeyP', 'ShiftLeft': 'P', 'key': 'p'},
    {'keyCode': 81, 'code': 'KeyQ', 'ShiftLeft': 'Q', 'key': 'q'},
    {'keyCode': 82, 'code': 'KeyR', 'ShiftLeft': 'R', 'key': 'r'},
    {'keyCode': 83, 'code': 'KeyS', 'ShiftLeft': 'S', 'key': 's'},
    {'keyCode': 84, 'code': 'KeyT', 'ShiftLeft': 'T', 'key': 't'},
    {'keyCode': 85, 'code': 'KeyU', 'ShiftLeft': 'U', 'key': 'u'},
    {'keyCode': 86, 'code': 'KeyV', 'ShiftLeft': 'V', 'key': 'v'},
    {'keyCode': 87, 'code': 'KeyW', 'ShiftLeft': 'W', 'key': 'w'},
    {'keyCode': 88, 'code': 'KeyX', 'ShiftLeft': 'X', 'key': 'x'},
    {'keyCode': 89, 'code': 'KeyY', 'ShiftLeft': 'Y', 'key': 'y'},
    {'keyCode': 90, 'code': 'KeyZ', 'ShiftLeft': 'Z', 'key': 'z'},
    {'keyCode': 190, 'code': 'Period', 'ShiftLeft': '>', 'key': '.'},
  ]
  return keyboard
};

 

 

 

 

더 많은 키보드 코드들을 알고 싶다면,

사이드 바에 공식문서 & 유용한 도구 목록을 확인해보세요!

 

 

웹크롤링-공식문서
웹크롤링 공식문서

 

그렇게 지정된 코드들을 아래의 특정 문자열을 받아와서 filter()를 적용해서 code, ShiftLeft를 가져옵니다

ShiftLeft가 값이 할당되면 대문자라는 뜻이므로 

대문자라면 keyboard.down(), keyboard.up()을 적용합니다

 

아래의 함수를 선언해서 handleWrite를 호출하는 것만으로 간단하게 적용할 수 있습니다

또 한, for(const id of idOfList)에서 비동기식 순차적으로 실행하게되어

글자를 한글자씩 입력하게됩니다

 

for (const id of idOfList) {
    const inputValue = await writeList.filter(element => element.ShiftLeft === id || element.key === id )
    if (inputValue.length > 0) {
      const code = inputValue[0].code
      const ShiftLeft = inputValue[0].ShiftLeft === id ? 'ShiftLeft' : ''
      if (ShiftLeft) {
        await page.keyboard.down(ShiftLeft);
        await setTime(selector, 500)
        await page.keyboard.press(code);
        await setTime(selector,500)
        await page.keyboard.up(ShiftLeft);
      } else {
        await page.keyboard.press(code);
      }
      await setTime(selector,500)
    }
}

 

 

 

이렇게 웹크롤링에서 키보드 코드를 입력해서 사용자 친화적으로 만들어서

봇으로 의심을 조금이라도 피하게하기위함입니다

웹크롤링의 포스팅 글이 점점 길어지고 있습니다

그래도 알게되는 것도 많으며,  앞으로 확실히 나아가는 것같습니다

그럼 다음 포스팅도 기대해주시고 

 

다음 웹 크롤링 관련 글로 찾아뵙겠습니다

 

대모험이 끝나기를 기대하며 화이팅!

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅에서 웹 크롤링 인피니트 스크롤링무엇인가 알아보았으며,

인피니트 스크롤링이 적용된 페이지에 대해서

이미지를 다운하고 파일로 만드는 방법을 알아보았습니다

 

인스타그램, 페이스북 여러 웹사이트에서 인피니트 스크롤링을 적용하고 있으니

꼭 알아두어야 할 것 같습니다

 

그럼 더 자세히 알고 싶으시면 아래의 링크를 확인해주세요

 

 

2022.08.27 - [IT_Web/Nodejs] - 웹 크롤링 인피니트 스크롤링 조작하여 이미지 다운로드하고 파일 만들기

 

웹 크롤링 인피니트 스크롤링 조작하여 이미지 다운받고 파일만들기

이전 포스팅에서 웹 크롤링을 하여 스크린샷을 찍는 방법에 대해 풀 스크린과 어느 특정 영역, 또는 화면 전체 이렇게 3가지 방법으로 스크린샷 찍는 방법을 알아보았습니다 웹크롤링할 때 화면

tantangerine.tistory.com

 

 

본론에 앞서 웹 크롤링을 하실 때는

본인이 책임져야 한다는 것을 잊지 마세요

합법적이지 못한 방법은 언제나 

꼭 책임이 따른다는 것을 명심하시길 바랍니다

 

그러니 회사 업무의 효율성을 위해서만 사용하시고

올바르게 사용하시길 바랍니다

 

그리고

웹 크롤링이 벌써 7번째(?) 포스팅인 것 같습니다

그래서 환경설정과 여러 라이브러리 설치 방법은 생략하고 진행하겠습니다

보고 싶으신 분이 계시다면 이전 포스팅에서 확인하시길 바랍니다

메뉴에 Node 쪽을 확인해보시면 관련 포스팅을 찾을 수 있습니다 

 

그럼 이제 본론으로 넘어가 보겠습니다

 

웹 크롤링 로그인, 로그아웃 및 waitForResponse(응답 대기) 알아보기 

 

아래의 코드를 보시면

약간 코드를 수정해서 값을 할당하는 함수와 웹 크롤링하는 함수를 불리하여

함수의 재사용을 높일 수 있게 변경하였습니다

 

그럼 아래에서 더 상세 한 설명을 이어나가겠습니다

기초적인 브라우저 환경설정 등은 주석으로 설명을 대신하겠습니다

로그인 로그아웃에 초점을 맞추겠습니다

 

const puppeteer = require('puppeteer');
const dotenv = require('dotenv'); // npm i dotenv
//const userAgent = require('new-user-agent')
dotenv.config();

const setCrawler = async () => {
  const browser = await puppeteer.launch({ // 웹 사이트의 브라우저를 띄우는 함수이다
    headless: false, // 웹크롤링할 때 사이트 노출 여부를 결정하는 값(운영을 하게될때 노출은 필요없다)
    args:['--window-size=1920,1080'] // 브라우저의 size를 설정한다
  });
  const page = await browser.newPage(); // 브라우저를 노출하고 새 탭보기로 페이지를 오픈한다
  await page.setViewport({ // 오픈할 때 가로 세로 길이를 설정한다
    width: 1080,
    height:1080,
  })

  //userAgent의 값을 랜덤으로 셋팅해주는 함수입니다 하지만 
  // 하지만 버전이 너무 낮아 현제 웹페이지에는 맞는게 없네요
  //await page.setUserAgent(userAgent.test());
  //console.log('userAgent', userAgent.test());

  const inputValue = async (selector, value) => {
    await page.evaluate((selector, value) => {
      document.querySelector(selector).value = value
    }, selector, value)
    await setTime(selector)
  }
  const buttonClick = async (selector, nextSelector, type) => {
    await page.evaluate((selector) => {
      document.querySelector(selector).click();
    }, selector)
    if(type === 'login'){
      const res = await page.waitForResponse(res => {
        return res.url().includes('graphql')
      })
      const result = await res.json()
      console.log('login', result);
      if(await result.extensions.is_final){
        await setTime(type)
        await page.keyboard.press('Escape')
      }
    } else if(type === 'logOut'){
      await page.waitForNavigation()
    } else {
      if(nextSelector){
        // waitForRequest(요청 대기), waitForResponse(응답 대기)
        await page.waitForSelector(nextSelector)
      }
      await setTime(selector)
    }
  }
  const setTime = async (selector = 'normal') => {
    const setTime = await page.evaluate(async() => {
      const timeList = await [3975, 4547, 5231, 6397, 7894, 3394, 4206, 5147, 6932, 7430, 3561, 4896, 5877, 6407, 7133];
      const randomTime = await timeList[Math.floor(Math.random() * timeList.length)]
      await new Promise(r => setTimeout(r, randomTime))
      return randomTime
    })
    console.log(`waitTime[${selector}]: `, setTime)
    return setTime
  } 
  return { buttonClick, inputValue, setTime, page: page, browser: browser }
}

// keyboard.press(); 인자값 목록 https://github.com/puppeteer/puppeteer/blob/v1.12.2/lib/USKeyboardLayout.js
// $('.i8zpp7h3')
// $('.i1n1lj7b.mmwt03ec')
// 

const crawler = async () => {
  try {
    const { buttonClick, inputValue, setTime, page, browser } = await setCrawler();
    const url = 'https://facebook.com';
    await page.goto(url);

    const id = await process.env.EMAILL;
    const password = await process.env.PASSWORD;
    
    // 로그인
    await inputValue('#email', id);
    await inputValue('#pass', password);
    await buttonClick('[name="login"]', '', 'login');
   

    // 로그아웃
    await buttonClick('.i8zpp7h3', '.i1n1lj7b.mmwt03ec');
    await buttonClick('.i1n1lj7b.mmwt03ec > div:last-child > div', '', 'logOut')
    // await page.close();
    // await browser.close();
  } catch (e) {
    console.log(e);
  }
}

crawler()

 


 

로그인 input 태그 찾기 및 로그인 하기

 

아래의 이미지를 보시면

<input> 태그 id, name속성 값이 email로 지정되어있습니다

 

 

웹크롤링-태그찾기
웹크롤링 태그 찾기

 

 

위에서 찾은 태그 이름인 #email, #pass를 함수 호출의 매개변수 첫 번째 인자 값으로 설정합니다

그리고 두 번째에는 로그인할 id와 password를 지정합니다

 

다음으로 선언된 inputValue 함수에 page.evaluate()를 호출하여 document.querySelector()로 태그에 접근합니다

이때 첫 번째 매개변수인 selector 인자 값을  document.querySelector(selector) 할당하여 호출합니다

그럼 태그를 가져오게 되고 그때 .value값을 새로 지정하거나 click()를 호출하여 버튼을 클릭하여

로그인을 시도하게 됩니다

 

 

여기서 잠깐!!

page.evalueate() 함수 안에서 document객체에 접근할 수 있다는 것 기억하시죠??

그리고 evaluate()에서 두 번째 매개변수부터 값을 할당해서 호출하면 

evaluate() 내부에서 사용이 가능하다는 것을 잊지 마세요

 

 

 

 

 

 

웹크롤링-로그인
웹크롤링 로그인 함수

 

 

evaluate() 함수에서 document객체를 활용해서 id, password를 할당하는 것과

puppeteer를 활용해서 하는 방법 두 가지가 있습니다

 

아래의 코드에서 page.type(), page.hover(), page.click() 3개의 함수를 확인할 수 있습니다

간단해 보이는 이 함수들은 그 사용법과 활용법이 다르며

언제 이 함수들이 버전업으로 활용방법이 변경될지 모르는 일입니다

그러니 대도록이면 함수를 선언하여 위와 같이 호출하는 방식으로 사용하시길 바랍니다

 

 

const crawler = async () => {
  try {
    const { buttonClick, inputValue, setTime, page, browser } = await setCrawler();
    await page.goto('https://facebook.com');

    const id = await process.env.EMAILL;
    const password = await process.env.PASSWORD;

    await page.type('#email', id);
    //await inputValue('#email', id);

    await page.type('#pass', password);
    //await inputValue('#pass', password);

    await page.hover('[name="login"]'); // 마우스를 선택자에 올리기
    await Promise.all([
      page.click('[name="login"]'),
      page.waitForNavigation(),
    ]);
    //await buttonClick('[name="login"]');
    await setTime()
    await page.keyboard.press('Escape');
        console.log('Escape')
    // await page.close();
    // await browser.close();
  } catch (e) {
    console.log(e);
  }
}

crawler()

 


 

waitForResponse 응답 대기 활용방법

 

 

로그인을 성공하면 아래와 같이 권한 페이지 설정을 하기 위해 dim처리가 되어 있습니다

dim처리를 없애보도록 하겠습니다

 

 

 

 

로그인을 클릭하게 되면 서버 호출을 시도하게 됩니다

그래서 page.waitForResponse()를 호출해서

웹 페이지에서 서버를 호출한 res.url()과 우리가 알고 있는 url주소를 비교하여

같으면 true값을 return 합니다

그리고 res값을 json()를 사용하여 Object형태로 받아올 수 있으며,

아래와 같이 접근이 가능합니다

 

 

 

웹크롤링 waitForResponse 함수 결과값

 

 

 

 

result.extensions.is_final에 접근을 하여 true일 경우

로그인 호출 후 페이지 랜더링 시간을 예방하기 위해 setTime()를 호출합니다

그리고 keyboard.press() 함수를 사용해서 Escape 즉 우리가 알고 있는 esc를 누르는 효과로 

dim처리를 해제할 수 있습니다 

 

사이드 바 공식문서 카테고리에 키보드를 누를 수 있는 인자 값으로 뭐가 있는지

링크를 걸어두도록 하겠습니다

 

 


 

 

로그인 아웃 태그 찾기 및 로그아웃하기

 

 

아래와 같이 빨간색 테두리를 클릭하게 되면 

class명을 확인할 수 있습니다

그럼 앞서 로그인 하기와 같이 선언된 함수를 사용해서 프로필 서브 창을 활성화합니다

 

 

 

웹크롤링-태그찾기2
웹크롤링 로그아웃 태그 찾기

 

 

 

태그를 찾기 위해서는 창에 태그를 확인해야 합니다

아래와 같이 불확실할 경우에는 특정 지역을 찾을 수밖에 없습니다

어차피 웹크롤링은 웹사이트 하나에 대해서 대응할 수밖에 없으니

아래와 같이 지정해 봅시다

서브 창에서 div를 찾아 전체 div태그를 찾습니다

그리고 > div:last-child > div를 해서 최하위 div태그를 찾아서 로그인 버튼을 클릭하게 합니다

 

 

 

 

웹크롤링-태그찾기3
웹크롤링 서브창 태그 찾기

 

 

 

서브 창이 활성화될 때까지 기다려서 그다음 로그아웃 메뉴를 클릭해야 합니다

 

그것을 구현하기 위해 서브 창을 활성화할 태그 첫 번째 인자 값과

활성화될 때까지 기다릴 두 번째 인자 값을 할당하여 호출합니다

 

 

그래서 클릭한 다음 아래와 같이 함수를 호출하여 

다음 태그를 기다리고 그다음 함수를 실행하게 됩니다

 

 

웹크롤링 서브창 활성화 대기

 

 

 

이렇게 웹 크롤링해서 로그인과 로그아웃 그리고 서버 응답 대기,

그리고 현재 페이지 내에서 클릭 이벤트 등을 이용하여 페이지가 변화될 때

기다리는 함수를 알아보았습니다

 

웹크롤링은 개발할 때 한눈에 확보여서 재밌기도 합니다

이런 것을 배워서 제가 하고자 하는 것에 더 좋은 시스템을 만들어서 활용해보도록 하겠습니다

 

이렇게 이번 포스팅은 마치도록 하겠습니다

오늘도 너무너무 고생했지만 그래도 보람이 있는 하루인 것 같습니다

 

이렇게 보람찬 생활을 한다면 언젠가는 이 대모험도 끝나지 않을까? 합니다

 

그럼 끝날 수 있다고 믿으면 파이팅합시다

그럼 다음 포스팅에 뵙겠습니다

 

 

 

 

 

 

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅에서 웹 크롤링을 하여 스크린샷을 찍는 방법에 대해

풀 스크린과 어느 특정 영역, 또는 화면 전체 이렇게 3가지 방법으로

스크린샷 찍는 방법을 알아보았습니다

 

웹크롤링할 때 화면 비율을 적용하는 등 여러가지를 알아보았습니다

더 자세히 알고 싶으시다면 아래 링크를 클릭해 주시길 바랍니다

 

그리고 이전 포스팅에서 설명한 세팅엑셀파일 쓰기, 읽기

자세한 설명은 생략할 수 있으니 참고 하시길 바랍니다

 

 

2022.08.26 - [IT_Web/Nodejs] - 웹 크롤링 퍼펫티어 활용하여 스크린샷 이미지 폴더에 저장하기

 

웹 크롤링 퍼펫티어 활용하여 스크린샷 이미지 폴더에 저장하기

이전 포스팅 웹크롤링 이미지 다운로드 적용하기 퍼펫티어를 활용해 웹 크롤링을 하여 이미지 다운로드를 적용해보았다 navigator.userAgent에 대한 속성 값도 변경해보고, 이미지를 웹에서 다운받

tantangerine.tistory.com

 

 


 

인피니트 스크롤링이란?

 

먼저 인피니트 스크롤링이란 무엇일까요?

이 효과는 페이스북, 인스타그램 등 많은 사이트에서 사용을 하고 있는 방법입니다

사용자가 클릭을 하지않고 컨텐츠에 계속 집중할 수 있도록

스크롤링만으로 사용자에게 컨텐츠가 무한 노출하여

사이트 이탈율을 방지하는 효과가 있습니다

 

그래서 조회하는 버튼형식이 아닌 스크롤을 조작해서

어느 위치에 오면 컨텐츠를 조회하는 것입니다

 

그래서 이제는 웹크롤링할 때 스크롤을 조작해서 

이미지를 다운받는 방법 구현하려합니다

 

하지만 인피니트 스크롤링을 구현할 경우에는

싱글 페이지 어플리케이션을 활용하는 웹 사이트가 대부분입니다

 

그리고 리액트를 사용했다면 클라이언트 사이드 랜더링을 사용하기 때문에

웹크롤링이 쉽지않습니다

 

그래서 이런 경우에는 puppeteer를 사용하면 웹크롤링이 수월해집니다

 

그리고 웹 크롤링하기 적당한 웹 사이트인지 판단하기위해 

postman을 사용하여 개발하기 이전에 어느정도 판단이 가능합니다

 

 

아래의 빨간영역을 보시면 웹크롤링할 사이트로 요청을 보내면

html코드, 그리고 웹 크롤링하려는 사이트의 미리보기가 가능합니다

그래서 이미지를 보거나 코드를 보면서 선택자 접근이 얼마나 가능한지도 판단해서

웹크롤링을 시작할 수 있습니다

 

웹크롤링-스크롤링이미지
웹 크롤링에 적합한 사이트 알아보기

 


 

 

 


 

스크롤조작 해서 이미지 다운받기

 

웹 스크롤 대상 웹 사이트는 unsplash.com 입니다

 

 

이미지 다운받기 이전에

태그에 접근 하는 방법을 먼저 알아보고 코드 분석에 들어가겠습니다

 

 

웹크롤링-스크롤링2
웹 스크롤링 태그 접근 미리하기

 

지금 현재 위에서 보시면 두개의 태그가 있습니다

<div class="GIFah"> <img class="YV19w"> 두 개의 태그를 볼 수 있습니다

그래서 접근이 가능한지 아니면 필요없는 태그가 붙어서 오는지 선택자를 사용해서 미리 태그에 접근을 해봅니다

아래의 이미지와 같이 태그 접근이 가능한 것을 확인할 수 있습니다

 

 

웹크롤링-스크롤3
선택자로 태그 접근하기

 

 

 

 

그럼 이제 아래의 코드를 천천히 분석해보시길 바랍니다

 

아래에 코드를 보시면

브라우저와 페이지 영역을 설정하여 해상도를 높입니다

그리고 querySelectorALL을 활용해서 이미지의 부모 엘리먼트를 태그를 가져옵니다

그리고 태그로 forEach를 사용하여 자식 img태그에 접근하여

src에 접근하여 이미지 주소를 가져와 이미지 다운받는 것까지 구현하였습니다

 

상세한 설명은 아래에서 하겠습니다

 

const axios = require('axios')
const fs = require('fs');
const puppeteer = require('puppeteer');

fs.readdir('imges', (err) => {
  if(err) {
    console.log('imges 폴더가 없어 imges 폴더를 생성합니다.')
    fs.mkdirSync('imges')
  }
})

const crawler = async () => {
  try {
  const browser = await puppeteer.launch({
      headless: false,
      args:['--window-size=1920,1080']
    });
  const page = await browser.newPage();
  await page.setViewport({
    width: 1920,
    height:1080,
  });
  await page.goto('https://unsplash.com')
  let result = []
  while(result.length < 10) {
    const srcs = await page.evaluate(() => {
      window.scrollBy(0, 0)
      const imgs = []
      const imgEls = document.querySelectorAll('.GIFah')
      if (imgEls.length) {
        imgEls.forEach((y, i) => {
          let src = y.querySelector('img.YVj9w').src
          if(i < 10){
            if(src){
                imgs.push(src)
            }
            y.parentElement.removeChild(y);
          }
        });
      }
      window.scrollBy(0, 30)
      return imgs
    });
    result = result.concat(srcs)
    await page.waitForSelector('.GIFah')
    console.log('result', result.length, result)
  }
  result.forEach(async(imgUrl, i) => {
    if (imgUrl) {
      const imgInfo = await axios.get(imgUrl.replace(/\?.*$/,''), { 
        responseType: 'arraybuffer',
      })
      fs.writeFileSync(`imges/${new Date().valueOf()}.jpeg`, imgInfo.data)
    }
  })
  await page.close();
  await browser.close();
  } catch (e) {
    console.error(e)
  }
}

crawler();

 

아래의 같이 해상도를 크게 설정합니다

스크롤을 조작하는데 너무 해상도가 작으면

스크롤 위치가 너무 많이 이동을 하게되면

스크롤링으로 다음 컨텐츠 조회가 잘 되지않는 현상이 있으니 참고하시길 바랍니다

 

웹크롤링-스크롤링4
웹크롤링 화면 비율 조정하기

 

 

아래에서 선택자를 사용해서 태그에 접근합니다

그리고 그 태그로 이미지태그를 접근하여 이미지를 다운받습니다

그리고 중요한것은 현재 가지고 있는 태그의 부모 태그에 접근해서 모든 하위 태그를 삭제합니다

그렇게 되면 처음 가져온 image 주소를 저장하고나서 필요없게 된 태그를 삭제하여

다음 스크롤링 해서 가져온 이미지 관련 태그만 접근할 수 있다는 장점이 있습니다

 

 

웹크롤링-스크롤5
태그 접속해서 삭제및 이미지 주소가져오기

 

while문으로 특정 조건이 만족할 때까지 무한 루프가 실행됩니다

그리고 concat을 해서 가져온 이미지 주소를 

최종 결과 변수에 저장을 합니다

 

 

 

웹크롤링-스크롤링6
특정 조건을 허용하기 전까지 무한루프 돌기

 

 

 

그리고 forEach로 파일을 만들어서 확인할 수있습니다

window.scrollBy()로 스크롤릉 내리는 효과를 주어 다음 컨텐츠를 조회하게 합니다

그리고 최종 결과 변수 값에 배열이 계속저장되어

결국에는 특정조건이 맞지 않게되면

무한 루프에서 나와 파일을 만들고

브라우저 및 페이지를 종료합니다

 

 

오늘은 여기까지입니다

웹 크롤링을 사이드 프로젝트를 하게되면서 여러가지 응용할 수 있을것같습니다

그래서 조금 기대도 되고 다음에 사이드 프로젝트를 하는것을

포스팅해서 올리겠습니다

 

이렇게 공부를 하면서 사이드 프로젝트를 만들어가는 과정에

흥미를 붙여서 계속 공부하려는 의지를 붙여주는게 정말 중요한 것같습니다

 

우리는 아직 너무많은 이야기들이 남았으니까요

솔직히 개발자가들의 공부가 어디가 끝인지도 잘모르겠네요

그러니 흥미 잃지마시고 끝까지 파이팅 하시길바랍니다

 

우리 IT 대모험은 끝나지 않았으니까요!!

 

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅 웹크롤링 이미지 다운로드 적용하기

퍼펫티어를 활용해 웹 크롤링을 하여 이미지 다운로드를 적용해보았다

navigator.userAgent에 대한 속성 값도 변경해보고,

이미지를 웹에서 다운받아 드라이버에 저장하는 것과

받아온 이미지 링크 주소를 엑셀에도 넣어보았습니다

 

이번 포스팅도 이어지는 부분이 있으니 확인하시길 바랍니다

그리고 조금 더 상세한 부분은 아래의 링크를 확인해주세요

 

 

2022.08.24 - [IT_Web/Nodejs] - 웹크롤링 이미지 다운로드 및 puppeteer 활용해 이미지 주소 엑셀에 입력하기

 

웹크롤링 이미지 다운로드 및 puppeteer 활용해 이미지 주소 엑셀에 입력하기

이전 포스팅에서 웹크롤링 Puppeteer 및 Promise 활용 방법 Node.js로 웹 크롤링할 경우 Puppeteer를 사용하면 정말 편하게 개발을 할 수 있습니다 evaluate() 함수를 활용하여 잠시 대기시간을 갖거나, 선택

tantangerine.tistory.com

 


 

Puppeteer 활용해서 웹 사이트 스크린샷 이미지 저장하기

 

웹 크롤링을 하기위한 세팅은 이전 포스팅에서부터 이어지고 있으니

관심 있으신 분은 #웹 크롤링 태그를 클릭해서 확인해주시길 바랍니다

 

우선 아래의 코드를 작성합니다

 

launch(), setViewport(), page.screenshot()

함수에 대해서 알아보겠습니다

 

const fs = require('fs');
const puppeteer = require('puppeteer');

const xlsx = require('xlsx');
const workbook = xlsx.readFile('xlsx/data.xlsx');
const ws = workbook.Sheets.Sheet1;
const xlsxRecords = xlsx.utils.sheet_to_json(ws)

fs.readdir('screenShot', (err) => {
  if(err) {
    console.log('screenShot 폴더가 없어 screenShot 폴더를 생성합니다.')
    fs.mkdirSync('screenShot') 
  }
})

const screenshotCrawler = async () => {
  try {
    const browser = await puppeteer.launch({
      headless: false,
      args:['--window-size=1920,1080']
    });
    console.log('첫번째: records', xlsxRecords)
    const page = await browser.newPage();
    await page.setViewport({
      width: 1920,
      height:1080,
    });
    await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36')
    for(const [i, r] of xlsxRecords.entries()) {
      await page.goto(r.링크);
      await page.screenshot({ 
        path: `screenShot/${r.제목}.png`, // 저장할 위치를 지정한다
        fullPage: false, // 웹 페이지의 스크롤의 밑 전체 페이지를 스크린샷할 수 있다
        clip: {
          x: 100,
          y: 100,
          width: 300,
          height: 300,
        }// 왼쪽 상단 모서리 (x, y), 너비(width), 높이(height)
      });// 그리고 fullPage와 clip은 같이 사용할 수 없다
      
      const setTime = await page.evaluate(async() => {
        const timeList = await [3975, 4547, 5231, 6397, 7894, 3394, 4206, 5147, 6932, 7430, 3561, 4896, 5877, 6407, 7133];
        const randomTime = await timeList[Math.floor(Math.random() * timeList.length)]
        await new Promise(r => setTimeout(r, randomTime))
        return randomTime
      })
      console.log('setTime', setTime)
    }
    await page.close();
    await browser.close();
  } catch (e) {
    console.error(e)
  }
}

screenshotCrawler();

 

 

우선 초기에 해상도를 적용하지 않을 경우

아래와 같이 브라우저는 큰 화면이 아닌 반 정도인 것을 확인할 수 있습니다

 

 

웹크롤링-스크린샷2
초기 브라우저 설정

 

 

아래의 코드에 보면 기존 코드에 args:['--window-size=1920,1080']를 추가하면 됩니다

그리고 1920, 1080은 자신의 모니터 해상도를 생각하시고 지정합시다

 

 

웹크롤링-스크린샷3
브라우저 해상도 지정하기

 

 

위의 코드를 적용하면 브라우저의 크기가 커진것을 확인할 수 있습니다

이제는 화면의 해상도를 키우면 브라우저만큼 크게 넓어질 것 같습니다

 

 

웹크롤링-스크린샷4
args 값 적용 화면

 

 

아래의 코드 setViewport() 함수를 적용합니다 

브라우저 해상도와 맞게 값을 지정합니다

 

 

웹크롤링-스크린샷5
화면 해상도 지정하기

 

 

아래와 같이 화면이 해상도에 맞게 커지게 되었습니다

이제는 스크린샷을 찍는 코드를 적용하도록 하겠습니다

 

 

웹크롤링-스크린샷6
setViewport() 적용 화면

 

 

 

 

 

 

아래의 코드를 확인해봅시다

clip에 값을 지정하면 어느 한 영역을 스크린샷하여 찍을 수 있습니다

그 영역은 지정된 웹 페이지의 x, y축에서

width, height를 지정하여 사각형 영역을 만들어 스크린샷을 찍습니다

 

 

 

웹크롤링-스크린샷7
스크린샷 적용 코드

 

위와 같이 코드를 적용하면 아래와 같이 

스크린샷을 찍게 되어 path위치에 저장하게 됩니다

 

 

웹크롤링-스크린샷8
스크린샷 clip 값 적용 화면

 

 

아래의 전체 화면에 clip영역이 어떻게 지정되는지 확인이 가능합니다

이제는 fullPagetrue값을 주면 어떻게 스크린샷을 찍는지 확인해보겠습니다

 

 

웹크롤링-스크린샷9
clip 값 영역 지정

 

 

fullPage값을 true로 지정하게 되면 아래와 같이 전체 화면을 찍게 됩니다

웹 페이지 전체를 찍는 것이니 유의해 주시길 바랍니다

그리고 한 가지 더 fullPage 값이 true일 경우 clip 값을 지정하면

에러가 발생하니 주의하시길 바랍니다

 

 

웹크롤링-스크린샷10
fullPage값이 true로 지정된 스크린샷 이미지

 

 

이렇게 웹크롤링에서 스크린샷을 찍어 보았습니다

간단하게 함수 3개로 구현하였습니다

 

웹크롤링 기능은 아직 많이 남았으니

조금만 참고 포스팅을 기대해주시고 

힘내시길 바랍니다

 

아직 IT 대모험은 끝나지 않았으니까요

그럼 파이팅 하시고

 

다음 포스팅에서 뵙겠습니다

 

728x90
반응형
728x90
반응형

이전 포스팅에서 웹크롤링 Puppeteer 및 Promise 활용 방법

 

Node.js로 웹 크롤링할 경우 Puppeteer를 사용하면 정말 편하게 개발을 할 수 있습니다

evaluate() 함수를 활용하여 잠시 대기시간을 갖거나,

선택자를 지정하여 태그에 대해 접근하여 값을 가져오거나 등 

여러 가지 작업을 간편하게 하였습니다

 

더 자세한 사항을 알고 싶으시다면 아래의 링크를 확인해주세요

 

 

2022.08.20 - [IT_Web/Nodejs] - nodejs 웹 크롤링을 Puppeteer 및 Promise 활용 완벽 파헤치고 CSV 파일 쓰기

 

nodejs 웹 크롤링을 Puppeteer 및 Promise 활용 완벽 파헤치고 CSV파일 쓰기

이전 포스팅에 대하여 이전 포스팅에서는 간단하게 웹 크롤링을 하는 방법을 알아보았습니다 axios와 cheerio를 사용해서 웹 크롤링을 해보았습니다 하지만 axios는 한계가 있습니다 싱글 페이지로

tantangerine.tistory.com

 

 

웹크롤링 이미지 다운로드 및 이미지 주소 엑셀에 입력하기

 

 

우선 이전 포스팅에서 계속 이어져서 작업을 진행하고있으니

이전 포스팅을 꼭 확인해주세요

 

아래의 코드를 한번 천천히 살펴보시길 바랍니다

그리고 세부 설명은 아래에서 이미지와 같이 진행하겠습니다

 

const axios = require('axios')
const fs = require('fs');
const puppeteer = require('puppeteer');

const xlsx = require('xlsx');
const add_to_sheet = require('./add_to_sheet');
const workbook = xlsx.readFile('xlsx/data.xlsx');
const ws = workbook.Sheets.Sheet1;
const xlsxRecords = xlsx.utils.sheet_to_json(ws)


fs.readdir('screenShot', (err) => { // readdir는 비동기로 이벤트 루프 속에서 작동하기 때문에 밑에 함수와는 관계가 없다
  if(err) {
    console.log('screenShot 폴더가 없어 screenShot 폴더를 생성합니다.')
    fs.mkdirSync('screenShot') // 프로그램이 처음 시작될때 한번만 시작할 때는 Sync는 사용해도 된다
  }
})


fs.readdir('poster', (err) => {
  if(err) {
    console.log('poster 폴더가 없어 poster 폴더를 생성합니다.')
    fs.mkdirSync('poster')
  }
})


const crawler = async () => {
  try {
    const browser = await puppeteer.launch({headless: false});
    console.log('첫번째: records', xlsxRecords)
    const page = await browser.newPage();
    await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36')
    add_to_sheet(ws, 'C1', 's', '평점')
    add_to_sheet(ws, 'D1', 's', '이미지 주소')
    for(const [i, r] of xlsxRecords.entries()) {
      await page.goto(r.링크);
      console.log('userAgent', await page.evaluate('navigator.userAgent'))
      const selectedTag = await page.evaluate(async () => {
        let score = ''
        let imgUrl = ''
        const scoreEl = await document.querySelector('.score.score_left .star_score')
        const imgEl = await document.querySelector('.poster img')
        if (scoreEl) {
          score = scoreEl.textContent;
        }
        if (imgEl) {
          imgUrl = imgEl.src;
        }
        return { score, imgUrl } 
      })

      if (!selectedTag?.score) {
        console.log('두번째 selectedTag 1: ',r.제목, `${i+1} 번째 영화 평점`, parseFloat(selectedTag.score.trim()));
        const newCell = 'C' + (i + 2)
        add_to_sheet(ws, newCell, 'n', parseFloat(selectedTag.score.trim()))
      } else {
        const scoreEl = await page.$('.score.score_left .star_score');
        if (scoreEl) {
          const selectedTag = await page.evaluate(tag => tag.textContent, scoreEl);
          if (selectedTag) {
            console.log('두번째 score 2: ', parseFloat(selectedTag.trim(), `${i+1} 번째 영화 평점`, parseFloat(selectedTag.trim())))
            const newCell = 'C' + (i + 2)
            add_to_sheet(ws, newCell, 'n', parseFloat(selectedTag.trim()))
          }
        }
      }
      // 이미지 주소로 요청을 받아서 이미지 데이터를 받는다
      // 이미지 데이터는 버퍼로 받는다 이buffer가 연속적으로 들어있는 자료구조가 arraybuffer라고 한다
      // 이미지 주소를 가져올때는 확인을 필히 해야하며 어떠한 규칙을 가지고 있는지 확인해야 한다 쿼리스트링을 없애는것이 답이 아니다
      if (!selectedTag?.imgUrl) {
        const imgInfo = await axios.get(selectedTag.imgUrl.replace(/\?.*$/,''), { // 정규표현식으로 쿼리스트링을 삭제한다
          responseType: 'arraybuffer',
        })
        fs.writeFileSync(`poster/${r.제목}.jpg`, imgInfo.data)
        console.log('두번째 imgUrl 2: ', selectedTag, `${i+1} 번째 영화 이미지 주소`, selectedTag);
        const newCell = 'D' + (i + 2)
        add_to_sheet(ws, newCell, 's', selectedTag)
      } else {
        const imgEl = await page.$('.poster img');
        if (imgEl) {
          const selectedTag = await page.evaluate(tag => tag.src, imgEl);
         
          if (selectedTag) {
            console.log('두번째 imgUrl 2: ', selectedTag, `${i+1} 번째 영화이미지 주소`, selectedTag);
            const newCell = 'D' + (i + 2)
            add_to_sheet(ws, newCell, 's', selectedTag)
            const imgInfo = await axios.get(selectedTag.replace(/\?.*$/,''), {
              responseType: 'arraybuffer',
            })
            fs.writeFileSync(`poster/${r.제목}.jpg`, imgInfo.data)
          }
        }
      }
      
      const setTime = await page.evaluate(async() => {
        const timeList = await [3975, 4547, 5231, 6397, 7894, 3394, 4206, 5147, 6932, 7430, 3561, 4896, 5877, 6407, 7133];
        const randomTime = await timeList[Math.floor(Math.random() * timeList.length)]
        await new Promise(r => setTimeout(r, randomTime))
        return randomTime
      })
      console.log('setTime', setTime)
    }
    await page.close();
    await browser.close();
    xlsx.writeFile(workbook, 'xlsx/xlsxRsult.xlsx')
  } catch (e) {
    console.error(e)
  }
}

crawler();

 

 

라이브러리를 우선 불러옵니다

세팅방법은 이전 포스팅을 참고해주시길 바랍니다

 

여러 개가 있으니 한번 확인해보세요!

 

라이브러리 불러오는 것은 그렇게 어려운 것이 없으니

설명은 생략하겠습니다

 

workbook.Sheets.Sheet1의 의미는 

제 엑셀 파일의 Sheet의 명이 Sheet1입니다

그래서 Sheet1의 데이터를 받아오게 됩니다

 

라이브러리-불러오기
필요 라이브러리 불러오기

 

 

 

 

 

이미지를 저장할 폴더가 있는지 없는지 체크해서 폴더를 생성합니다

fs.readdir은 비동기로 이벤트 루프 속에서 작동하기 때문에 밑에 crawler함수와는 관계가 없습니다

 

또 한, fs함수 중 Sync를 사용하면 안 된다고 알고 계시겠지만

프로그램이 처음 시작될 때 한 번만 실행할 함수라면 Sncy함수를 사용해도 무관합니다

 

 

 

폴더생성
폴더 생성

 

아래의 코드처럼 puppeteerlaunch()로 실행 준비를 하고

newPage()로 브라우저를 띄우게 됩니다

이때 setUserAgent()를 사용해서 나의 Agent정보를 지정할 수 있습니다

이 정보 값은 navigator.userAgent를 개발자 모드에서 콘솔 창에 적게 되면 노출되니 참고하세요

 

wssheet1의 데이터를 가져오며 형식이 들어있기 때문에

add_to_sheet()를 사용해서

엑셀 파일의 평점과, 이미지 주소를 먼저 적습니다

 

 

 

add_to_sheet()의 코드는 아래와 같습니다

설명은 생략하며 한번 분석해보시길 바랍니다!!

 

 

const xlsx = require('xlsx');

function range_add_cell(range, cell) {
  var rng = xlsx.utils.decode_range(range);
  var c = typeof cell === 'string' ? xlsx.utils.decode_cell(cell) : cell;
  if (rng.s.r > c.r) rng.s.r = c.r;
  if (rng.s.c > c.c) rng.s.c = c.c;

  if (rng.e.r < c.r) rng.e.r = c.r;
  if (rng.e.c < c.c) rng.e.c = c.c;
  return xlsx.utils.encode_range(rng);
}

module.exports = function add_to_sheet(sheet, cell, type, raw) {
  sheet['!ref'] = range_add_cell(sheet['!ref'], cell);
  sheet[cell] = { t: type, v: raw };
};

 

 

page.goto로 페이지 이동이 가능합니다

그리고 태그 접근을 하려면 두 가지 방법이 있습니다

우선 첫 번째 방법은 evaluate()에서 document접근이 가능하기에

querySelector()를 사용해서 태그에 접근해서 값을 가져옵니다

그 값들은 비구조화 할당으로 한 번에 객체로 return 합니다

 

 

evaluate함수-활용법
evaluate 함수의 활용법

 

위에서 받아온 값에서 score값이 있으면

바로 엑셀에 값을 적용합니다

두 번째 매개변수는 엑셀의 어디 부분에 값을 넣을지 지정하는 것입니다

세 번째 매개변수는 데이터의 자료형을 지정하는 것입니다

그래서 저는 C2부터 시작하며, 'n' number를 넣겠다고 지정한 것입니다

 

그리고 score값이 없으면

다시 두 번째 방법으로 받는 것을 적용해보았습니다

이 방법은 page.$() 활용해서 태그에 접근하는 element 값을 가져옵니다

 

그다음 evaluate() 함수를 사용해서 값을 가져와 할당합니다

 

 

 

태그-접근방법
두번째 태그 접근방법

 

 

이미지를 받기 위해서는 우선 axios.get으로 이미지 주소를 값으로 요청을 합니다

responseTypearraybuffer로 설정합니다

이미지 주소로 요청받은 데이터는 버퍼이며,

이 버퍼가 연속적으로 들어가 있는 자료구조가 arraybuffer입니다

 

그리고 엑셀에 이미지 링크 주소를 입력하고

이미지를 다운로드하여. jpg로 내 컴퓨터의 poster폴더에 저장합니다

 

 

 

이미지받기
이미지 받기

 

크롤링 로봇으로 보이지 않기 위해

여러 가지 타임을 배열로 넣어 setTime을 실행합니다

그래서 다음 페이지 작업의 시간 간격을 두어 진행하게 됩니다

 

 

잠시대기시긴설정
로봇으로 보이지않게 하기위한 함수

 

 

마지막 브라우저를 닫고 

엑셀 파일에 데이터를 넣은 정보 값을 할당하여

파일로 만들어서 종료합니다

 

엑셀파일생성
엑셀 파일 생성

 

 

콘솔 창에 노출된 여러 정보 값을 확인할 수 있으며

진행상황에서 확인된 setTimescore값 등을 확인할 수 있습니다

 

 

콘솔창-노출
콘솔창 정보값 노출화면

 

 

아래와 같이 파일 구조와

poster폴더에 jpg 파일이 생성된 것이 확인할 수 있습니다

 

 

이미지다운완료결과
이미지 생성된 파일들

 

 

엑셀 파일에 입력된 이미지 주소를 확인할 수 있습니다

 

 

 

이렇게 엑셀 쓰기와 웹 크롤링 작업을 알아보았습니다

여러 작업이 동반된 개발이라 개인적인 프로젝트를 해도 유용하다고 생각합니다

이것으로 무엇을 해볼까 생각 중입니다

아무튼 이렇게 공부를 끝내겠습니다

 

앞으로도 계속 공부를 해야 하는 입장에서 힘들겠지만

파이팅 하시길 바랍니다

 

그럼 다음 포스팅도 많은 관심 부탁드립니다

728x90
반응형
728x90
반응형

이전 포스팅에 대하여

 

이전 포스팅에서는 간단하게 웹 크롤링을 하는 방법을 알아보았습니다

axioscheerio를 사용해서 웹 크롤링을 해보았습니다

하지만 axios는 한계가 있습니다

싱글 페이지로 만들어진 웹페이지는 axios로 정보를 받아올 수 없다는 점이

그 한계입니다 그래서 좀 더 강력한 라이브러리를 사용하여 웹크롤링을 하려 합니다

 

그래도 cheerio는 간단한 웹페이지를 크롤링할 때에는 

라이브러리가 가볍기 때문에 상황에 맞게 사용한다면 좋을 듯합니다

 

더 자세한 사항은 아래의 링크를 클릭해주시길 바랍니다

 

 

2022.08.18 - [IT_Web/Nodejs] - 웹 크롤링 axios 및 cheerio 활용해서 간단하게 적용해보기

 

웹 크롤링 axios 및 cheerio 활용해서 간단하게 적용해보기

이전 포스팅에서는 엑셀과 CSV 파일에 저장되어 있는 데이터를 불러오는 것을 알아보았습니다 다른 타 부서나 프로젝트진행에 필요한 파일들이 저장된 데이터를 가져오기 위해 엑셀 및 CSV 파일

tantangerine.tistory.com

 

 

지금 웹 크롤링 관련 포스팅이 이어지다 보니 

기존 소스코드를 사용하는 경우가 많으니 이전 포스팅에서 

어떻게 진행되었는지 꼭 확인하시고 오시길 바랍니다

 

그럼 CSV 파일의 정보를 가져와서

웹크롤링을 하도록 하겠습니다

CSV 파일에는 영화 제목과 url 링크 주소가 함께 있습니다

 

파일 구조와 처음 세팅이 궁금하시다면

아래의 링크를 확인해주세요

 

2022.08.17 - [IT_Web/Nodejs] - node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

 

node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

node.js 관련은 처음 소개하는 것 같습니다 다음에 node.js로 서버 구축하는 것도 알아볼 테니 많은 관심 부탁드립니다 우선 node 다운로드 링크 주소는 사이드바에 생성해 놓을 테니 확인해 보시길

tantangerine.tistory.com

 


 

Puppeteer 및 Promise 활용해서 웹 크롤링하기 

 

 

puppeteer의 공식문서는 아래의 링크를 확인해보세요!!

https://github.com/puppeteer/puppeteer/blob/v16.2.0/docs

 

GitHub - puppeteer/puppeteer: Headless Chrome Node.js API

Headless Chrome Node.js API. Contribute to puppeteer/puppeteer development by creating an account on GitHub.

github.com


 

이제 본론으로 들어가서

웹 크롤링을 시작해 봅시다

먼저 아래와 같이 라이브러리를 설치합니다

 

 

npm i puppeteer

 

 

 

아래와 같이 코드를 작성합니다

새로운 함수들과 부가적인 설명이 필요할 것 같습니다

 

 

const puppeteer = require('puppeteer');

const crawler = async () => {
    const browser = await puppeteer.launch({headless: false}); // 웹브라우저 노출여부
    const page1 = await browser.newPage() // 페이지 노출하기
    const page2 = await browser.newPage() // 탭으로 페이지가 여러개 노출할 수 있습니다
    const page3 = await browser.newPage()

    await page1.goto('https://naver.com'), //페이지 이동
    await page2.goto('https://google.com'),
    await page3.goto('https://www.daum.net')


    await page1.evaluate(async() => { // 그리고 잠시대기
      await new Promise(r => setTimeout(r, 3000))
    }),
    await page2.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    await page3.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    })

    await page1.goto('https://n.news.naver.com/mnews/ranking/article/437/0000310583?ntype=RANKING&sid=001'),
    await page2.goto('https://newslibrary.naver.com/search/searchByDate.naver'),
    await page3.goto('https://google.com')

    await page1.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    await page2.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    await page3.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    })
    
    await page1.close(), // 탭 페이지 닫기
    await page2.close(),
    await page3.close()
  	await browser.close(); // 브라우저 닫기
}

crawler();

 

 

 

웹 크롤링일반적으로 비동기를 사용해야 합니다

그 이유는 브라우저를 띄우고 로그인을 하고 그런 일괄된 작업들이 진행되면서 순차적으로 작업을 

진행해야 작업이 꼬이지 않기 때문입니다

 

예로 브라우저를 띄우고 로그인을 할 경우 브라우저가 노출이 되지도 않았는데

로그인을 하려고 한다면

에러를 발생하기 때문입니다

 

그래서 puppeteer를 사용하면서 얼마나 비동기를 자유자재로 사용하는지가

가중 중요한 핵심이라고 할 수 있습니다

 

이제 코드적인 설명을 해보겠습니다

아래와 같이 launch() 함수에 headless: false가 적용되어있습니다

웹브라우저를 노출해서 웹 크롤링하는 작업을 볼 수 있습니다

 

개발 초기에는 false로 적용해서 개발을 하면서

수정사항을 고쳐나갈 것입니다

 

웹크롤링-웹페이지-노출
웹크롤링을 할 경우 웹페이지 노출에 대한 여부

 

실제 프로젝트를 진행한다면 false값에

아래와 같이 적용해야 할 것입니다

그럼 env의 상태로 개발, 운영을 구분하여 웹페이지를 노출하게 될 것입니다

 

환경설정활용-웹브라우저노출여부
환경설정 값을 활용해서 웹브라우저 노출여부를 지정한다

 

 

그리고 페이지를 이동하고 나서

잠시 대기하는 시간을 갖습니다

예전에 puppeteer.waitFor을 사용하였습니다

하지만 버전이 올라가면서 해당 함수가 삭제되어서

아래와 같이 구현할 수 있습니다

 

태그에 접근할 정보가 있다면

evaluate()함수는 tagvalue값에 접근이 가능합니다

하지만 콜백함수를 활용해서 비동기로 setTimeout함수를 사용해서

함수 실행을 딜레이 합니다

 

그리고 잠시 대기하는 시간이 왜 필요한지도 궁금하실 수 있습니다

그 이유는 자동으로 웹크롤링을 진행하게 되면

속도가 엄청 빠르다는 것입니다

그럼 웹페이지를 운영하는 입장에서는

비정상적인 동작이라고 생각하기 때문에

우리에게 제재를 줄 수 있습니다

 

비정상적인 방법이라는 인식을 피하기 위함입니다

 

웹크롤링-잠시대기
웹크롤링 잠시 대기

 

 

이렇게 간단한 함수들을 정리해보았습니다

하지만 지금 이 방법은 순차적으로 진행하다 보니

소 시간이 소요됩니다

만약 더 많은 작업을 한다면 소요되는 시간은 더 클 것이라고 생각합니다

 

그래서 이제 프로미스를 적용해서 한방에 요청하고

순차적으로 가 아닌 먼저 도착한 순서대로 진행하여

작업 시간을 단축해보도록 하겠습니다


 

반응형

 


 

Promise.all을 활용해서 웹크롤링 시간 단축 하기

 

아래와 같이 코드를 변경합니다

Promise.all을 사용한 것이 보이시나요?

 

함수마다 Promise.all을 적용해서 동시 작업을 진행합니다

이때 비동기이지만 한 번에 작업할 때마다 동시에 한다는 점입니다

그래서 전체적으로 볼 때는 비동기라는 것을 인지해 주세요 

 

부가적인 설명은 필요가 없을 것 같습니다

 

그럼 이제는 CSV 파일을 읽어서 그 정보를 바탕으로 웹 크롤링을 해보겠습니다

 

const puppeteer = require('puppeteer');

const crawler = async () => {
  const browser = await puppeteer.launch({headless: process.env.NODE_ENV === 'production'});
  const [page1, page2, page3] = await Promise.all([
    browser.newPage(),
    browser.newPage(),
    browser.newPage()
  ])
  await Promise.all([
    page1.goto('https://naver.com'),
    page2.goto('https://google.com'),
    page3.goto('https://www.daum.net')
  ]);
  await Promise.all([
    page1.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    page2.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    page3.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    })
  ]);
  await Promise.all([
    page1.goto('https://n.news.naver.com/mnews/ranking/article/437/0000310583?ntype=RANKING&sid=001'),
    page2.goto('https://newslibrary.naver.com/search/searchByDate.naver'),
    page3.goto('https://google.com')
  ]);
  await Promise.all([
    page1.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    page2.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    }),
    page3.evaluate(async() => {
      await new Promise(r => setTimeout(r, 3000))
    })
  ]);
  await Promise.all([
    page1.close(),
    page2.close(),
    page3.close()
  ]);
  await browser.close();
}

crawler();

 


 

CSV 파일을 읽어서 웹크롤링 시작하기

 

CSV 파일을 읽기 위해서는 라이브러리가 필요합니다

그리고 초기 세팅이 필요하겠지요

그것은 위에 링크를 걸어 두었던 것 기억하시지요?

그것을 참고하시길 바랍니다

 

아래와 같이 코드를 작성합니다

 

 

const parse = require('csv-parse/lib/sync');
const stringify = require('csv-stringify/lib/sync') 
const fs = require('fs');
const puppeteer = require('puppeteer');

const csv = fs.readFileSync('csv/data.csv');
const records = parse(csv.toString('utf-8'))

const crawler = async () => {
  const resultScore = []
  try {
    const browser = await puppeteer.launch({headless: false});
    console.log('첫번째: records', records)
    // Promise.all을 활용해서 csv에서 받아온 records를 모든 배열의 정보를 동시에작업을 진행합니다
    await Promise.all(records.map(async (r, i) => {
        const page = await browser.newPage();
        await page.goto(r[1]);
        const scoreEl = await page.$('.score.score_left .star_score');
        if (scoreEl) {
          const text = await page.evaluate(tag => tag.textContent, scoreEl);
          if (text) {
            console.log('두번째: ', r[0], '평점', text.trim());
            resultScore.push([r[0], r[1], text.trim()])
            // resultScore[i] = [r[0], r[1], text.trim()]
          }
        }
        await page.evaluate(async() => {
          await new Promise(r => setTimeout(r, 3000))
        })
        await page.close();
    }));
      await browser.close();
      const str = stringify(resultScore);
      console.log('3번째: ', str)
      fs.writeFileSync('csv/result.csv', str);
  } catch (e) {
    console.error(e)
  }
}


crawler();

 

 

위의 코드에서

Promise.all을 사용해서 recodes.map을 매개변수 사용해서

한 번에 작업을 진행합니다

그리고 콘솔 창에는 아래와 같이 확인되었습니다

그래서 링크 주소를 활용해서 웹 크롤링을 시작합니다

 

콘솔데이터정보
콘솔 데이터 정보 노출 상황

 

 

 

개발자 모드에서 아래와 같이 클래스명을 확인해서

데이터 값을 가져올 수 있는 위치를 확인합니다

 

 

개발자모드-선택자찾기
개발자모드 선택자찾기

 

그래서 아래와 같이 클래스명을 기입해서

그 해당하는 textContent를 가져옵니다

이때 필요한 함수는 page.evaluate를 활용합니다

 tag.textContentscoreElreturn 받아

 

text 변수에 값이 있으면 push를 해서 최종 값을 저장합니다 

 

 

데이터-가져오기
데이터 가져오기

 

 

 

그리고 웹 크롤링해온 평점을 다시 CSV 파일에 넣어보도록 하겠습니다

먼저 라이브러리를 설치합니다

 

 

npm i csv-stringify

 

 

이전에 웹 스크롤링을 해서 저장된 데이터를

stringify함수를 사용해서 간단히 String으로 만듭니다

그래서 CSV 파일 형식으로 다른 이름으로 저장합니다 

 

 

CSV파일-웹크롤링정보쓰기
웹크롤링 정보 CVS파일 쓰기

 

그 파일은 아래와 같습니다

하지만 문제가 있습니다

첫 번째 콘솔에 정렬된 순서와 저장되고 나서의 순서가

다르다는 것입니다

 

협업을 하다 보면 파일의 정보 순서가

정말 중요할 경우가 있습니다

 

저장된 CSV파일
저장된 CSV파일

 

위의 코드에서 주석 부분을 변경해서 i를 사용합니다

i는 인덱스 넘버가 저장되어있습니다

초기 배열의 저장된 순서가 i에 저장되어있어서

정렬순서가 보존될 수 있습니다

 

정렬순서보존
map의 인덱스 넘버를 활용해서 순서보존하기

 

 

 

 

아래는 웹 크롤링할 경우에 노출되는 웹페이지입니다

10개가 동시에 열려있는 것이 보이시나요?

 

 

웹크롤링-브라우저모습
웹크롤링 브라우저 모습

 

 


 

이렇게 웹 크롤링을 알아보았습니다

아직 여러 함수들을 사용하지 않고 정말 기본적인 것을 활용해서

웹크롤링을 해보았습니다

 

다음 포스팅에서는 더욱 심도 있는 기능으로 찾아뵙겠습니다

우리 IT의 대모험은 끝나지 않았습니다

 

끝나는(?) 그날까지 파이팅 하세요

 

 

728x90
반응형
728x90
반응형

 

 

이전 포스팅에서는 엑셀과  CSV 파일에 저장되어 있는 데이터를 불러오는 것을 알아보았습니다

다른 타 부서나 프로젝트진행에 필요한 파일들이 저장된 데이터를 가져오기 위해

엑셀 및 CSV 파일 파싱을 해서 가져와서 데이터를 핸들링하는 방법을 알아보았습니다

 

더 상세히 알고 싶으시면 아래의 링크를 클릭해서 확인해보시길 바랍니다

 

2022.08.17 - [IT_Web/Nodejs] - node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

 

node.js로 엑셀 및 CSV 파일 데이터 간단하게 가져오기

node.js 관련은 처음 소개하는 것 같습니다 다음에 node.js로 서버 구축하는 것도 알아볼 테니 많은 관심 부탁드립니다 우선 node 다운로드 링크 주소는 사이드바에 생성해 놓을 테니 확인해 보시길

tantangerine.tistory.com

 

 

웹 크롤링을 간단하게 해 보자

 

 

그럼 바로 라이브러리부터 설치해보겠습니다

 

 

npm i axios cheerio

 

 

아래의 코드도 추가합니다

파일 구조나 시작점은 위의 링크로 들어가셔서 처음에 어떻게 시작했는지 확인해 보시길 바랍니다

이전 포스팅과 연결되는 부분이 조금 있어서 꼭 한번 들어가서 확인하시고

다시 돌아오세요~

 

 

const xlsx = require('xlsx');

const workbook = xlsx.readFile('xlsx/data.xlsx');
//console.log('readFile: ', Object.keys(workbook.Sheets))

const ws = workbook.Sheets.Sheet1; // 시트 명을 
//console.log('resultSheet: ', ws)

const wsRecods = xlsx.utils.sheet_to_json(ws); // 시트에 담긴 데이터를 json파일로 변경해준다
//console.log('resultWsRecods: ', wsRecods)


const axios =  require('axios');
const cheerio = require('cheerio');

const crawler = async () => {
  for (const [i, r] of wsRecods.entries()) {
    console.log('링크', r.링크)
    const response = await axios.get(r.링크);
    if(response.status === 200){
      const htmlStr = response.data;
      console.log('htmlStr: ', htmlStr)
      const $ = cheerio.load(htmlStr) // cherrio는 태그를 접근할 수 있습니다
      const text = $('.score.score_left .star_score').text(); // 선택자로 직접적인 class명에 접근이 가능합니다
      console.log(r.제목, '평점: ', text.trim());
    }
  }
}

 

데이터링크
링크가 들어있는 모습

첫 번째 콘솔에 링크를 보시면

현재 엑셀 파일에 받아온 데이터가 링크 주소인 것을 확인할 수 있습니다

그 주소를 불러와서 axios.get()의 함수를 사용해서

링크를 매개변수로 할당하여 데이터 값을 받아옵니다

그렇게 되면 responsehtml이 문자열로 들어오게 됩니다

 

html문자열을 콘솔 창에서 확인을 하면,

아래와 같이 html문자열을 확인할 수 있습니다

이 문자열을 활용하여 정보를 가지고 옵니다

 

html문자열
html 문자열을 확인할 수 있다

 

 

반응형

 

 

 

아래의 두줄의 코드를 보시면 cheerio를 활용하여 태그에 접근 가능하며,

이때 jQery를 사용해서 선택자의 클래스명에 접근이 가능합니다

이때 선택자는 어떻게 지정하느냐가 중요합니다

 

두 번째 줄에 보시면. score.score_left. star_score 클래스 명을 사용해서

 jQery함수인. text()를 사용해서 값을 받아오고 있습니다

 

 

 

F12로 개발자 모드 창을 띄워서 

아래의 빨간색 테두리에 있는 것을 클릭해서 웹페이지의 태그를 알아볼 수 있습니다

 

 

개발자모드창
개발자 모드 창

 

 

 

그리고 아래의 이미지처럼

보고 싶은 웹사이트의 부분을 클릭하면 됩니다

 

개발자모드
개발자모드

 

 

 

아래의 이미지를 개발자 모드 창에서 확인할 수 있습니다

 

 

개발자태그코드
개발자 모드의  html

 

그럼 text()를 사용하여 그 선택자로 접근한 태그의 textContent를 가져오게 됩니다

그렇게 되면 아래와 같이 결과가 출력됩니다

 

 

 

평점출력결과
평점출력결과

 

 

지금은 for of문과 await 사용하여 순차적으로 요청하는 모습을 볼 수 있습니다

하지만 이것은 한 개씩 요청하고 받기 때문에 시간이 다소 걸릴 수 있습니다

 

시간이 걸리는 것을 해결하기 위해서는 Promise를 사용하면

모든 요청을 한 번에 다 보내게되고 한번에 받게 됩니다

하지만 그렇게 되면 순서를 보장할 수 없습니다

 

그래서 이 두 가지를 생각하고 순서를 보장해야 할 경우에는 for of문 await을 같이 사용하고

그럴 필요가 없다면 Promise를 사용하는 것이 좋을 것입니다

 

 

Promise의 방법은 아래와 같습니다

 

const crawler = async () => {
  await Promise.all(wsRecods.map(async (r) => {
    //console.log('링크', r.링크)
    const response = await axios.get(r.링크);
    if(response.status === 200){
      const htmlStr = response.data;
      //console.log('htmlStr: ', htmlStr)
      const $ = cheerio.load(htmlStr) // cherrio는 태그를 접근할 수 있습니다
      const text = $('.score.score_left .star_score').text(); // 선택자로 직접적인 class명에 접근이 가능합니다
      console.log(r.제목, '평점: ', text.trim());
    }
  }))
}

 

 

지금까지 간단한 axioscheerio 활용하면,

간단한 웹 크롤링을 할 수 있습니다

그래서 다른 중요한 웹 페이지들은 cheerio 활용하면 웹 크롤링을 할 수 없습니다

이제 오늘은 간단한 웹크롤링을 배워 보았습니다

 

cheerio 말고 더 강력한 라이브러리를 사용해서 웹 크롤링해보도록 하겠습니다

그럼 오늘도 열심히 하시고

 

다음 포스팅도 기대해주십시오!

 

 

 

 

 

728x90
반응형
728x90
반응형

node.js 관련은 처음 소개하는 것 같습니다

다음에 node.js로 서버 구축하는 것도 알아볼 테니 많은 관심 부탁드립니다

 

우선 node 다운로드 링크 주소는 사이드바에 생성해 놓을 테니 확인해 보시길 바랍니다

그리고 설치방법은 구글 검색으로 간단히 해결할 수 있습니다

그러니 그것은 그냥 스킵하도록 하겠습니다

 

그럼  아래와 같이 폴더를 생성해주시고

 

파일구조
파일구조

아래와 같이 lecture 폴더 안에서 

npm init을 하시길 바랍니다

npm설정
npm 설정하기

입력하게 되면 몇 가지 질문사항을 확인하실 수 있습니다

 

package name 패키지 명은 폴더 이름이 기본값입니다.

version는 패키지 버전입니다. (1.0.0)는 기본 값입니다.

description는 패키지에 대한 설명입니다. 

entry point는 시작 파일 명입니다. (index.js)는 기본 값입니다.

test commandnpm test를 호출할 때마다 실행되는 명령입니다.

git repository는 패키지가 저장되어 있는 Git 저장소의 URL입니다.

keywords는 패키지의 키워드입니다.

author는 원작자의 이름입니다. 여러분의 이름이나 아이디를 입력하면 됩니다.

license는 패키지 사용에 대한 라이선스입니다. (ISC)는 기본 값입니다.

 

모든 질문사항을 기본값으로 설정해도 무관합니다

 

그럼 위와 같이 package.json파일이 생성됩니다

그리고 package.json파일 내부의 코드를 변경합니다

 

package.json 구조
package.json 구조

 

npm start를 할 경우에 index를 실행시킨다는 의미입니다

그리고 추후에 라이브러리를 설치하지만 csv-parse, xlsx의 버전을 확인해주시길 바랍니다

버전이 다르면 현재 제가 하는 것과 다르게 사용해야 하니 주의해 주세요

 

 


 

csv파일 파싱 해서 데이터 가져오기

CSV 파일이란 무엇일까요?

Comma Separated Value라고 합니다

한마디로 콤마로 이루어진 파일이라고 할 수 있습니다

 

아래와 같이 파일이 구성되어있지요

정확히 말하면 콤마와 줄 바꿈 정도라고 생각하실 수 있습니다

타이타닉,https://movie.naver.com/movie/bi/mi/basic/nhn?code=18847
아바타,https://movie.naver.com/movie/bi/mi/basic/nhn?code=62266
메트릭스,https://movie.naver.com/movie/bi/mi/basic/nhn?code=24453
반지의 제왕,https://movie.naver.com/movie/bi/mi/basic/nhn?code=31794
어벤져스,https://movie.naver.com/movie/bi/mi/basic/nhn?code=72363
겨울왕국,https://movie.naver.com/movie/bi/mi/basic/nhn?code=100931
트랜스포머,https://movie.naver.com/movie/bi/mi/basic/nhn?code=61521
해리 포터,https://movie.naver.com/movie/bi/mi/basic/nhn?code=30688
다크나이트,https://movie.naver.com/movie/bi/mi/basic/nhn?code=62586
캐리비안해적,https://movie.naver.com/movie/bi/mi/basic/nhn?code=37148

 

그런데 여기서 잠깐 의문이 생길 수 있겠지요

왜 이런 작업이 필요할까?

 

우리가 프로젝트를 진행하다 보면 현업 즉 고객사와 협업을 많이 진행합니다

아니면 다른 타 부서 사람과도 협업을 하겠지요

그러다 보면 대량의 정보를 데이터 베이스에 저장을 한다거나,

이 정보에 해당하는 어떤 값을 저장해 달라고 하거나

여러 정보가 필요하게 됩니다

이때 몇 천 건의 데이터를 수기로 하기는 어려움이 있습니다

그래서 이러한 프로그램적으로 구현하여 손쉽게 하기 위함입니다

 

그럼 다시 본론으로 넘어와

csv 파싱 라이브러리를 설치합니다

우리는 현존에 개발해놓은 좋은 라이브러리를 잘 사용하기만 하면 됩니다

이거 가져다 쓴다고 흉이 아니니 잘 쓰고 빨리 개발하면 되겠습니다

 

npm i csv-parse

 

 

 

index.js에서 아래와 같이 코드를 작성합니다

CSV 파일을 파싱 할 때 주의해야 할 점은 버퍼 파일이기 때문에 인코딩을 해야 하는 것을 잊지 말아야 합니다

 

// npm 패키지 불러올때 사용하는 require함수입니다
// 또한 node_modules파일에서 경로를 그대로 따라가서 그 함수를 들고온다고 생각하면된다
// 노드서버를 만들어서 배포하는 것이 좋습니다
const parse = require('csv-parse/lib/index.js'); 
const fs = require('fs'); // 파일시스템 모듈을 불러온다
const csv = fs.readFileSync('csv/data.csv'); // 파일을 불러오는 함수

// 이때 불러오게된 csv에는 버퍼(Buffer)형식이라서 문자열로 변경해주어야한다
// 버퍼는 0,1로 이루어진 컴퓨터 친화적인 데이터이다
csv.toString('utf-8') // toString을 하면서 인코딩을 하는 것은 잊지말자

console.log('result: ', csv.toString('utf-8'));

const records = parse(csv.toString('utf-8'))
records.forEach((movieInfo, i) => {
  console.log('영화제목: ', movieInfo[0], '*** 영화링크: ', movieInfo[1]);
});

 

 

첫 번째 콘솔result에 해당하는 값을 확인해보면 아래와 같이 확인할  수 있습니다

아래는 단지 파일을 읽어온 결과 값입니다

 

csv파일읽은결과
csv파일읽은결과

 

두 번째 CSV 파일파싱 한 결과

영화 제목과 링크로 나누어 보이는 결과 값입니다

아래와 같습니다 

 

csv파일파싱결과
csv파일 파싱 결과

이제 데이터를 어떻게 사용해야 할지 감이 잡이시나요?

그럼 다음은 엑셀입니다


 

 

 

반응형

 

 


 

엑셀 파싱 해서 데이터 가져오기

 

엑셀 파일에 가져올 데이터를 채워서 파일로 준비합니다

 

엑셀파싱데이터
엑셀파싱데이터

 

 

엑셀 파싱 관련 라이브러리를 추가합니다

엑셀 파싱은 정말 혼자서 만들기 힘드니 라이브러리를 이용하시길 바랍니다

 

 

npm i xlsx

 

 

그리고 아래의 코드를 index.js에 작성하여 줍니다

그리고 npm start를 실행하게 합니다

 

const xlsx = require('xlsx');

const workbook = xlsx.readFile('xlsx/data.xlsx');
console.log('readFile: ', Object.keys(workbook.Sheets))

const ws = workbook.Sheets.Sheet1; // 시트 명(Sheet1)을 뒤에 붙여준다
console.log('resultSheet: ', ws)

// .sheet_to_json()함수로 시트에 담긴 데이터를 json파일로 변경해준다
const wsRecods = xlsx.utils.sheet_to_json(ws); 
console.log('resultWsRecods: ', wsRecods)

for (const [i, r] of wsRecods.entries()) {
  console.log(`${i}번 ${r.제목} ** ${r.링크}`)
}

 

첫 번째 콘솔readFile을 보시면 ['Sheet1']이 들어가 있는 것을 확인할 수 있습니다

그래서 workbooks.Sheet.Sheet1으로 파일을 받아올 수 있게 되는 것입니다

 

그리고 두 번째 콘솔resultSheet를 보시면 엑셀 파일의 데이터가 저장되어있는 것을 확인할 수 있습니다

엑셀의 데이터를 ws라는 변수에 저장해서  xlsx라이브러리를 사용해서 파싱 해서 데이터를 가져옵니다

 

 

 

 

세 번째 콘솔에 보시면 resultWsRecodswsRecods값을 보시면

아래와 같이 json형태로 데이터가 들어오는 것을 확인할 수 있습니다

그 이유는 xlsx.utils.sheet_to_json() 함수를 사용해서

편하게 json형태 데이터를 파싱 할 수 있게 된 것입니다

 

 

 

네 번째 콘솔에 for문을 활용해서 데이터 값을 가져올 수 있습니다

그 결과 값은 아래와 같이 노출됩니다

이렇게 간단하게 라이브러리를 사용해서 파싱을 하여

외부 데이터를 가져와 핸들링하는 방법을 알아보았습니다

 

 

 

가져온 데이터를 데이터베이스에 저장을 하거나,

다른 작업을 할 수 있겠지요?

 

그리고 중요한 것이 있습니다

웹 크롤링을 위해서 for문, forEach문을 익혀두셔야 합니다

자바스크립트는 싱글 스레드이기 때문에 크롤링할 때도 똑같이 적용됩니다

그래서 비동기로부터 벗어날 수 없어서 비동기를 조심하면서 크롤링을 해야 합니다

이때 forEach문을 사용할 때와 for문을 사용할 때의 차이가 극명하게 다르기 때문

비동기를 자유자재로 사용해야 크롤링된 데이터를 모아서 한방에 저장할 수가 있습니다

 

오늘도 유익한 정보들을 알아보았습니다

이렇게 공부를 하다 보면 언젠가는 눈감고도 할 수 있는 날이 오겠죠?

ㅎㅎ 그럼 오늘도 수고하셨고

우리의 대모험이 끝나는 날까지 힘내시고 파이팅하시길 바랍니다

그럼 다음 포스팅도 많은 관심 부탁드립니다

 

 

 

 

728x90
반응형

+ Recent posts

Powered by Tistory, Designed by wallel