dukDukz

[블록체인] 데몬에 http로 요청하기, 크롤링 [수업내용] 본문

웹 개발/블록체인

[블록체인] 데몬에 http로 요청하기, 크롤링 [수업내용]

헤일리_HJ 2021. 9. 27. 12:51

오늘의 목표

→ 데몬을 http 통신으로

 

데몬의 내용을 볼때 cli 라는걸 활용했었다.

cli API 들을 배웠고...

 

데몬과 인터넷을 연결하고,

인터넷에서는 우리가 볼 수 있는 화면 (거래소) 를 만든다. 

브라우저에 뭔가를 띄우고 한다는 뜻...

 

간단하게 말해서.. 이 데몬으로 요청을 보낼때, cli 로 하는게 아니라 http 통신으로 해보겠다는 것이다.

 

cli 도 PRC 통신..

거의 http 와 흡사하다.

그래서 http 로 요청했을 때 응답을 받을 수 있다.

 

그리고 응답을 받은것을 터미널에서 보여주는게 아니라 브라우저에 띄울 것이다.

 


 

# 단계

1. http 요청 - 응답이 잘 오는가?

2. node js 환경(express)에서 http 요청을 한 것을 코드로 옮겨서 테스트를 해본다.

   (어떻게 처리하는지도 알아볼 것이다.) - 응답 받는것까지.

express 로 다른 곳(다른 서버)에 요청을 보내서 값을 가져오는.. 그런느낌으로..

 

API 를 쓴다는 뜻.

코드 구성을 이해해보고...

그 쪽에 포커스를 맞춰서 해보자

 

http 로 한다는 것은 curl 로 데몬에게 요청을 보내야 함.

 

curl 을 쓰기 위해서 어떤 환경을 구축해야 할까?

- 데몬이 잘 돌아가는지 확인하기 위해서 cli 로 테스트 해보고 그 다음에 curl 로 되는지 해보자

 


[getnewaddress]

 

1. cli 로 요청

 

데몬 1 켜고

클라 1에 

.\juncoin-cli.exe -conf="C:\Users\USER\juncoin\bin\juncoin1.conf" getnewaddress ingoo4

하면

jRj1TuXBSfQCG6becMBmXWaS9KsZ5wZWMH

이렇게 나온다.


 

2. http 로 요청해본다.

 

curl 은 윈도우 터미널이 아닌 wsl 에서 가능한 녀석이다. (쉘)

탭을 하나 열어서 wsl 로 들어가자

 

# curl 요청 형식
-H : header
-d : data
-X : Request method

curl [option][도메인]

curl -X POST -H "" -d "data~" http://naver.com

이렇게 썼었는데

RPC 통신에는 user와 pw 가 들어간다.

 

1) 도메인

[도메인]=RPCUSER:RPCPASSWORD@IPADDRESS:RPCPORT

 

데몬1

 

RPCUSER = ingoo

RPCPASSWORD = 1234

IPADDRESS = 127.0.0.1

RPCPORT = 3000

 

[도메인] = ingoo:1234@127.0.0.1:3000

이게 주소(도메인)이 되는 것이다.


2) Header

 

curl -X POST -H "Content-type:application/json" -d "{}" ingoo:1234@127.0.0.1:3000

H 는 컨텐츠 타입... json 형태로 데이터를 주고 받기 때문에 

data 는 객체 형태로 들어가게 된다.

 

클라에서 요청 할때,

getnewaddress 를 했는데

curl 형태에서는 body 영역에 들어가게 된다.

 

 

3) data

data 부분

method : 데몬 API 명

params : 인자값, 인자값이 많은경우를 대비해서 배열을 사용함

{
   "method":"getnewaddress","params":["ingoo5"] 
}

curl 요청

curl -X POST -H "Content-type:application/json" -d '{"method":"getnewaddress","params":["ingoo5"]}' ingoo:1234@127.0.0.1:3000

이렇게 쓰게 된다. - wsl 에서 이걸 입력해보자 (/mnt/c/Users/USER 여기에서 요청하면 된다.)



그러면 결과가 

{"result":"jXcGe7mXeqdxcYjKvezNrLGWWvqrBf7gJy","error":null,"id":null}

이렇게 나오게 된다.

 

getnewaddress 도 http 로 요청할 수 있다는 것을 확인했다.

 


# Node JS 환경 구축

curl 을 node js 환경에서 express 에서 만든다면 브라우저에서 보여줄 수 있겠다..!!!

기본적으로 nodejs express 구축하고

express 에서 http 요청을 할 수 있게끔 하면 된다

 

vs code 키고

# 1. server.js 만들기

# 2. 터미널 켜고 npm init 해서 nodejs 환경 만들기

# 3. npm install express

# 4. npm install request

 

# express 의 역할?

- 얘가 하는 역할은 무엇인가?

요청이 들어오면 응답을 주는 녀석이다.

(http 로 요청, http 에 대한 응답)

 

express 로 요청이 들어오면

express 가 응답을 줄텐데,

응답 코드가 res.send('hello') 이런식으로 주었다.

그런데 응답을 주기 전에 또 요청이 하나 끼게 된다.

 

즉 요청이 들어오면 또 new 요청을 보냄 new 요청에 대한 응답을 받으면,

그때 처음 요청에 대한 응답을 준다. (서버가 두대가 있다고 생각)

 

브라우저 = 클라이언트

 

new 요청은 데몬에 대한 요청이다.

 

이런 형식은 db를 쓸 때 써봤다. db 도 어떤 하나의 서버라고 생각하면 이와 비슷하다고 볼 수 있다.

 

그래서 express 는 최종 응답만 주는 녀석

 

나중에는 db 도 추가해서 할거임

 

브라우저--------------> express -----------------------> daemon

                                            requset 패키지 사용

 


 

[server.js]  

요청 -> 응답

// url 127.0.0.1:3800 -> hello ingcoin! 출력을 해보자

const express = require('express')
const app = express()

app.get('/',(req,res)=>{
    res.send('hello ingcoin!')
})

app.listen(3800,()=>{
    console.log('server open');
})

실행 해보고 브라우저에서 주소 쳐보기 [사진2]

-> 여기까지는 요청- 응답 바로 준거임

 


# 요청 속의 요청

기본적인 http 요청을 해보자

naver 에 대한 body 영역을 가져오는것을 해보자

const request = require('request')
// 기본적으로 함수이다.
//node js 환경에서 요청을 보내는 녀석

// 요청A - 요청B - 응답B - 응답A
app.get('/naver',(req,res)=>{
    // 요청 B - request 라는 패키지를 사용할거임
    request('https://naver.com',(err,response,body)=>{
        console.log(body);
    })
    res.send('naver')
})

requeset 안에는,

두가지 인자값 존재

1. url 값 (string) or object => 어디로 보낼지

2. callback 에 대한 값 - 요청에 대한 결과물이 떨어지고 나면 콜백이 실행된다.
콜백함수에는 3가지 인자값 err,response,body 가 존재함

response
: body에 있는 영역..

body
: body 에 있는 내용을 그대로 string 값으로 가져오는 것.

 

서버 껐다가 켠 다음

http://127.0.0.1:3800/naver

로 요청을 보내면 vs code 터미널에 naver body 에 대한 내용이 쫘르륵 뜨게 되면 성공이다.

 

console.log(response);

하면 뭔가 내용이 뜸..

 

에러

console.log(err);


성공 : null

실패 : 

일부러 에러를 만들어보았다.

 

err 가 찍혀도 

res.send('naver')

가 브라우져가 찍힌다.

 

왜 그럴까?

 

싱글 스레드이기 때문에 request (비동기 처리 되어서)가 다른 곳(테스트큐)으로 빠지고

결과 여부와 상관없이 

res.send('naver') 가 실행된다.

 


# 코드를 어떻게 수정해야 할까?

 

요청A 는 응답A 를 주는데 

요청B 가 응답B 까지 다 떨어지고 난 다음에 주려고 한다면 

이 코드를 어떻게 수정하면 될까??

 

res.send('naver') 를 콜백함수 안에서 실행하면 된다!!

그 처리가 안에서 되기 때문에

request('https://naver.cm',(err,response,body)=>{
   console.log(err)
   res.send('naver')
})

이렇게 바꾸면

console.log(err) 얘가 실행이 되는걸 기다리고

그 다음에 res.send('naver') 얘가 뜬다.

 

이제는 순서가 맞게 된거임

 

여기서 응용을 하자면

err 체크 하는 부분을 만들면 된다.

 

let msg = "naver"
if(err != null){
    msg="error"
}
res.send('error')

이런식으로...
혹은

if(err == null){
   res.send('naver')
}else{
   res.send('error')
}

이렇게

 

 


두번째 인자값 처리

app.get('/naver2', (req, res) => {
    // header 내용 넣고 method 값도 넣어보자
    // 1. url 또는 object   2.콜백
    request({url:"http://naver.com"},(err, response, data)=>{
        console.log(data);  //body 영역을 가져옴
        res.send('naver2')
    })
})

객체 안에 url 속성값을 넣어서 써도 된다는 것.

 

왜 굳이 객체로 받았지??

url 말고 또 넣을 값들이 존재하기 때문에.

 

app.get('/naver2', (req, res) => {
    request(
    { 
        url: "http://naver.com", 
        method: "POST", 
        headers: { "Content-type": "application/json" }, 
        body: "{msg:'hello world!'}" 
    },
    (err,response, data) => {
        console.log(data)
        console.log(response.statusCode) // 상태코드를 볼 수 있다
        res.send('naver2')
    })
})

그냥 모양이 그렇다는 거임

결과물이 나오지는 않을거임 post 로 받는 naver 는 없기 때문

 

console.log(response.statusCode)

상태 코드를 봤을 때 성공(200)

인데 301 이 떴다 - 실패라는 뜻

 

즉 성공이라는 조건이라면 상태코드가 200이고 err==null 값이어야 한다.

 

그래서 둘 중에 하나라도 안되면 실패라고 간주해야 한다.

if (err == null && response.statusCode==200) {
    res.send('naver2')
} else {
    res.send('error')
}

이런식으로 해주면 된다.

 

전체코드)

app.get('/naver2', (req, res) => {
    request(
    { 
        url: "http://naver.com", 
        method: "POST", 
        headers: { "Content-type": "application/json" }, 
        body: "{msg:'hello world!'}" 
    },
    (err, response, data) => {
        console.log(data)
        console.log(response.statusCode);
        if (err == null && response.statusCode==200) {
            res.send('naver2')
        } else {
            res.send('error')
        }
    })
})

curl 로 데몬에 요청 보내기

이제 본격적으로 

cli 로 데몬에 요청을 보냈던 것을

express 환경에서 요청해보는 것을 해보자

 

curl 
-X POST 
-H "Content-type:application/json" 
-d '{"method":"getnewaddress","params":["ingoo5"]}' 
ingoo:1234@127.0.0.1:3000
const headers = {"Content-type": "application/json"}
const USER = process.env.RPC_USER || 'ingoo'
const PASS = process.env.RPC_PASSWORD || '1234'
const RPCPORT = process.env.RPC_PORT || 3000

app.get('/newaddress/:account', (req, res) => {
    const {account} = req.params
    const body = `{"method":"getnewaddress","params":["${account}"]}`
    const options = {
        url: `http://${USER}:${PASS}@127.0.0.1:${RPCPORT}`,
        method: "POST",
        headers,
        body
    }

    const callback = (err, response, data) => {
        if (err == null && response.statusCode == 200) {
            const body = JSON.parse(data)   // 결과물이 json 형태로 오지만 string 이므로 이렇게 parse 해주고
            res.send(body)
        } else {
            res.send('error')
        }
    }
    request(options, callback)
})

 

요청할때마다 주소가 계속 생기고

 

wsl 에서 확인 가능

 

.\juncoin-cli.exe -conf="C:\Users\USER\juncoin\bin\juncoin1.conf" getaddressesbyaccount ingoo6

[
  "jFjPqNC32T8a2TDCmPNQJtXbDYoCx9qJFR",
  "jTVJ13fjd1PRJdta9AqNBjiGPFYAU1B6J4",
  "jWYVSmqQcbVLh5KVFDjB1rfNcFgXZhoofw",
  "jZvDCjuvan3YL3CVdpEUDnAA2dYj4fk156",
  "jc6SUpx14QucBS3vCzKbrGaT1F9L3LSftc"
]

번외) 크롤링

 

저 request 랑 항상 같이 쓰는 애가 있다.

바로 크롤링!!!!

블록체인이랑 별개의 내용이긴 함.

알아두면 유용함!

 

사이트에 있는 body 영역에서 내가 필요한 부분만 뽑아오는게 크롤링이다.

 

naver html 이 존재할텐데

가변적으로 내용이 변하는 곳이 있다면,

그 부분을 자동적으로 하는걸 만들었으면 좋겠다 싶어서

크롤링을 사용하면 된다.

 

div 안에 텍스트내용만 갖고 오고 싶다면..??

innerHTML 내용들만?

 

우리가 body 전체를 가져오는 건 했다.

특정 부분만 가져오는게 있다. 

 

cheerio 패키지 사용

const cheerio = require('cheerio')  
// npm install cheerio
// 이 텍스트 형태로 읽어서 html element 형태로 만들어줘서 data 저장하는 녀석이라고 보면 된다.

app.get('/crawling',(req,res)=>{
    request('http://naver.com',(err,response,data)=>{
        let $ = cheerio.load(data)      // 얘를 선택자로 쓸거임
        // 뽑아낼 영역. css 에서 선택하듯이 뽑아서 결과물이 여러개니까 each 로 반복문 돌림

        $('.partner_box_wrap > .partner_box:nth-child(3) > a').each((index,item)=>{
            // console.log(typeof(item)) // 이렇게 찍어서 어떤 형태인지 알수 있음 object
            // console.log(item.children[0].data)      // 하나씩 찍어보면서 안으로 들어가면 된다.
            let {data} = item.children[0]
            console.log(data);
        })
    })
})

 

copy select 사용하면 선택자 쉽게 사용가능