[PostGRES + Python] 데이터베이스에서 리스트 불러와 웹크롤링
이번에 해야할 태스크는 크게
1. PostGRES 에서 Third Place 정보를 불러온다
2. Python 으로 Third Place 를 검색한 NAVER API 결과물을 JSON 형태로 얻는다
3. 기존 검색 결과와 일치 여부를 확인한다
4. 데이터베이스에 저장한다
로 이루어져있다. 여기서 고민해야할 부분은 3번과 4번.
JSON 에서 정보를 추출하는 건 python 이 편할 것 같지만,
추출한 정보와 기존 정보의 비교 및 저장을
Python - csv 로 할지, Python - Postgres - Database 의 형태로 할지가 고민이다.
내가 편하고 다루기 좋은 건 csv지만, 용량이 너무 아깝고, 시간도 느리다.
Database 는 부가적인 작업이 필요 없지만 업데이트가 까다로운 경우가 있다.
그래, 기왕 하는 거, Database로 바로 보내자.
일단 PostGRES 에서 테이블을 가져오자.
import psycopg2 as psql
import pandas as pd
from sqlalchemy import create_engine
conn = psql.connect(host = '누가불렀어요', port = 5432, user = '아이디뭐에요', password = '비밀번호뭐에요', database = '데이터베이스이름뭐에요')
#psql의 conn 은 pandas 의 데이터프레임과 호환이 편하다.
#engine = create_engine('postgresql+psycopg2://아이디뭐에요:비밀번호뭐에요@누가불렀어요:5432/데이터베이스이름뭐에요')
#sqlalchemy의 engine 은 csv 를 데이터베이스에 import 할 때 사용하기 편하다.
df = pd.read_sql('SELECT * FROM crawl_permit_place', conn)
여기에서 10개의 음식점의 이름을 샘플로 해서 NAVER API 검색 결과가 어떻게 나오는지 테스트해보자
import os
import sys
import urllib.request
client_id = "아이디가뭐에요"
client_secret = "비밀번호뭐에요"
sample = df[:10] #10개만 해보자
jsons = []
for s in range(len(sample)):
title = sample.loc[s]['title']
encText = urllib.parse.quote(title) #title만 따와보자
print(title)
url = "https://openapi.naver.com/v1/search/local.json?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'))
jsons.append(response_body.decode('utf-8')) #utf-8로 디코딩한 json 을 리스트로
else:
print("Error Code:" + rescode)
뭔가 억울하다. 진작 이렇게 할걸.
그리고 제일 중요한, 여기까지 했을 때 사용된 데이터 양의 통계
수상하다... 일단 지금까지는 0개인데... 두고봐보자....
그리고 저 json 중 나에게 필요한 정보
title, category, roadAddress, mapx, mapy 정도만 있으면 되겠다.
import json
js = json.loads(jsons[0].decode('utf-8')) #json.loads 는 string 을 json 으로 바꿔준다
print(js)
print(js['items'][0]['title'], #item 은 list 다. list의 index를 찾아준 이후에 'title' 다는 걸 잊지말자
js['items'][0]['category'],
js['items'][0]['roadAddress'],
js['items'][0]['mapx'],
js['items'][0]['mapy'])
간단한 크롤링 및 csv 저장 코드를 만들어놓고 집에 가도 될 것 같다.
import psycopg2 as psql
import pandas as pd
from sqlalchemy import create_engine
import os
import sys
import urllib.request
import json
############### Postgres 연동 및 불러오기
conn = psql.connect(host = '누가불렀니', port = 5432, user = '이름이뭐니', password = '비밀번호가뭐니', database = '데이터베이스이름이뭐니')
#engine = create_engine('postgresql+psycopg2://이름이뭐니:비밀번호가뭐니@누가불렀니:5432/데이터베이스이름이뭐니')
df = pd.read_sql('SELECT * FROM crawl_permit_place', conn)
############### 본격적인 크롤링 시작
client_id = "이름이뭐니"
client_secret = "비번이뭐니"
for i in range(10):
sample = df[i*1000:(i+1)*1000] #10,000개까지, 1,000개 단위로 불러오겠다
jsons = []
columns = ['pmid', 'p_title', 'p_addr', 'n_title', 'n_addr', 'n_category', 'n_x', 'n_y']
df1000 = pd.DataFrame(columns = columns) #비어있는 데이터프레임을 만들고
for s in range(i*1000,(i+1)*1000):
title = sample.loc[s]['title'] #일단 이름으로만 검색하겠다
encText = urllib.parse.quote(title)
url = "https://openapi.naver.com/v1/search/local.json?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'))
js = json.loads(response_body.decode('utf-8'))
q_total = js['total']
new_rows = []
if len(js['items']) > 0: #검색결과가 없는 녀석은 그냥 넘어가겠다. 나중에 다른 알고리즘으로 또 찾아보면 된다
for q in range(q_total): #일부 다른 검색 결과들이 같이 나와도 일단 다 데려와보자
new_row = {'pmid': sample.loc[s]['pmid'],
'p_title': sample.loc[s]['title'],
'p_addr': sample.loc[s]['addr'],
'n_title': js['items'][q]['title'],
'n_addr': js['items'][q]['roadAddress'],
'n_category': js['items'][q]['category'],
'n_x': js['items'][q]['mapx'],
'n_y': js['items'][q]['mapy']}
new_rows.append(new_row)
df1000 = pd.concat([df1000, pd.DataFrame(new_rows)], ignore_index = True) 데이터프레임에 row들을 추가
else:
print("Error Code:" + rescode)
df1000.to_csv(f'api_crawl_1/api_crawl_step1_{i*1000}.csv') 1000개마다 저장하겠다. 내 데이터가 총 90000개니까, 90번 돌리면 된다 ^0^
굳이 바보같이 1000개마다 저장하는 이유는, 중간에 에러가 나면 파이썬이 튕기기 때문인데
생각해보면 database에 그때그때 연동하면 되는 일이 아닌가 싶지만,
다듬어지지 않은 테이블을 계속 DB에 추가하면 그건 또 그거대로 골치아프니까
temporary 한 이 데이터는 csv로 저장하도록 하자.
그리고 겁나 문제는, 지금 몇번 안돌렸는데 벌써 쿼리 용량이 다 찼다는 사실...