React로 만든 이력서 페이지에 블로그 글 목록을 크롤링해서 불러오는 기능을 만들었다.

API는 Python, Flask, Selenium, BeautifulSoup를 사용해서 제작했으며 Heroku에 서버를 올렸다.

크롤링 자체를 구현하는데는 30분도 걸리지 않았는데, Heroku에 올리는데 많은 시간이 사라졌다.

만들면서 경험한 에러들이 어떤것이 있을까?

1 - Heroku에서 H10 deploy failed 에러 (Application error)

  1. Profile 부분이 제대로 작성되지 않았음.
  2. 새로운 패키지를 추가했는데, requirements.txt를 갱신하지 않았음.
    pip freeze > requirements.txt를 통해 갱신해줘야 한다.
  3. Heroku는 WebDriver를 buildpack을 통해 자체적으로 넣어줘야 한다는 사실을 뒤늦게 깨달음

위 사진과 같이 buildpack에 heroku의 크롬드라이버, 구글크롬 주소를 넣어줌

  1. 코드 자체에 return되는 결과물이 없었음 (왜그랬을까..)
  2. PORT 번호를 heroku env를 통해 넣어줘야 한다는걸 뒤늦게 알았음.

2 - Heroku에서 APP CRASH 에러

  1. Heroku에서는 ChromeDriver와 Chrome 주소를ENV를 통해 넣어주는데 자잘한 오타로 인해 원인을 못찾고 헤맴
    ex )
  2. 원래 코드에는 GET_IMAGE라는 코드도 있었는데, img태그의 src링크를 긁어오기 위해 있는 코드였음
    문제는 IMG 태그 자체는 원래 존재하지만, src Attr 부분이 동적으로 생성되었음.
    이 부분을 불러온 뒤 크롤링하기 위해 WebDriverWait이나 다른 여러가지 방법도 시도해봤지만 간헐적으로 Crash가 나거나, 정상적으로 뜨는데 로딩속도가 엄청나게 느렸음. ( Local에선 잘되는데 Heroku에 올리면 생기는 문제점 )
  3. os.environ.get("GOOGLE_CHROME_BIN") // True os.environ.get("GOOGLE_CHROE_BIN") // Error

3 - 원인을 모르는 500 Error

​ 어쩌다보니 해결되서 원인도 모르겠음.. 이것저것 만지다 보니 해결된거라

4 - CORS 에러

API를 완성하고 FRONT에 연결하려고 보니 CORS 에러때문에 데이터를 제대로 불러올수가 없었음.
또한 이전에 CORS 에러때문에 하루종일 고생했던 기억이 있어서 지레 겁을 먹고 도전했는데.

from flask_cors import CORS, cross_origin // 1
app = Flask(__name__)
CORS(app) // 2
app.config['CORS_HEADERS'] = 'Content-Type' // 3
@app.route('/')
@cross_origin() // 4
def hello():
start()
return jsonify(dataList)

구글 검색을 통해 위 4가지 코드를 삽입해주니 허무할정도로 말끔하게 해결되었다.

또한 Heroku는 30분 미사용시 자동으로 Seep 모드로 들어간다고 한다.
Sleep 모드로 들어가면 다시 데이터를 불러올때 10~ 12초정도의 시간이 걸린다고 하는데
https://m.blog.naver.com/kbs4674/221406115523

위 블로그 글을 참고해서 10분마다 접속해서 Sleep를 방지했다.

현재 남은 문제는..

API 호출시 걸리는 시간이 3~4초정도 소요되는데 이 시간을 어떻게 해야 바로 호출이 가능할지 고민해봐야 할것같다.

아래는 API 전체코드

from selenium import webdriver
from flask import Flask, request, jsonify
from flask_cors import CORS, cross_origin
from bs4 import BeautifulSoup
import os
app = Flask(__name__)
CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'
tistoryId = "jsbeginner"
URL = 'https://' + tistoryId + '.tistory.com'
options = webdriver.ChromeOptions()
options.binary_location = os.environ.get("GOOGLE_CHROME_BIN")
options.add_argument("headless")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--no-sandbox") # 샌드박스 보안 비활성화
def start():
driver = webdriver.Chrome(executable_path=os.environ.get("CHROMEDRIVER_PATH"), options=options)
# driver = webdriver.Chrome('../chromedriver.exe', options=options)
driver.get(URL)
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
# WebDriverWait(driver, 10).until(
# EC.presence_of_element_located((By.CSS_SELECTOR, '.diva[src]'))
# )
thumbList = soup.select('.fixed_img_col > ul > li')
global dataList
dataList = []
for i in thumbList:
get_Date = i.find("strong").find("p").get_text()
get_Title = i.find(class_="elli").get_text()
get_Link = i.a.attrs['href']
get_New = True if i.find(class_="thumbnew") else False
data = {
"date" : get_Date,
"title": get_Title,
"link" : URL + get_Link,
"new" : get_New,
}
dataList.append(data)
print(dataList)
driver.close()
@app.route('/')
@cross_origin()
def hello():
start()
return jsonify(dataList)
if __name__ == "__main__":
port = int(os.environ.get("PORT", 5000))
app.run(host="0.0.0.0", port=port)

'프로그래밍' 카테고리의 다른 글

Script 태그의 종류 (defer, async)  (0) 2021.01.24