๐ก JSON vs XML ์ค JSON์ ์ ํํ ์ด์
- ๊ฐ๋
์ฑ๊ณผ ์ฌ์ฉ ํธ์์ฑ : JSON์ {key: value} ํ์์ด๋ผ ์ฌ๋์ด ์ฝ๊ณ ์ฐ๊ธฐ ์ฝ๊ณ , XML๋ณด๋ค ๊ตฌ์กฐ๊ฐ ๊ฐ๋จํจ, Python์์๋ dict๋ก ์ฝ๊ฒ ๋ณํํ ์ ์์ด ๋ค๋ฃจ๊ธฐ ํธ๋ฆฌํจ.
- ๋น ๋ฅธ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์๋ : JSON์ Python์ json.loads()๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ๋ก dict๋ก ๋ณํ๋จ โ ํ์ฑ ์๋๊ฐ ๋น ๋ฆ, XML์ ElementTree ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์จ์ผ ํ๊ณ , ํ๊ทธ๋ฅผ ํ์ฑํด์ผ ํ๋ฏ๋ก ๋ ๋๋ฆผ.
- ๋ฐ์ดํฐ ํฌ๊ธฐ ๋ฐ ๋คํธ์ํฌ ํจ์จ์ฑ : JSON์ ์์ด {} ํ์์ด๋ผ XML๋ณด๋ค ๋ฐ์ดํฐ ํฌ๊ธฐ๊ฐ ์์ โ ๋คํธ์ํฌ ์ ์ก ๋น์ฉ ์ ๊ฐ, ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ผ ๋ JSON์ด ๋ ๊ฐ๋ณ๊ธฐ ๋๋ฌธ์ API ์๋ต ์๋๊ฐ ๋ ๋น ๋ฅผ ์ ์์.
๐ ๋ค์ด๋ฒ ๊ฐ๋ฐ์์ผํฐ API ๋ฐ๊ธ
1. ๊ณต์ ์ฌ์ดํธ
2. key ๋ฐ๊ธ ์ ์ฒญ
- ๋ด์ค์ ๋ธ๋ก๊ทธ๋ '๊ฒ์' ์นดํ
๊ณ ๋ฆฌ์ด๊ณ , ํ๋ฃจ 25,000ํ ํธ์ถ์ด ๊ฐ๋ฅํ, ๋น๋ก๊ทธ์ธ ์คํ API ์
๋๋ค.

- ์ ์ค๋ช
์ ์ฐธ๊ณ ํ์ฌ API ๋ฐ๊ธ์ ์ํด์ ํ์ํ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค์์ต๋๋ค.
- http://localhost:3000 8080 ํฌํธ๋ airflow๊ฐ ์ฐจ์งํ์์ผ๋ฏ๋ก, ํํ ์ฐ์ด๋ 3000ํฌํธ๋ฅผ ์ง์ ํ์์ต๋๋ค.

3. key ๋ฐ๊ธ ์๋ฃ
- ์์ ๊ฐ์ด ๋ฐ๊ธ๋ฐ์ ํค๋ฅผ docker์ settings.py ํ์ผ์ ํ๋์ฝ๋ฉํ ์ง, ํน์ ๋ค๋ฅธ ๋ ๋์ ๋ฐฉ๋ฒ์ด ์๋์ง ๊ณ ๋ฏผํด ๋ณด๊ฒ ์ต๋๋ค.

CLIENT_ID = 'YOUR_CLIENT_ID' # ๋ค์ด๋ฒ ๊ฐ๋ฐ์ ์ผํฐ์์ ๋ฐ๊ธ๋ฐ์ ํด๋ผ์ด์ธํธ ์์ด๋
CLIENT_SECRET = 'YOUR_CLIENT_SECRET' # ๋ค์ด๋ฒ ๊ฐ๋ฐ์ ์ผํฐ์์ ๋ฐ๊ธ๋ฐ์ ํด๋ผ์ด์ธํธ ์ํฌ๋ฆฟ
๐ ์ํ ๋ฐ์ดํฐ ํ์ธํ๊ธฐ
1. ๋ค์ด๋ฒ ๊นํ๋ธ api ๋ช
์ธ์
- ๋ค์ด๋ฒ์ ๊ณต์ ๊นํ๋ธ์ ๋ค์ด๊ฐ๋ฉด api์ ํ๋ผ๋ฏธํฐ์ ์๋ต ์์๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
- ๋ด์ค : naver/naver-openapi-guide/ko/service-apis/search/news
- ๋ธ๋ก๊ทธ : naver-openapi-guide/ko/service-apis/search/blog/blog
- 25๋
3์ ๊ธฐ์ค ์ ๊ณต๋๋ ํ๋ผ๋ฏธํฐ๊ฐ ์ ์ด ์กฐ์์ ํ ์ ์๋ ๋ฐฉ๋ฒ์ด ๋ง์ง ์์ต๋๋ค. ๊ทธ๋๋ ํ๋ฃจ 25,000๋ฒ ํธ์ถ์ด ๊ฐ๋ฅํ๋ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ชจ์ ์๋ ์์ ๊ฒ ๊ฐ์ต๋๋ค.

- ๋ค์ด๋ฒ ๋ด์ค ๋ฐ ๋ธ๋ก๊ทธ api๋ ํน์ "ํค์๋"๋ฅผ ๊ฒ์ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๋ ๊ฒ์ด ํน์ง์
๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ ๋ ์ฅ๋ ์ ์น ์ง๋์ ํ๋ณด์๋ค์ ์ด๋ฆ์ ํค์๋๋ก ์ค์ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
2. ์ํ ์ฝ๋
- 25๋
๋ 3์ ๊ธฐ์ค ์์ ์ฝ๋๋ ๋ธ๋ก๊ทธ ๊ฒ์์ ๊ฒฝ์ฐ๋ง ์ ๊ณต๋์ด ์์ง๋ง, ๋ด์ค๋ ๊ฑฐ์ ๋์ผํ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ ์ ์์ต๋๋ค.
# ๋ค์ด๋ฒ ๊ฒ์ API ์์ - ๋ธ๋ก๊ทธ ๊ฒ์
import os
import sys
import urllib.request
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
encText = urllib.parse.quote("๊ฒ์ํ ๋จ์ด")
url = "https://openapi.naver.com/v1/search/blog?query=" + encText # JSON ๊ฒฐ๊ณผ
# url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # XML ๊ฒฐ๊ณผ
request = urllib.request.Request(url)
request.add_header("X-Naver-Client-Id",client_id)
request.add_header("X-Naver-Client-Secret",client_secret)
response = urllib.request.urlopen(request)
rescode = response.getcode()
if(rescode==200):
response_body = response.read()
print(response_body.decode('utf-8'))
else:
print("Error Code:" + rescode)
๐ PostgreSQL ํ
์ด๋ธ ์ฝ๋ ์ง๊ธฐ
1. ๋ด์ค ํ
์ด๋ธ
- ๋ด์ค ๋ฐ์ดํฐ ๋ฐํ ์ํ

- ๊ณ ๋ ค์ฌํญ 1 : original_link๊ฐ PK๊ฒฉ์ธ๋ฐ(๊ธฐ์ฌ๊ฐ ์ญ์ ๋๋ ๊ฒฝ์ฐ๋ ์๊ธฐ ๋๋ฌธ) ์ค๋ณต ๋ฐ์ดํฐ ๋ฐฉ์ง๋ฅผ ์ํด UNIQUE NOT NULL ์กฐ๊ฑด์ด ๋ค์ด๊ฐ์ผ ํ๋ค. ๊ทธ๋ฐ๋ฐ ์ํ์ ๋ณด๋ NULL๊ฐ์ด ์๋ค. ๊ทธ๋ ๋ค๋ฉด Transform ๊ณผ์ ์์ ํด๊ฒฐํ๋๋ก ํ์. ELT๊ฐ ์๋ ETL๋ก ๊ฐ์ผ ํ๋ค.
- ๊ณ ๋ ค์ฌํญ 2 : title์ ํ๋ณด์์ ์ด๋ฆ, ์ฆ ํค์๋๊ฐ ํฌํจ๋์ง ์์ ๊ฒฝ์ฐ๊ฐ ์๋ค. ์ด๊ฒ๋ Transform์์ ๊ฑธ๋ฌ์ผ๊ฒ ์ง..?
- ๊ณ ๋ ค์ฌํญ 3 : pubdate๋ ๋ด์ค ๋ฐํ์ผ์ธ๋ฐ, ํ์์ด ๋ง์ ๋ค์ง ์๋๋ค. yyyy-mm-dd ํ์์ผ๋ก ๋ฐ๊พธ๋๋ก ํ๋ค.
-- naver_news
CREATE TABLE IF NOT EXISTS naver_news (
kid SERIAL PRIMARY KEY,
keyword TEXT NOT NULL,
title TEXT NOT NULL,
original_link TEXT UNIQUE NOT NULL, -- ์ค๋ณต ๋ฐ์ดํฐ ๋ฐฉ์ง
naver_link TEXT UNIQUE NOT NULL,
description TEXT,
published_date DATE NOT NULL,
FOREIGN KEY (keyword) REFERENCES candidate_info(candidate_name) ON DELETE CASCADE
);
2. ๋ธ๋ก๊ทธ ํ
์ด๋ธ
- ๋ธ๋ก๊ทธ ๋ฐ์ดํฐ ๋ฐํ ์ํ

- ๊ณ ๋ ค์ฌํญ 1 : ๋ธ๋ก๊ทธ ๋ฐ์ดํฐ๋ ์ฒด๊ฐ์ ๋ด์ค๋ณด๋ค ์ ํ๋๊ฐ ๋ ๊ด์ฐฎ์ ๊ฒ ๊ฐ๋ค. ๋ด์ค๋ ์ฌ๋ฌ ์ ๋ฌธ์ฌ ์น์ฌ์ดํธ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์์ง๋ง ๋ธ๋ก๊ทธ๋ ๋ค์ด๋ฒ ์ค๋ฆฌ์ง๋ ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ ์ง๋ง ํน์ ๋ชฐ๋ผ title์ ํค์๋ ํฌํจ์ฌ๋ถ๋ฅผ ํ์ธํ๊ณ ์ ์ฌํ๋๋ก ํ๋ค.
- ๊ณ ๋ ค์ฌํญ 2 : ๋ธ๋ก๊ทธ ์ฌ์ฉ์๋ค์ด ์ด๋ชจ์ง๋ฅผ ์จ์ ์๋ฃํ์ด TEXT์ธ ์ปฌ๋ผ์ ์ด๋ชจ์ง๊ฐ ๋ค์ด๊ฐ๋ค. GPT์๊ฒ ๋ฌผ์ด๋ณธ ๊ฒฐ๊ณผ PostgreSQL์ TEXT ์๋ฃํ์ UTF-8์ ๊ธฐ๋ณธ ์ธ์ฝ๋ฉ์ผ๋ก ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ด๋ชจํฐ์ฝ์ ์ ์์ ์ผ๋ก ์ ์ฅํ ์ ์๋ค๊ณ ํ๋ค. ๋คํ
- ๊ณ ๋ ค์ฌํญ 3 : ๋ ์ง ํ์
-- naver_blog
CREATE TABLE IF NOT EXISTS naver_blog (
kid SERIAL PRIMARY KEY,
keyword TEXT NOT NULL,
title TEXT NOT NULL,
link TEXT UNIQUE NOT NULL, -- ์ค๋ณต ๋ฐ์ดํฐ ๋ฐฉ์ง
description TEXT,
blogger_name TEXT,
blogger_link TEXT,
post_date DATE NOT NULL,
FOREIGN KEY (keyword) REFERENCES candidate_info(candidate_name) ON DELETE CASCADE
);