🤔 더미 데이터가 왜 필요해?

상품 데이터는 쿠팡에서 크롤링해 왔지만 실서비스를 목적으로 하지 않는 우리 프로젝트에 유저 경우에는 더미 데이터로 진행하기로 했다.

랜덤으로 데이터를 생성해주는 서비스를 이용하여 집어 넣을 예정이었는데, 언어가 한글인 게 없어서 직접 코딩하기로!

 

간단히 파이썬으로 코딩

 


😃 더미 데이터 생성 코드

🪪 랜덤 이름

# 이름 생성 함수들
def firstName():
    first = ["김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", "한", "오", "서", "신", "권", "황", "안",
             "송", "류", "전", "홍", "고", "문", "양", "손", "배", "조", "백", "허", "유", "남", "심", "노", "정",
             "하", "곽", "성", "차", "주", "우", "구", "신", "임", "나", "전", "민", "유", "진", "지", "엄", "채",
             "원", "천", "방", "공", "강", "현", "함", "변", "염", "양", "변", "여", "추", "노", "도", "소", "신",
             "석", "선", "설", "마", "길", "주", "연", "방", "위", "표", "명", "기", "반", "왕", "금", "옥", "육",
             "인", "맹", "제", "모", "장", "남", "탁", "국", "여", "진", "어", "은", "편", "구", "용"]

    return random.choice(first)

def LastName():
    last =  ["가", "강", "건", "경", "고", "관", "광", "구", "규", "근", "기", "길", "나", "남", "노", "누", "다",
            "단", "달", "담", "대", "덕", "도", "동", "두", "라", "래", "로", "루", "리", "마", "만", "명", "무", "문", "미", "민", "바", "박",
            "백", "범", "별", "병", "보", "빛", "사", "산", "상", "새", "서", "석", "선", "설", "섭", "성", "세", "소", "솔", "수", "숙", "순",
            "숭", "슬", "승", "시", "신", "아", "안", "애", "엄", "여", "연", "영", "예", "오", "옥", "완", "요", "용", "우", "원", "월", "위",
            "유", "윤", "율", "으", "은", "의", "이", "익", "인", "일", "잎", "자", "잔", "장", "재", "전", "정", "제", "조", "종", "주", "준",
            "중", "지", "진", "찬", "창", "채", "천", "철", "초", "춘", "충", "치", "탐", "태", "택", "판", "하", "한", "해", "혁", "현", "형",
            "혜", "호", "홍", "화", "환", "회", "효", "훈", "휘", "희", "운", "모", "배", "부", "림", "봉", "혼", "황", "량", "린", "을", "비",
            "솜", "공", "면", "탁", "온", "디", "항", "후", "려", "균", "묵", "송", "욱", "휴", "언", "령", "섬", "들", "견", "추", "걸", "삼",
            "열", "웅", "분", "변", "양", "출", "타", "흥", "겸", "곤", "번", "식", "란", "더", "손", "술", "훔", "반", "빈", "실", "직", "흠",
            "흔", "악", "람", "뜸", "권", "복", "심", "헌", "엽", "학", "개", "롱", "평", "늘", "늬", "랑", "얀", "향", "울", "련"]

    return random.choice(last)

# 사용시
name = firstName() + LastName() + LastName()

 

📨 랜덤 주소

이번 더미데이터 생성의 핵심!

주소 같은 경우에는 어떻게 생성해야하나 고민했는 데 구글링 하다가 전국 건물 주소를 제공하는 서비스가 있었다.

이를 다운 받고 랜덤 줄을 받아와 파이프기준으로 list형태를 만들어 가공해 필요한 정보만 가져오는 식으로 정제하는 방법을 채택했다.

 

-원 정보 형태 (아래 사이트에서 전국 시도 기준으로 txt 파일을 제공하고 있다. 참고로 2018년 때의 주소!!! 주의!!!) 

 

주소정제전환서비스

주소를 입력하세요. 정제된 도로명주소 및 지번주소로 자동 변환

www.sujiewon.com

- 위 파일을 사용하여 주소 추출

def address():
    f_Ad = open('build_seoul.txt', 'r')
    random_line = random.choice(list(f_Ad.readlines())).splitlines()[0]
    f_Ad.close()
    return random_line

# 사용시
address_full = address()
address_list = address_full.split('|')

sido = address_list[1]
sigungu = address_list[2]
eupmyeondong = address_list[3]
street = address_list[9]
zipcode = address_list[19]

 

 

🪪 랜덤 아이디와 이메일

def loginId():
    n = random.randrange(5,10)
    loginId = ""
    for x in range(n):
        loginId += str(random.choice(string.ascii_lowercase))

    return loginId

 # 사용시
login_Id = loginId()
email = login_Id + "@google.com"

 

🔒 랜덤 패스워드

password_make = string.ascii_letters + string.digits

def password():
    lenNum = 64
    password = "";
    for y in range(64) :
        password += random.choice(password_make)

    return password

# 사용시
password()

 

📄 CSV파일로 변환

password_make = string.ascii_letters + string.digits
create_cnt = 1000

f_Save = open('random_member.csv', 'w', encoding='utf-8', newline='')
wr = csv.writer(f_Save)

for i in range(create_cnt) :
    name = firstName() + LastName() + LastName()
    address_full = address()
    address_list = address_full.split('|')

    login_Id = loginId()
    email = login_Id + "@google.com"

    sido = address_list[1]
    sigungu = address_list[2]
    eupmyeondong = address_list[3]
    street = address_list[9]
    zipcode = address_list[19]

    wr.writerow([sido, sigungu, eupmyeondong, street, zipcode, email, login_Id, name, password()])
    print([sido, sigungu, eupmyeondong, street, zipcode, email, login_Id, name, password()])

f_Save.close()

 

🎉 결과물

 

 

flask 라우팅 방법 중에 정적 라우팅, 동적 라우팅 두가지 방법이 있다.

정적 라우팅 방법은 아래와 같이 경로를 직접 설정해주는 방식으로 해당 url의 정확한 주소로 들어와야만 진행되는 방식.

(로컬에서 아래 경우라면 기본주소/login 이라는 url에서만 반응하는 방식)

@app.route("/login")

 

동적 라우팅은 변수등을 통해 동적으로 페이지를 반응하게 하는 방식.

(아래는 기본주소/ 뒤에 오는 값에 따라 동적으로 화면을 로딩하는 방식이다)

@app.route('/<path>')
def get_path(path):
    return render_template(path+'.html')

 

 

구현 테스트 

준비)

templates에 여러페이지 생성

index.html 파일에 버튼

app.py 코드

@app.route('/<path>')
def get_path(path):
    return render_template(path+'.html')

 

 

실행)

화면에서 A버튼을 누르게 되면 /A 로 라우팅되면서 

/(슬러쉬)뒤의 코드를 path 변수 값으로 인식해 아래코드의 모든 path값은 A가 된다

get_path 매개변수 path 값도 A, 따라서 아래 render_template함수 안의 path에도 A가 들어가게 되어

결론적으로 A.html 을 렌더하게 된다. 저 위에 B나 C도 같은 방식으로 이동하여 각각 B.html과 C.html을 렌더.

이처럼 url을 일부를 변수로서 활용하게 되면 여러가지 페이지를 연결해야할때 @app.route 데코레이션을 여러개 쓸 필요 없이 하나로 여러가지 페이지를 연결할 수 있다.

 

 

유튜브는 영상 하나의 페이지 등을 크롤링 할때는 requests와 BeautifulSoup만으로도 가능하지만

검색 내용 등의 동적페이지부분을 크롤링 할때 문제가 생긴다.

한화면에 내용이 다 보이지 않고 스크롤을 내리며 로딩을 해야 나머지 결과들이 나오기 때문

 

Selenium 패키지가 이런 동작을 대신할 수 있다.

Selenium은 웹 애플리케이션 테스트를 위한 포터블 프레임워크로 프로그래밍 언어들에서 테스트를 작성하기 위한 테스트 도메인 특화 언어(Selenese)를 제공한다. 이 테스트들은 현대의 대부분의 웹 브라우저에서 수행이 가능하다.

 

쉽게 말하자면 브라우저를 직접 동작시켜 실제 사용자 동작을 직접 시뮬레이션하는 기능을 가지고 있다.

 

그래서 우리가 크롤링할 때 대신 스크롤을 내려주어 더 많은 검색내용을 볼 수 있게 해준다.

 

Selenium을 사용하려면 크롬드라이버도 설치해줘야한다.

 

 


설치

 

1. 자신의 크롬 버전 확인

케밥메뉴(세로 3점 더보니 메뉴를 지칭) > 설정 > Chrome 정보 에서 버전을 확인

+ UI 아이콘 호칭

점 3개가 가로로 나란히 있는 아이콘 : 미트볼메뉴

점 3개가 세로로 나란히 있는 아이콘 : 케밥메뉴 

선 3개가 세로로 나란히 있는 아이콘 : 햄버거 메뉴

 


2.  Selenium 홈페이지에서 웹드라이버 다운받기

https://www.selenium.dev/

 

Selenium

Selenium automates browsers. That's it!

www.selenium.dev

내 크롬버전보다 아래긴 한데 가장 최근 버젼이라 다운

윈도우 유저 이므로 윈도우 버젼 선택해서 다운

 


3. 압축 풀어서 실행해 실행 확인

 


4. Selenium 설치

설정 > 프로젝트: 프로젝트명 > Python 인터프리터 > + > selenium 검색  > 선택 > 패키지 설치

 

 


5.  코드를 통해 실행해보기

크롬드라이버 파일 위치는 자신이 크롬드라이버를 위치시킨 위치에 맞춰 변경

from selenium import webdriver
browser = webdriver.Chrome(executable_path="../setting/chromedriver.exe")
url = "https://www.naver.com"
browser.get(url)

실행했을때 이 주소의 화면이 뜬다면 성공

 

다만, 이런 경고가 떴다. 해당 경고는 내 크롬버전이 크롬드라이버 보다 높을 경우에 뜬다고 하는데 실행은 된다.

매번 수동으로 버젼을 맞춰주는게 싫다면 아래방법을 참조하자.

아래는 크롬드라이버를 이용하는 것이 아닌 자신이 사용하는 크롬을 사용하는 방법

https://velog.io/@sangyeon217/deprecation-warning-executablepath-has-been-deprecated

 

DeprecationWarning: executable_path has been deprecated 해결하기

DeprecationWarning: executable_path has been deprecated 해결 방법에 대한 포스팅 입니다.

velog.io

 

 

 


 

셀레늄을 이용한 크롤링 구현

 

임포트 내용

import time
from selenium import webdriver
from selenium.common.exceptions import WebDriverException as WDE
from selenium.webdriver.common import keys
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import requests
from bs4 import BeautifulSoup

 

기초세팅

#----- 기본세팅 및 홈페이지 접속
browser = webdriver.Chrome(executable_path="../setting/chromedriver.exe")
url = "https://youtube.com" # 접속 url
test_keyword = '자바' # 검색할 키워드
finish_line = 1000 # 스크롤 내릴 기준
browser.get(url)
delay = 2 # 페이지 로딩을 위해
time.sleep(delay)

 

키워드 세팅

#----- 키워드 검색
search = browser.find_element(By.NAME, "search_query") # 이 아이디에 해당하는 걸 찾아
search.send_keys(test_keyword) # 키워드 넣고
search.send_keys(keys.Keys.ENTER) # 엔터키 쳐주기

present_url = browser.current_url
browser.get(present_url) # 작업창을 기본 유튭에서 검색한 창으로 교체

 

스크롤 작업

 #----- 스크롤작업 시작
last_page_height = browser.execute_script("return document.documentElement.scrollHeight")

while True:
    # 스크롤 내리기
    browser.execute_script("window.scrollTo(0, document.documentElement.scrollHeight);")
    time.sleep(delay)  # 작업 중간에 1이상으로 간격을 줘야 데이터 취득가능(스크롤을 내릴 때의 데이터 로딩 시간 때문)
    # 현재까지 내린 스크롤 위치 값
    new_page_height = browser.execute_script("return document.documentElement.scrollHeight")

    # 과거의 길이와 현재 위치 비교하기
    if new_page_height > finish_line: # 현재 위치값이 제한 길이 보다 크다면
        break # 반복 종료
    else: # 아니라면
        last_page_height = new_page_height # 지금 위치를 라스트 위치에 대입
        
 #----- 스크롤작업 종료 (뒤에서 더 쓸일 없음)

 

유튜브 제목과 video ID 추출

html = browser.page_source # 로드한 페이지의 소스를 가져옴
time.sleep(delay)

soup = BeautifulSoup(html, 'html.parser')
result = soup.select("#contents > ytd-video-renderer")

for one in result :
    title = one.select_one('#video-title')['title']
    href = one.select_one('#video-title')['href'].replace("/watch?v=","")
    print(title)
    print(href)

 

결과

 

 

? 셀레늄 실행시 브라우저가 실행되는데 이부분은 어쩔수 없는 부분인건지 

찾아보니 크롬창 없이 하는 옵션이 있음

https://minimin2.tistory.com/118

 

[Python] Selenium을 크롬 창 없이 실행하기(background에서 실행하기)

[Python] Selenium을 크롬 창 없이 실행하기(background에서 실행하기) 개요 지난 게시물에서 selenium을 사용하는 방법을 알아보았습니다. 보통 selenium으로 크롤링을 하거나 인터넷으로 하는 단순 반복 작

minimin2.tistory.com

 

 

 

 

 

 

 

 

 

 

 

 


+ 이 포스팅 작성시 참고한 사이트

Selenium 공식 문서 : 

https://selenium-python.readthedocs.io/index.html

 

Selenium with Python — Selenium Python Bindings 2 documentation

Note This is not an official documentation. If you would like to contribute to this documentation, you can fork this project in GitHub and send pull requests. You can also send your feedback to my email: baiju.m.mail AT gmail DOT com. So far 50+ community

selenium-python.readthedocs.io

크롬 드라이버 설치 방법

https://somjang.tistory.com/entry/WindowsWindows10%EC%97%90-Selenium%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0

 

[Windows]Windows10에 Selenium설치하기(20.2.13 업데이트)

1. 구글 크롬 최신으로 업데이트하기 먼저 크롬의 맨 우측 상단의 세 개의 점을 클릭하여 크롬의 설정페이지로 들어갑니다. 왼쪽 메뉴에서 Chrome 정보를 클릭하여 업데이트를 실시합니다. 다시시

somjang.tistory.com

Selenium 코드

https://data-ssung.tistory.com/129

 

[데이터 수집] selenium 유튜브 검색 결과 스크래핑

유튜브 검색 결과 스크래핑 이 글은 유튜브 검색 결과를 데이터로 수집하는 글입니다. 유튜브 검색 결과에서 컨텐츠 제목, url 등의 가져와서 csv 파일로 저장해보았습니다. 열심히 배우고 있는

data-ssung.tistory.com

 

 

 

Python 언어 - Flask / flask_jwt_extended 사용

 

 

항해 토이 프로젝트에 로그인 관련해서 매니저님이 언급해주신 JWT 토큰에 대해 구현 코드를 뜯어보고 공부해보자.

 

JWT란?

JSON포맷을 이용하여 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token이다. Token 자체를 정보로 사용하는 Self-Contained 방식으로 정보를 안전하게 전달.

 

* 클레임(Claim)은 주체가 수행할 수 있는 작업보다는 주체가 무엇인지를 표현하는 이름과 값의 쌍을 말한다.

예를 들어 신분증의 생년월일 이란 이름이 클레임의 이름이 되며 1970년 1월 1일이라는 값은 클레임의 값이 된다.

 

 

구조, 작동원리 및 장점 단점은 아래 포스팅이 잘 설명되어있다.

https://blog.naver.com/PostView.naver?blogId=dktmrorl&logNo=222410946965 

 

[IT정보] JSON 웹 토큰(JSON Web Token, JWT) 개념

JSON 웹 토큰(JSON Web Token, JWT)이란? JSON 웹 토큰(JSON Web Token, JWT)은...

blog.naver.com

 

 

+ jwt 토큰은 만료를 정해주지 않으면 그대로 남기 때문에 만료와 갱신에 신경써야한다 

Access_token 의 기간이 만료되면 Refresh_token을 가지고 Access_token을 재발급 받아야 한다.

그래서 토큰 시간이 만료되기 전에 프런트에서 refresh 요청을 보낸다

 

 

 

 

구현코드(BE)

 

임포트 내용 

from flask import Flask, request, jsonify, render_template
from flask_jwt_extended import (
    JWTManager, jwt_required, create_access_token, get_jwt_identity, unset_jwt_cookies, create_refresh_token,
    set_access_cookies, set_refresh_cookies, get_jwt)
    
# refresh 갱신 관련해 처리하려면 아래사항도 임포트
from datetime import timedelta, datetime, timezone

 

 

초반 세팅

#----------------------------------------------------------------------

# __name__ 변수는 모듈의 이름을 가진 변수로 실행하는 기준이 되는 .py 파일의 이름은 __main__에 해당
# 만약 app.py에서 another.py라는 모듈을 사용했다면 app.py의 __name__ == __main__ , 
# another.py의 __name__ == another가 된다.
# Flask(__name__) 라우팅 경로를 설정. 해당 라우팅 경로로 요청이 올 때 실행할 함수를 아래에 작성한다
app = Flask(__name__)


# jwt 가장 기초세팅 start ------------------------------------------------------------------------

app.config["JWT_COOKIE_SECURE"] = False # https를 통해서만 cookie가 갈수 있는지
app.config["JWT_TOKEN_LOCATION"] = ["cookies"] # 토큰을 어디서 찾을지 설정
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(hours=1) # 토큰 만료시간 설정 기본은 30분
app.config["JWT_SECRET_KEY"] = "직접 정의"  # 토큰 암호화에 이용되는 히든키로 직접 정의

jwt = JWTManager(app)

# jwt 가장 기초세팅 end ------------------------------------------------------------------------

 

로그인 

@app.route("/login", methods=["POST"])
def login():
    user = request.get_json()
    username = user['username']
    password = user['password']

    # 여기서 DB단 비교 부분이 들어가야함

    if username != "test" or password != "test":
        return jsonify({"msg": "Bad username or password"}), 401 # 접근불가 오류

    access_token = create_access_token(identity=username)  # jwt token 생성
    refresh_token = create_refresh_token(identity=username) # 갱신을 위한 refresh_token 생성
    # 이걸 리턴해서 어떻게 쓰는거지? > 쿠키에 저장한다

    response = jsonify({"login" : True})
    # response을 만든이유가 단지 메세지를 위한게 아니라 쿠키를 같이 보내야하기 때문에 만든듯
    # 쿠키에 토큰을 넣으면서 response 안에 jwt cookie를 넣어 리턴하려고
    set_access_cookies(response, access_token) 
    set_refresh_cookies(response, refresh_token)

    return response, 200 # 서버가 제대로 요청을 처리했다는 성공

 

유효성 검사

@app.route("/protected", methods=["GET"])
@jwt_required() # 토큰이 인정된 (접근권한이 인정된) 유저만이 이 API를 사용할 수 있다. 유효성 테스트
def protected():
    current_user = get_jwt_identity() # token으로부터 저장된 데이터를 불러온다
    return jsonify(logged_in_as=current_user), 200

 

갱신

@app.after_request
def refresh_expiring_jwts(response):
    try:
        exp_timestamp = get_jwt()["exp"]  # 만료시간을 찍는
        now = datetime.now(timezone.utc)  # 현재시간 출력하는데, 기준시는 협정 세계시
        target_timestamp = datetime.timestamp(now + timedelta(minutes=30)) # 지금 시간으로 부터 30분 추가한 대상시
        if target_timestamp > exp_timestamp: # 대상시가 만료시간보다 크면
            access_token = create_access_token(identity=get_jwt_identity()) # 다시 토큰을 갱신
            set_access_cookies(response, access_token) # 갱신한 토큰을 다시 쿠키에 저장
        return response # 갱신한 토큰이 담긴 쿠키가 있는 response를 반환
    except (RuntimeError, KeyError):
        return response # 유효하지 않은 jwt라 갱신하지 않고 오리지날을 반환

 

로그아웃

@app.route("/logout", methods=["GET"])
def logout():
    response = jsonify({"msg": "Logout Successful"})
    unset_jwt_cookies(response) # 쿠키 없애는
    return response

 

 

구현코드(FE)

 

로그인 : 꼭 JSON형태로 보낼필요는 없지만 서버단에서 json으로 받도록 코딩해놔서 JSON으로 전달

function login() {
    var data = {
        "username": $('#username').val(),
        "password": $('#password').val()
    }
    $.ajax({
        type: "POST",
        url: "/login",
        data: JSON.stringify(data),
        dataType: "json",
        success: function (response) {
            alert(response["login"]);
        }
    });

 

유효성 검사 사용할때는 서버단에 @jwt_required()가 있으면 된다.

function writeform(){
    $.ajax({
        type: "GET",
        url: "/protected",
        data: {},
        success: function (response) {
            console.log(response)
        }
    });
}

 

 

 

 

 

 


 

JWT에 대한 이해:

https://flask-jwt-extended.readthedocs.io/en/stable/

 

Flask-JWT-Extended’s Documentation — flask-jwt-extended 4.4.4 documentation

 

flask-jwt-extended.readthedocs.io

https://velog.io/@0617kim/%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-JWT-%ED%86%A0%ED%81%B0

 

로그인에 필요한 JWT 토큰

로그인을 하고 로그인 상태인지를 확인하기 위해 JWT토큰을 발급한다.Access_token 과 Refresh_token 2가지를 발급한다.Access_token은 토큰 만료기간이 짧고 json 형식으로 전달해줄 수 있다.Refresh_token은 보

velog.io

https://coding-hyeok.tistory.com/m/86

 

[Javascript] 인증과 인가, 암호화, jwt (flask, bcrypt)

회원가입과 로그인은 알면 알수록 짜릿하다. 실생활에서 겁나 아무생각 없이 쓰고 있는 별거 아닌 기능인 거 같지만 사용자의 정보가 들어가기 때문에 매우매우 중요한거 같다. 그래서 이번에

coding-hyeok.tistory.com

 

 

 

0. Django란?

파이썬 웹 프레임워크

* 프레임워크 (Framwork) : 원하는 기능 구현에만 집중하여 빠르게 개발 할 수 있도록 기본적으로 필요한 기능을 갖추고 있는 것

 


1. Open Folder

원하는 폴더를 불러오거나 생성 

- 믿을 수 있는 프로젝트인가

열때 프로젝트 신뢰도 여부 질문 

 

 


2. Django 설치

방법 1) pip를 통해  django를 설치

터미널을 통해 pip 명령어 입력으로 django 설치

+ pip는 파이썬(python)으로 작성된 패키지 소프트웨어를 설치 · 관리하는 패키지 관리 시스템

pip install django

설치 명령어 입력하면 아래처럼 설치 진행

 


방법 2)  PyCham의 메뉴를 통한 Django 설치

방법2) PyCham Setting을 이용한 패키지 설치

- File > Settings 

- Project: 프로젝트이름 > Python Interpreter > +

- 원하는 패키지 이름 검색 > 해당 패키지 선택 > Install Package 선택

- install 진행

- Successfully installed 설치 성공

 

 


Django 설치 완료

+ Recent posts