3주차 필기록 통합본

김다피·2026년 1월 30일

SKN-25 필기본

목록 보기
3/14

Day1

🟢Database

🔵R(관계형)DBMS

→ MySQL

⇒ SQL(구조적 절차 언어)

→ DDL(Data Definition Language)

  • CREATE, ALTER,DROP

→ DML(Data Manipulation Language)

  • SELECT, INSERT, UPDATE,DELETE

→ DCL(Data Control Language)

  • GRANT,

⇒ DBA(DB 전문가)

→ 데이터 아키텍쳐 전문가.(도메인을 알아야함)

  • CRM(Customer Relationship Management)

    • 유명한 회사 → Salesforce(대표제품 : slack)
  • 요새는 통계학과도 sql 배우는 추세

🟢다시 wsl을 켠다…

wsl --shutdown 
  • 메모리 너무 많이 잡아먹을 때 죽인다……..

🔵 Daemon

  • 백그라운드에서 돌아가고 있는 프로그램
  • 작업관리자에서 서비스라는 탭에 떠있음
sudo service mysql status

🔵 시작 및 로그인

  • sudo는 root 권한 빌려오는 것
  • service → 백그라운드
sudo service mysql start/stop 등 가능
rosie@ming9:~$ mysql -uplay -p000 #임시

🔵 Ubuntu 환경에서 DB를 다루기

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| encore             |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)
mysql> use encore;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+------------------+
| Tables_in_encore |
+------------------+
| hotel_ratings    |
| members          |
| onnuri           |
+------------------+
3 rows in set (0.01 sec)

ANSI SQL → 표준의 SQL

IEEE → 세계적인 기술 단체, 인터넷 프로토콜

  • 공유기 살 때는 프로토콜 보고 사야함.

IEEE 754 paper → 소수점에 대한 정의를 내림.

🟢 SQL query 사용해보기

SELECT  * 
from hotel_ratings hr
where hr.final_rating in ('5성','4성')
order by final_rating desc, hr.region desc;

SELECT  count(*)
from hotel_ratings hr
where hr.final_rating in ('5성','4성')
order by final_rating desc, hr.region desc; 
--148

🔵문자열 함수

  • left, right, substring, concat
SELECT left(hr.final_rating ,1)
from hotel_ratings hr ;

SELECT concat(substring(hr.final_rating ,1,1),"성급이다")
from hotel_ratings hr ;

SELECT cast(substring(hr.final_rating ,1,1) as signed) as rating
from hotel_ratings hr ;

select  * from
(select hotel_name, cast(substring(hr.final_rating ,1,1) as signed) as rating_int
from hotel_ratings hr) hr
where rating_int between 2 and 4;

  • 서브쿼리 활용
select region, count(*) cnt
from hotel_ratings
group by region
order by cnt desc;

select region,final_rating, count(*) cnt
from hotel_ratings
group by region,final_rating
order by cnt desc, final_rating;

select region,final_rating, count(*) cnt
from hotel_ratings
group by region,final_rating
having cnt > 30
order by cnt desc, final_rating;

  • having ⇒ 집계 값에 대해 조건을 걸 때
  • where ⇒ 테이블에 대해 조건을 걸 때
select region, count(*) cnt
from hotel_ratings
group by region
having cnt > 50
order by cnt desc;
  • group by, having , order by ⇒ 순서 유의

🔵char vs varchar

  • char가 고정값이어서 처리 속도는 훨씬 빠름
  • 그러나 메모리는 varchar이 덜 잡아먹음
CREATE TABLE SK25(
	id int PRIMARY key ,
	name varchar(10) not null,
	phone varchar(30) null,
	age tinyint not null,
	gender varchar(5));
	
desc SK25;

구조 보기 위해서 그저 desc SK25; 사용

ALTER TABLE SK25 MODIFY GENDER VARCHAR(5) NOT NULL;
  • DDL 중 ALTER 사용하여 NULL 허용에서 NOT NULL 수정
-- 히히 어케 알았지
INSERT INTO SK25 VALUES(1,'김다피','010-0000-0000','20','남성');

update Sk25 s 
set age = 40
where id =1;

🟢 수업에 활용할 데이터 크롤링

🔵html 태그

  • 테이블 태그
import requests 
from bs4 import BeautifulSoup
import pandas as pd 
import io

url = "https://www.koreabaseball.com/Record/Player/HitterDetail/Basic.aspx?playerId=69100"

r = requests.get(url)
pd.read_html(io.StringIO(r.text))[0]
팀명AVGGPAABRH2B3BHRTBRBISBCSSACSF
0LG0.286131397343419816211213810512
  • 추후 파이썬이 크롤링을 진행할 때 멀티 프로세싱을 진행시키면 속도가 빨라질 수 있음

🟢 데이터 DB에 넣기

🔵외래키 설정하는 핵심적인 이유

  • 관계형 데이터베이스의 무결성 보장

🔵데이터베이스 정규화

  • 데이터베이스 잘 쪼개두는 것.
ALTER TABLE profile ADD bmi float(4,2);
  • 데이터 컬럼 추가

  • 데이터프레임 출력 시 보여주는 것은 그저 뷰에 불과함

  • 그래서 복사해서 사용하지 않으면 일이 복잡해짐

pd.merge(df,tmp2,left_index=True, right_index=True,how='right')
  • sql에서의 join = 판다스의 merge

🔵트랜잭션

  • 하나의 논리적인 작업 단위
  • 은행에서의 계좌 이체는 대표적인 예시
  • 오토커밋이 자동인 소프트웨어도 존재
df2.drop(['신장/체중',],axis=1,inplace=True)

확정적으로 수정됨

DBeaver랑 파이썬이 동시에 접근하면 둘 중 하나가 기다리고 있음

conn.close()
trans_dict = {'선수명' : 'NAME',
'등번호' : 'NUMBER',
'생년월일' : 'BIRTH_DAY',
'포지션' : 'POSITION',
'경력' : 'CAREER', 
'입단 계약금' : 'SIGNING_BONUS',
'연봉' : 'SALARY',
'지명순위' : 'DRAFT_PICK', 
'입단년도' : 'JOIN_YEAR',
'Team' : 'TEAM', 
'id' : 'ID'}

df2.rename(columns=trans_dict)
  • 컬럼의 특정 부분을 변경하고 싶을 때 딕셔너리 선언해서 변형하면 편함
df2.rename(columns=trans_dict,inplace=True)
  • 코드 확정이다 싶으면 inplace = true하기
df2.BIRTH_DAY.apply(lambda x : '-'.join(p.findall(x)))
  • 숫자만 뽑아내는 코드
df2["SALARY"] = df2["SALARY"].apply(
    lambda x: int(x.replace("만원", ""))
    if x[-2:] == "만원"
    else int(x.replace("달러", "")) * 1469
).copy()
  • 단위 다른 컬럼 통일시키기

ALTER TABLE profile 
ADD CONSTRAINT fk_key
FOREIGN KEY (id) REFERENCES pitcher_record (ID)
ON DELETE RESTRICT ON UPDATE CASCADE ;
cursor.execute("truncate table profile")
conn.commit()

  • truncate : 구조는 두고 내용만 삭제

🔵정규표현식

  • re ⇒ regex.
    • 컴퓨터 전체에서 보통 사용 가능하다.
import re
p=re.compile('[0-9]+')
"-".join(p.findall('2025년 12월 31일')) # 예시
  • 날짜로 형변환
df.생년월일 = df.생년월일.apply(lambda x: "-".join(p.findall(x)))
df['신장/체중'].str.split('/',expand=True)
  • 코드 쪼개기
pd.concat([df,df['신장/체중'].str.split('/',expand=True)],axis=1)
  • concat : 길이만 같으면 옆으로 붙일 수 있다.
df.rename(columns={0:'HEIGHT',1:'WEIGHT'},inplace=True)
df.drop(['신장/체중'],axis=1,inplace=True)
df[df.연봉.apply(lambda x : x!='')& df.HEIGHT.apply(lambda x: len(x)>2)]
  • and or 비트 연산에 대해 기억하기

Day2

🟢SQL 쿼리로 데이터 추출

SELECT TEAM, AVG(SALARY) AS 평균연봉, AVG(HEIGHT) AS 평균키, AVG(WEIGHT) AS 평균체중, AVG(BMI) AS 평균연봉 
FROM profile p 
GROUP BY TEAM;

DESC profile;

SELECT *
FROM profile p 
WHERE p.SALARY =300000;
SELECT *
FROM profile p
WHERE TEAM LIKE '%SSG%'
ORDER BY p.SALARY DESC;

🔵순서 출력하기

  • RANK OVER, PARTITION BY
SELECT NAME , POSITION, 
RANK() OVER (PARTITION BY TEAM ORDER BY SALARY DESC) AS SAL_RANK
FROM profile
WHERE TEAM LIKE '%SSG%';


SELECT NAME , POSITION, TEAM,
RANK() OVER (PARTITION BY TEAM ORDER BY SALARY DESC) AS SAL_RANK
FROM profile;
SELECT NAME , POSITION, SALARY, TEAM,
RANK() OVER (ORDER BY SALARY DESC) AS SAL_RANK
FROM profile;
  • SAL_RANK → 공동순위가 다 포함

DENSE_RANK OVER

SELECT NAME , POSITION, SALARY, TEAM,
DENSE_RANK() OVER (ORDER BY SALARY DESC) AS SAL_RANK
FROM profile;

ROW_NUMBER

SELECT NAME , POSITION, SALARY, TEAM,
ROW_NUMBER() OVER (ORDER BY SALARY DESC) AS SAL_RANK
FROM profile;

  • 공동순위없이 그냥 일렬종대로.

DENSE_RANK OVER PARTITION BY

SELECT NAME , POSITION, SALARY, TEAM,
DENSE_RANK() OVER (PARTITION BY TEAM ORDER BY SALARY DESC) AS SAL_RANK
FROM profile;

SELECT *
FROM 
(SELECT NAME , POSITION, SALARY, TEAM,
DENSE_RANK() OVER (PARTITION BY TEAM ORDER BY SALARY DESC) AS SAL_RANK
FROM profile ) AS TEMP

  • 서브쿼리 돌리면 대신 시간이 오래 걸릴 수 있음

SELECT * 함부로 쓰면 재판을 받을 수 있음

WITH TEMP AS (
SELECT NAME , POSITION, SALARY, TEAM,
DENSE_RANK() OVER (PARTITION BY TEAM ORDER BY SALARY DESC) AS SAL_RANK
FROM profile p 
)
SELECT * FROM TEMP
WHERE SAL_RANK =1;
  • CTE : Common Table Expression
  • Mysql 8.0 이상

🟢DB에 진짜 데이터 넣기

import MySQLdb

conn = MySQLdb.connect(host='myserver', user='play', passwd='123', db='encore')
cursor  = conn.cursor()

cursor.execute("SELECT id, NAME, position FROM profile")
rt = cursor.fetchall()
##투수용
pitcher = "https://www.koreabaseball.com/Record/Player/PitcherDetail/Basic.aspx?playerId={}"

##타자용
hitter = "https://www.koreabaseball.com/Record/Player/HitterDetail/Basic.aspx?playerId={}"
import requests 
from bs4 import BeautifulSoup
import pandas as pd 
import io

for id_,name_ , position_ in rt:
    print(id_,name_ , position_)
    if position_[:2]=='투수':
        r = requests.get(pitcher.format(id_))
    else:
        r = requests.get(hitter.format(id_))
    break
df= pd.concat([pd.read_html(io.StringIO(r.text))[0],pd.read_html(io.StringIO(r.text))[1]],axis=1)

sql = 'INSERT INTO hitter_record VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'

import numpy as np
cursor.execute(sql, [id_, name_, team_] + df.replace("기록이 없습니다.", None).iloc[0].tolist()[1:])
#df.replace("기록이 없습니다.", np.nan)

conn.commit()
  • mysql 2006 에러 → 연결 에러.

` ← 원래는 테이블 이름에 넣어줘야 했는데 변경됨.

DBA 관심 있는 사람들 경우 InnoDB에 대해 알 필요가 있음.

InnoDB는 mysql의 엔진 중 하나.

🔵크롤링 확정 코드

import requests 
from bs4 import BeautifulSoup
import pandas as pd 
import io
import tqdm

##투수용
pitcher = "https://www.koreabaseball.com/Record/Player/PitcherDetail/Basic.aspx?playerId={}"
##타자용
hitter = "https://www.koreabaseball.com/Record/Player/HitterDetail/Basic.aspx?playerId={}"

sql_hitter = 'INSERT INTO hitter VALUES(%s, %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
sql_pitcher = 'INSERT INTO pitcher VALUES(%s,  %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'

import requests 
from bs4 import BeautifulSoup
import pandas as pd 
from tqdm import tqdm

for id_, name_, position_, team_ in tqdm(rt):
    print(id_, name_, position_, team_)
    if position_[:2] == '투수':
        r = requests.get(pitcher.format(id_))
        sql = sql_pitcher
    else:
        r = requests.get(hitter.format(id_))
        sql = sql_hitter
    df = pd.concat([pd.read_html(io.StringIO(r.text))[0], pd.read_html(io.StringIO(r.text))[1]],axis=1 )
    try:
        cursor.execute(sql, [id_] + df.replace("기록이 없습니다.", None).replace("-", None).iloc[0].tolist()[1:])
    except Exception as e:
        print(e)
        

🔵DB 저장

rosie@ming9:~$ mysqldump -uplay -p123 encore > ~/encore.sql

🔵DB 복원

mysql -uplay -p123 encore < ~/encore.sql

~ 과 <~ 는 운영체제 공통 명령어

🟢이제 조인을 하자…

select *
from profile p 
inner join pitcher h
on p.id = h.id
order by h.w desc, h.era;
select p.name, p.SALARY , p.team, h.w, h.era
from profile p 
inner join pitcher h
on p.id = h.id
order by h.w desc, h.era;
select p.name, p.SALARY , p.team, p.BIRTH_DAY , p.`POSITION` , h.w
from profile p
LEFT  JOIN pitcher h
ON p.id = h.id
  • profile이 왼쪽.
with temp as (select p.name, p.SALARY , p.team, p.BIRTH_DAY , p.`POSITION` , h.ops
from profile p
inner  JOIN hitter h
ON p.id = h.id)
select team, avg(ops) avg_ops from temp
group by team 
order by avg_ops desc;

  • 널 값은 알아서 빼고 연산.

  • 지금 하고 있는 sql 연산들 pd.merge로 연산할 수 있다.

  • 조인은 이너 조인, 아우터 조인, 레프트 조인. 등등이 있다.

🟢인덱스 개념 및 필요성

  • 인덱스는 보통 binary tree로 구성된다.
  • 이진 탐색의 개념.

🔵이진트리

  • 항상 자식의 각 노드가 0,1,2
  • Decison tree. ⇒ 설명하기가 쉬움
  • 비선형 자료구조를 활용하여 머신러닝에서 활용함.
  • Random forest → decision tree가 모인 모델.

🔵이진탐색트리

  • 자료구조
  • 전공면접에 중요할 수 있음
create index idx_product_name on onnuri (name);

🔵

  • 민감성 정보 다룰 때 유용
create view my_kbo as(
select * from profile p where p.salary >100000
)

Day3

🟢API 기반 크롤링 이해

pip install fastapi  uvicorn

api를 활용할 예정인 것으로 보인다…

from fastapi import FastAPI

app = FastAPI()## 인스턴스 하나 생성

# 데코레이터
@app.get("/encore") #주소
async def get_data(): #비동기 
    return {'test': 'ㅋㅋㅋㅋ'}

실행메시지 → python -m uvicorn api:app --reload

🔵동기 vs. 비동기

우선 차이점부터 설명하자면, 동기는 '직렬적'으로 작동하는 방식이고 비동기는 '병렬적'으로 작동하는 방식이다. 즉, 비동기란 특정 코드가 끝날때 까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 것을 의미한다. 비동기 처리를 예로 Web API, Ajax, setTimeout 등이 있다.

  • 비동기

    • 요청할 때마다 백그라운드에서 실행시키는 것.
    • Ajax.
  • GET

  • Restful API

from fastapi import FastAPI
import MySQLdb

conn = MySQLdb.connect(host='myserver', user='play', passwd='123', db='encore',
                      autocommit=True)
cursor  = conn.cursor()

app = FastAPI()## 인스턴스 하나 생성

# 데코레이터
@app.get("/sk25")
async def get_data(id_): #비동기 
    sql =f"select * from profile where id = {id_}"
    cursor.execute(sql)
    rt =cursor.fetchall()
    return {'player': rt[0]}

  • api를 알면 굳이 DB를 몰라도 됨.
    • api한테 호출해서 정보값 받아내면 됨.
  • kafka 사용?

🟢API가 데이터를 요청하는 법

🔵get, post

get

  • 주소창에 담아보내는 방식
    • get은 보안이 취약하다.

post

  • 주소창이 아니라 그냥 담아서 보내는 방식
  • 대량의 데이터도 전달 가능
POST 방식의 요청은 캐시 되지 않습니다.
POST 방식의 요청은 브라우저 히스토리에 남지 않습니다.
POST 방식은 서버의 값이나 상태를 바꾸기 위해 활용
GET은 CRUD 기능 중에 C-Create(생성)의 역할

INFO:     127.0.0.1:57845 - "GET /sk25/?id_=50054 HTTP/1.1" 307 Temporary Redirect
INFO:     127.0.0.1:57845 - "GET /sk25?id_=50054 HTTP/1.1" 200 OK
INFO:     127.0.0.1:53513 - "GET /sk25?id_=50054 HTTP/1.1" 200 OK
@app.get("/sk25") # 데코레이터
async def get_data(id_): #비동기 
    sql =f"select * from profile where id = {id_}"
    cursor.execute(sql)
    rt =cursor.fetchall()

    if len(rt)==0:
        return {'player':'정보없음'}
    else:
        return {'player': rt[0]}
  • id가 없는 값이면 정보 없다고 뜨게 하기.
r =requests.get('http://127.0.0.1:8000/sk25?id_=5004')
r.text
  • '{"player":"정보없음"}’
r.json()

🔵내 서버를 크롤링하기.

total=[]
for x in range(50000,50101):
    r =requests.get(f'http://127.0.0.1:8000/sk25?id_={x}').json()['player']
    if type(r) ==str:
        continue
    total.append(r)

🔵F12 → 개발자도구

  • network 탭 접근

  • 새로고침 시 밑에 정보가 잡히게 됨.
  • 내가 찾고싶은 정보가 get이면 request_url 보면 됨

Network → Fetch/XHR → name 중 day 선택하거나 filter에 검색

JSON 형식의 데이터를 요청하는 방식으로 JavaScript의 Fetch API와 AJAX 요청을 생성하는 XHR(XMLHttpRequest)를 사용

  • 크롤링 시 접근할 수 있는 url 주소

Status Code

  • 200 - ok
  • 404 - page not found
    • 진짜 권한이 없거나 페이지가 없거나
  • 5로 시작
    • 사이트 내부에 문제가 있어서 못 줌
r= requests.get('https://www.melon.com/chart/index.htm')

<Response [406]>

head 없을 때 접근이 안될 가능성 농후

head={
    'user-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
}
r= requests.get('https://www.melon.com/chart/index.htm',headers= head)

<Response [200]>

  • head를 일반 크롬처럼 접근하여 열기.

🔵bs4 기반 파싱

from bs4 import BeautifulSoup

bs = BeautifulSoup(r.text)

이쁘게 담아봅시다….


와…이쁘다…

div → 태그

class → 반

id → 본인 이름

bs.find("div", class_ ='service_list_song type02 d_song_list').find_all('tr',class_='lst50')

🔵 post 방식

  • 포스트는 주소에 보낼 수 없다.
  • 코드로 post로 전달해야한다.
  • get 같은 경우 코드에서 받을 수 있지만 post는 network 탭에서 해야한다.

지역을 바꾸어도 url이 변경되지 않음

  • post로 데이터 전달되는 상황

SSG

  • SM (system management)
  • SI (system integration)

https://databoom.tistory.com/entry/FastAPI-get-post

저거 vscode에 복사해둔 후
ctrl f → & 후 엔터 후 엔터

r= requests.post("https://www.starbucks.co.kr/store/getStore.do?r=N7GEEZLV6B",data=payload)
df=pd.DataFrame(r.json()['list'])

tmp = """<ul class="sido_arae_box"><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="01">서울</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="08">경기</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="02">광주</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="03">대구</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="04">대전</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="05">부산</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="06">울산</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="07">인천</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="09">강원</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="10">경남</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="11">경북</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="12">전남</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="13">전북</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="14">충남</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="15">충북</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="16">제주</a></li><li><a href="javascript:void(0);" class="set_sido_cd_btn" data-sidocd="17">세종</a></li></ul>"""

location={x.text : x['data-sidocd'] for x in BeautifulSoup(tmp).find_all('a') }
total=[]
for x in {x.text : x['data-sidocd'] for x in BeautifulSoup(tmp).find_all('a') }.values():
    payload['p_sido_cd']=x
    r = requests.post('https://www.starbucks.co.kr/store/getStore.do?r=CMS3FGABXL', data=payload)
    df=pd.DataFrame((r.json()['list']))
    total.append(df)
starbucks_df=pd.concat(total,ignore_index=True)

from urllib import request
import os 
if  not os.path.isdir("./star"):
    os.mkdir("./star")
for idx, x in starbucks_df[['s_name','defaultimage']].iterrows():
    if '서초' in x.s_name :
        try:
            # print(f'{x.s_name} -> https://www.starbucks.co.kr{x['defaultimage']}')
            request.urlretrieve(f"https://www.starbucks.co.kr{x['defaultimage']}", f"./star/{x.s_name}.jpg")
        except:
            pass

🔵메가커피


url = "https://www.mega-mgccoffee.com/store/find/store.php?sigungu=%EA%B0%95%EB%82%A8%EA%B5%AC"

from urllib import parse
parse.unquote(url)
mega_url ="https://www.mega-mgccoffee.com/store/find/store.php?"
maga_payload={'sigungu':"서초구"}

requests.post(mega_url,data =maga_payload).json()['positions']

Day4

🟢Network Session

  • 세션이 저장되는 것 ⇒ 쿠키
  • 쿠키는 어디에 쓰이는가
    • ADTech - 내가 검색해봤던 키워드가 쿠팡에 뜸
    • 타겟 마케팅

🔵크롤링에서의 보안 이슈

  • 보안 이슈를 해결하기 위해 csrf 토큰 정보를 삽입하여 url에 접속한다
head = {'user-agent' : 
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36'}

p = re.compile('value="([0-9a-zA-Z-]+)"')

with requests.session() as s :
    r = s.get('https://gs25.gsretail.com/gscvs/ko/store-services/locations#;')
    tmp = BeautifulSoup(r.text).find('form', id='CSRFForm')
    # print(p.findall(str(tmp)))
    ## 보안 이슈를 해결하기 위해 csrf 토큰 정보를 삽입하여 url에 접속한다
    csrf = p.findall(str(tmp))[0]
    url = "https://gs25.gsretail.com/gscvs/ko/store-services/locationList?CSRFToken={}".format(csrf)
    r2 = s.post(url, data=payload, headers=head)
    print(r2.status_code)
    print(r2.json())

부산시 클릭할 때 관련 정보가 오른쪽 바가 변경되어야함

for x in data.find_all('option')[1:]:
    print(x['value'],x.text)

  • 이벤트를 주면 관련된 값을 network 탭 중 response에서 볼 수 있음
list(set([y for x in gs25_service.offeringService.dropna()  for y in x]))
  • dropna() → 중복제거 코드
  • 이중 리스트 컴프리헨션
  • 위 코드는 중복 제거 코드(압축 너무 심한 거 아니야?@)
pd.merge(gs25_service,pd.DataFrame(columns=service_list),left_index=True,right_index=True,how="left")

🔵녹이기

gs_melt=pd.melt(gs25_service2,id_vars=['shopCode'])
  • 기둥 하나 기준으로 두고 녹이기.

🔵피벗으로 복구하기

gs_melt.pivot(index='shopCode',columns='variable',values='value').reset_index()

🟢ORM

  • python - sqlalchemy → 코드로 DB만지기.

🔵SQLalchemy

import MySQLdb

conn = MySQLdb.connect(host='myserver', user='play', passwd='123', db='encore', autocommit = True)
cursor  = conn.cursor()
import sqlalchemy
from urllib import parse

user = 'play'
password = '123'
host='myserver'
port = 3306
database = 'encore'
password = parse.quote_plus(password)
engine = sqlalchemy.create_engine(f"mysql://{user}:{password}@{host}:{port}/{database}")

try:
    with engine.connect() as connection:
        print("Database connection successful!")
except Exception as e:
    print(f"Database connection failed: {e}")

gs25.to_sql('gs25',  index=False , if_exists='replace', con=engine)
select gs.shopCode, g.shopName , g.address ,gs.variable 
from gs25_service gs
inner join gs25 g 
on gs.shopCode = g.shopCode 
where g.address like "%서초%" and gs.variable ='parcel_service';

세션 정보를 받아와서 접속하면 속음

로그인 정보를 세션한테 넘겨주면 빠르다

profile
지루하게 선명하기보다는 흐릿해도 흥미롭게

0개의 댓글