Pandas 데이터 <-> MySQL

이전 포스팅에서 만든 데이터프레임을 mySQL과 연동하기 위한 과정이다.

import pandas as pd
from sqlalchemy import create_engine
import pymysql

# db 연결
db = pymysql.connect(host='localhost', user='root', password='비번')
cursor = db.cursor()

# DataFrame을 MySQL 데이터베이스에 저장
try:
    engine = create_engine("mysql+pymysql://root:비번@localhost:포트번호/연결할db")
    데이터프레임이름.to_sql(name="테이블", con=engine, if_exists="replace", index=False)
    print("DataFrame이 성공적으로 MySQL 데이터베이스에 저장되었습니다.")
except Exception as e:
    print("에러 발생:", e)
finally:
    db.close()

 

데이터프레임의 정보가 그대로 mySQL에 저장


Mysql <-> node.js 연동

https://www.npmjs.com/package/mysql2

 

mysql2

fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS. Latest version: 3.9.6, last published: 3 days ago. Start using mysql2 in your project by running `npm i mysql2`. There are 4319 other projects in the npm re

www.npmjs.com

여기에 기본적인 사용법이 나와있다.

// Get the client
const mysql = require('mysql2');

// Create the connection to database
const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: '비번',
  database: '연결할 db',
  dateStrings: true
});

// Using placeholders
connection.query(
  'SELECT * FROM `테이블`',
  ['Page', 45],
  function (err, results) {
    console.log(results);
    results.map((item)=>{
      console.log('key : ', item.key)
      console.log('title : ', item.title)
      console.log('sub_title : ', item.sub_title)
      console.log('img : ', item.img)
    })
  }
);

연결 후 테스트 로그를 찍어보면

잘 연결된 것을 볼 수 있다 !

크롤링하기 위해 필요한 라이브러리

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

안양 청년 정책 웹 사이트 url을 얻어온다.

driver.get("https://www.anyang.go.kr/youth/contents.do?key=3567")

웹페이지에서 요소를 찾기 위해 암묵적으로 10초 대기

driver.implicitly_wait(10)

제목, URL 크롤링

해당 페이지에서 각 정책의 제목과 url 크롤링

해당 정책이 몇 개인지 고정되어있지 않기 때문에 값을 설정할 수 없어서 while문을 사용하여 정책 요소들을 순회한다.

순회하면서 해당 정책의 각 url의 key값, 정책 제목을 구한다.

구해온 리스트 안의 딕셔너리 형태로 저장한다. key: key, title: 정책 제목 형태

while True:
        try:
            # key
            url_element = driver.find_element(By.XPATH, '//*[@id="contents"]/div/div/div/ul/li[{}]/a'.format(i))
            title = driver.find_element(By.XPATH, '//*[@id="contents"]/div/div/div/ul/li[{}]/a/span[1]'.format(i))
            
            url = url_element.get_attribute('href')
            key = url.split('=')[-1]
            policyData.append({"key": key, "title": title.text})
            
            i += 1
        except NoSuchElementException:
            break  # 에러가 발생하면 반복문 종료

각 정책들이 리스트 안의 딕셔너리 형태로 저장됨.


데이터프레임으로 저장

이후 고유한 값은 해당 url의 key 값으로 지정하고, 정책 제목은 title로 지정하여 데이터 프레임으로 만든다.

데이터 프레임은 엑셀의 파이썬 버전이라고 생각하면 쉽다.

dfPolicy = pd.DataFrame(policyData) # 데이터 프레임으로 만들기

위 리스트와 같은 값이지만 데이터프레임으로 저장하면 데이터를 관리하기 쉬워진다.


데이터프레임 column 추가하기

현재 key, title만 설정되어있다. sub_title, img의 값들도 추가할 것이기 때문이기 때문에 컬럼명부터 설정한다.

dfPolicy['sub_title'] = None
dfPolicy['img'] = None


위에서 구한 Key 값으로 나머지 sub_title / img 구하기

url의 key 값으로 정책의 이미지와 사업 목적을 구해올 것이다.

정책 페이지 순회

먼저 데이터 프레임 안에 있는 키값을 이용하여 정책 페이지를 순회한다

for index, row in dfPolicy.iterrows():
        policy_key = row['key']
        driver.get('https://www.anyang.go.kr/youth/contents.do?key={}'.format(policy_key))

이미지 구하기

이미지는 로딩이 느릴 수도 있으므로 로드될 때까지 기다린 후 로딩이 끝나면 요소를 구해온 후 리스트로 저장

# 이미지 로드될 때까지 기다림
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@id="contents"]/div/div/div[1]/div/div[1]/div/div/div/div/div/img')))
img = element.get_attribute('src')
policy_img_lst.append(img)

사업목적 구하기

이후 사업목적도 동일한 방식으로 구해온 후 리스트로 저장

sub_title = driver.find_element(By.XPATH, '//*[@id="contents"]/div/div/div[2]/div/ul/li[1]/p')
sub_title_lst.append(sub_title.text)

데이터 프레임에 저장

dfPolicy['img'] = policy_img_lst
dfPolicy['sub_title'] = sub_title_lst

리스트 안의 값들을 데이터프레임 img, sub_title 열 안에 저장한다.

여러 줄로 잘렸지마나 데이터프레임에 key, title, sub_title, img가 잘 들어가있는 것을 볼 수 있다!

  • 회원가입 - POST /signup
  • 로그인 - POST /signin
  • 회원 개별 조회 - GET /users/:id
  • 회원 개별 탈퇴 - DELETE /users/:id

회원가입 : POST

app.post('/signup', (req, res) => {
  if (req.body == {}) {
    res.status(400).json({
      message: '입력 값을 다시 확인해주세요.'
    })
  } else {
    db.set(id++, req.body)

    res.status(201).json({
      message: `${db.get(id-1).name}님 환영합니다.`
    })
  }
})

요청 바디에는 id, pwd, name 등이 올 수 있다.

요청 바디로 들어온 회원 정보를 db에 저장하고, 성공했다는 응답 코드를 돌려준다.

id++ 을 하는 이유는 자동으로 1을 올려주기 위함 (하나의 객체를 유니크하게 구별하기 위함)

아직 db랑 연결을 안 하고 설계하는 과정이라 수동으로 하고 있다 . ㅎㅎ


회원 개별 조회 : GET

app.get('/users/:id', (req, res) => {
  const id = parseInt(req.params.id)
  const user = db.get(id)

  if (user == undefined) {
    res.status(404).json({
      message: "회원 정보가 없습니다."
    })
  } else {
    res.status(200).json({
      userId: user.userId,
      name: user.name,
    })
  }
})

 

조회하고 싶은 회원의 id를 params로 받는다.

params는 모두 문자열로 들어오기 때문에 parseInt로 정수로 변환한다.

회원의 id를 db에서 찾고,

존재하지 않는다면 상태코드 404와 회원정보가 없다는 메세지를 보냄

존재한다면 상태코드 200과, id와 일치하는 회원 정보를 응답으로 넘겨준다.


회원 개별 탈퇴

// 회원 개별 탈퇴
app.delete('/users/:id', (req, res) => {
  const id = parseInt(req.params.id)
  const user = db.get(id)

  if (user == undefined) {
    res.status(404).json({
      message: "회원 정보가 없습니다."
    })
  } else {
    db.delete(id)
    res.status(200).json({
      message: `${user.name}님, 다음에 또 뵙겠습니다.`
    })
  }
})

 

회원 개별 조회와 거의 비슷하다.

params로 회원의 id를 받고 있다면 db에서 삭제, 없다면 상태코드 404와 에러 메세지 반환


route를 활용한 코드 리팩토링 - 회원 개별 조회 / 회원 개별 탈퇴

개별조회와 개별탈퇴는 http method만 다르고 url이 같다.

route를 이용해 중복 코드를 줄일 수 있다.

app
  .route('/users/:id')
  .get()
  .delete()

 

중복되는 url을 묶고, 아래 메서드들로 분리 할 수 있다.

각 메서드 괄호 안에는 해당 요청이 들어왔을 때의 실행할 콜백 함수만 넣어주면 된다.

app
  .route('/users/:id')
  // 회원 개별 조회
  .get((req, res) => {
    const id = parseInt(req.params.id)
    const user = db.get(id)
  
    if (user == undefined) {
      res.status(404).json({
        message: "회원 정보가 없습니다."
      })
    } else {
      res.status(200).json({
        userId: user.userId,
        name: user.name,
      })
    }
  })
  // 회원 개별 탈퇴
  .delete((req, res) => {
    const id = parseInt(req.params.id)
    const user = db.get(id)
  
    if (user == undefined) {
      res.status(404).json({
        message: "회원 정보가 없습니다."
      })
    } else {
      db.delete(id)
      res.status(200).json({
        message: `${user.name}님, 다음에 또 뵙겠습니다.`
      })
    }
  })

로그인

1. db를 순회하면서, 요청받은 userId가 db에 있는지 확인

2. 있다면 해당 객체의 정보를 loginUser에 담는다. (없으면 loginUser는 빈 값이 됨)

3. loginUser가 빈 값인지 확인하고, (빈 값이 아니면 > userId가 일치한다는 것)

4. 요청받은 pwd가 해당 userId의 pwd와 같은지 확인

// 로그인
app.post('/signin', (req, res) => {
  var loginUser = {}
  const { userId, password } = req.body

  // userId가 db에 저장된 회원인지 확인
  db.forEach((user, idx)=>{ // data, idx, totalData
    if (user.userId === userId) { // 요청데이터 userId와 db에 있는 userId가 같은지 확인
      loginUser = user 
    }
  })
  
  // userId 값을 못 찾았으면
  if(isExisted(loginUser)) {
    // pwd도 맞는지 비교
    if (loginUser.password === password) {
      console.log('비번도 같아')
    } else {
      console.log('틀렸다.')
    }
  } else {
    console.log('없는 아이디입니다.') 
  }
})

 

 

Object.keys(obj)의 길이가 0이라면, 비어있는 것 (속성 이름이 하나도 존재하지 않는다는 것이기 때문)

Object.keys() > 객체의 속성 이름을 배열로 얻을 수 있는 메서드

// 객체가 비었는지 확인하는 함수
function isExisted(obj) {
  if (Object.keys(obj).length) {
    return true
  } else {
    return false
  }
}

'졸업작품 > Node.js' 카테고리의 다른 글

Express - params, query(쿼리 문자열)  (0) 2024.04.18
RESTful API 및 HTTP 프로토콜 기본 개념  (0) 2024.04.17

Params : 동적인 URL 경로 처리

app.get(`/products/:n`, function(req, res) {
  res.json({
    num: req.params.n
  })
})

클라이언트에서 /products 뒤에 입력한 값을 req.params로 받을 수 있음.

/products/:n 경로에 접속하면 :n자리에 오는 값이 req.params.n에 저장되어 해당 값을 JSON 형식으로 반환.

n에 200 전달 > req.params.n으로 받을 수 있음.


쿼리 문자열

  • 쿼리 문자열은 URL의 끝에 '?'를 사용하여 추가적인 데이터를 전달하는 데 사용
  • 쿼리 문자열은 'key=value' 쌍으로 이루어져 있음
  • '&'를 사용하여 여러 개의 쌍을 연결할 수 있음
  • Express에서는 req.query를 통해 이러한 쿼리 문자열을 읽어올 수 있다.
// https://www.youtube.com/watch?v=2KheQK5srCc&t=2339s
app.get(`/watch`, function(req, res) {
  const query = req.query
  res.json({
    video: query.v,
    timeline: query.t
  })
})

/watch?v=2KheQK5srCc&t==2339s 에 접속하면

watch경로 뒤에 오는 쿼리문자열을 req.query로 받을 수 있다.

현재 v=value / t=value 형태로 두 쌍이 존재하며, 이 두 쌍을 JSON 형태로 반환할 수 있다.

더 깊이 접근하고 싶다면, req.query.v / req.query.t > 객체 표기법으로 각 키의 값에도 직접 접근할 수 있다.


+ TIP! 자바스크립트 객체, 배열 비구조화 할당

코드를 간결하고 가독성 있게 사용할 수 있음.

객체

  • 객체 비구조화 할당에서는 변수의 이름과, 객체의 속성명이 반드시 같아야 한다.
  • 변수의 이름이 객체의 속성명과 일치하지 않으면 값이 할당되지 않는다.

배열

  • 배열은 속성명이 따로 존재하지 않기 때문에, 배열의 순서대로 할당된다.
  • 선언된 변수의 순서와, 배열의 요소 순서가 일치해야 한다.
  • 중간에 필요없는 값이 끼여있다면 , ,로 생략 처리를 해주어야 한다.

'졸업작품 > Node.js' 카테고리의 다른 글

[Node.js] 회원 API 기초 설계  (0) 2024.04.19
RESTful API 및 HTTP 프로토콜 기본 개념  (0) 2024.04.17
def solution(a, b):
    return sum([a[i]*b[i] for i in range(len(a))])
  • 두 배열은 길이가 같다고 했기 때문에 리스트를 순회할 때, 어떤 리스트의 길이를 사용하든 상관은 없다.(난 a의 길이를 사용했다.)
  • 각 리스트에 대응되는 요소끼리 곱한 후 반환된 리스트의 값들을 sum 함수를 사용해 전부 더한다.

https://school.programmers.co.kr/learn/courses/30/lessons/70128

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

 

'코테 > 프로그래머스' 카테고리의 다른 글

약수의 합 - python  (0) 2024.04.16
제일 작은 수 제거하기 - python  (0) 2024.04.16
시저 암호 - python  (0) 2024.04.16

클라이언트와 서버 간의 약속, 프로토콜(Protocol)

서로 정보를 주고 받을 때 지켜야 하는 약속이 존재한다.

반드시 이 약속을 지켜서 통신해야 하며, 우리는 이 약속을 프로토콜이라 부른다.

 

인터넷으로 연결된 클라이언트와 서버는 웹 프로토콜인 HTTP를 사용하여 데이터를 주고 받는다.

웹(=인터넷망 속 가상 공간) 개발자 > 인터넷을 돌아다니기 위한 규약을 지켜야만 함 > HTTP를 지켜야만 한다.

 

HTTP 형식을 엄격하게 준수하지 않아도 통신을 가능하다. 하지만 규약 따르면 효율이 극대화!


RESTful API : HTTP 규약을 매우 잘 따른 API, REST의 원칙을 완벽하게 준수하는 API

RESTful API 쓰는 이유

  • 웹에서 일반적으로 사용하는 표준 프로토콜 활용 > 다양한 플랫폼 및 언어에서 API를 손쉽게 사용하고 통합할 수 있다.
  • API 사용이 직관적 > GET, POST, PUT, DELETE < 명확한 의미를 가지고 있어 이해하고 사용하기가 쉽다.
  • 자원 중심적으로 URL을 통해 고유하게 식별할 수 있다. 일반적으로 CRUD 작업 지원(Create, Read, Update, Delete)

HTTP 에 담아 보내야하는 것들

인터넷 상에서 공유/전달하고 싶은 모든 것들은 다 http에 넣어서 보내야 한다

HTTP 요청과 응답은 Head / Body 로 나누어져있음.

 

Header(헤더)

  • 통신 상태가 어떤지 알려주는 HTTP code(상태 코드, 200, 404, 500 등)
  • 응답이 어떤 형태인지.

Body(바디)

  • 응답의 본문 데이터(요청 성공, 요청 실패에 대한 정보 등)
  • post, put의 경우, 전달하려는 데이터

HTTP 에 담아보내는 나의 목적 = HTTP method

  • 생성(=등록) : POST
  • 조회 : GET
  • 수정 : PUT(덮어쓰기) / PATCH(일부 변경)
  • 삭제 : DELETE

REST API URL 규칙

  • 대문자 X, 소문자 O
  • 언더바(_) X, 하이픈(-) O
  • 마지막에 / 포함 X
  • 목적을 포함하지 않음 X
  • 복수형 O

URL + HTTP method (API 설계)

http://localhost:8888 : 내 컴퓨터 주소(local host)

 

잘못된 예

http://localhost:8888 상품 "등록"=> /post_product

http://localhost:8888 전체상품 "조회" => /select_all_products

http://localhost:8888 전체 상품 "삭제" => /DeleteAllProducts

 

올바른 예

http://localhost:8888 상품 "등록"=> "POST" /product

http://localhost:8888 전체 상품 "삭제" => "DELETE" /products

http://localhost:8888 전체 상품 "조회" => "GET" /products

 

http://localhost:8888 상품 " 개별 조회" => "GET" /products/{id}

http://localhost:8888/products/1
http://localhost:8888
/products/2
http://localhost:8888
/products/3

'졸업작품 > Node.js' 카테고리의 다른 글

[Node.js] 회원 API 기초 설계  (0) 2024.04.19
Express - params, query(쿼리 문자열)  (0) 2024.04.18
def solution(n):
    sum = 0
    for i in range(1, n+1):
        if n % i == 0:
            sum += i
    return sum

 

  • n을 어떤 수로 나누었을 때, 나머지가 0이라면, 어떤 수는 n의 약수
  • 약수의 조건을 만족하는 수를 sum에 더한 후 반환
def solution(n): 
	return sum([i for i in range(1, n+1) if n % i == 0])

 

  • 짧은 코드들은 리스트 컴프리헨션으로 표현하는 것이 깔끔한 것 같다.
약수의 조건
"나머지가 0일 때"
주어진 수를 다른 수로 나누었을 때, 그 나머지가 0이 되어야 함.

https://school.programmers.co.kr/learn/courses/30/lessons/12928

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

'코테 > 프로그래머스' 카테고리의 다른 글

내적 - python  (0) 2024.04.17
제일 작은 수 제거하기 - python  (0) 2024.04.16
시저 암호 - python  (0) 2024.04.16
def solution(arr):
    if len(arr) == 1:
        return [-1]
    minV = min(arr)
    return [i for i in arr if minV != i]
  • 길이가 1이면 [-1] 반환
  • min함수로 최솟값을 찾아 냄
  • arr를 순회하면서 최솟값이 아닌 요소로 이루어진 새로운 배열로 만들어서 반환!

https://school.programmers.co.kr/learn/courses/30/lessons/12935

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

'코테 > 프로그래머스' 카테고리의 다른 글

내적 - python  (0) 2024.04.17
약수의 합 - python  (0) 2024.04.16
시저 암호 - python  (0) 2024.04.16
def solution(s, n):
    answer = ''
    for i in list(s):
      if i == ' ': answer += ' '
      elif 'a' <= i <= 'z':
        if ord('z') < ord(i) + n:
          answer += chr(ord(i) + n - 26)
        else:
          answer += chr(ord(i)+n)
      else:
        if ord('Z') < ord(i) + n:
          answer += chr(ord(i) + n -26)
        else:
          answer += chr(ord(i) + n)
    return answer
  • 공백일 땐 그대로 공백 처리를 해준다.
  • 알파벳+n을 했을 때, 'z'를 넘어간다면 다시 알파벳의 시작으로 돌아가게 한다.
    • 알파벳은 26개 >> 즉,  -26을 해줌으로써 a로 돌아가도록 설정
    • 아닐 경우 그대로 더한 후 변환

파이썬에서 ord()와 chr() 함수는 문자와 해당 문자의 아스키 코드 값을 변환하는 데 사용

  1. ord() 함수:
    • ord('a')는 문자 'a'의 아스키 코드 값인 97을 반환
  2. chr() 함수:
    • chr(97)은 아스키 코드 값이 97인 문자(a)를 반환 >> 이는 소문자 'a'를 의미

https://school.programmers.co.kr/learn/courses/30/lessons/12926

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

'코테 > 프로그래머스' 카테고리의 다른 글

내적 - python  (0) 2024.04.17
약수의 합 - python  (0) 2024.04.16
제일 작은 수 제거하기 - python  (0) 2024.04.16

TypeScript는 왜 필요할까 ?

JS 코드가 너무 지저분하다.

코드 스케일이 커지면서 코드 관리가 되지 않는다.

 

여러 사람들이 견고하지 않은 언어를 이용해서 협업을 하다보니 지저분한 코드들이 발생한다.

협업시 수많은 버그들이 발생된다. > 디버깅 환경이 빈약하다.

TypeScript의 장점 !

코드를 깔끔하게 정리할 수 있다.

어떻게 ? > Type을 명시함으로써 애매모호했던 코드들을 걷어낼 수 있다.

가독성 높아짐 > 퀄리티 높아짐 > 클린 코드에 가까워진 코드들을 생산할 수 있다 > 유지/보수가 쉬워진다. 선순환 !

TypeScript = JavaScript + Type Check !

TS 환경에 JS를 코딩하면 동작함.

JS 환경에 TS를 코딩하면 동작 안 함.

JS는 TS의 범주 안에 존재한다 !

Data Type Check 

JS는 변수는 종이컵과 같다 > 액체를 담기에는 편하지만 내용물이 무엇인지 정확하게 알 수가 없다.

TS의 변수는 투명페트병과 같다. > 내용물이 무엇인지 정확하게 보이고, 뚜껑을 닫음으로써 안전하게 보관할 수 있다.

 

- JS는 변수의 Data Type을 정확히 명시하지 않는다. ex) var -> 대입되는 값에 따라 변수의 type 달라진다

- TS는 변수에 Data Type을 명확하게 지정하여 안정성을 높여준다. ex) int, double -> 변수 type 고정

 

Data Type을 표기하면 좋은 점

- 객체 지향 프로그래밍의 특성을 지원한다.

- 클래스, 인터페이스, 컨스트럭터, 접근 지정자 등 객체 지향의 프로그래밍 특성을 지원

- TS를 이해하면 JS에 객체 지향 코드를 적용할 수 있다.


function Plus(a : number, b : number) {
  return a + b;
}

console.log(Plus(3, 5));

a, b의 type을 명시하여 해당 변수의 데이터 타입을 유추할 수 있다.

즉, a,b에 number가 아닌 다른 데이터 타입(문자열 등)이 들어오게 되면 바로 에러로 인식 > 에러를 발견할 수 있게 된다.

하지만, 만약 해당 함수가 js로 작성된 것이라면 a, b의 데이터 타입이 문자열로 들어오게 되어도 에러로 인식하지 않음.

우리가 의도한대로 작동하지 않아도 에러가 나지 않는다는 것 !

+ Recent posts