Color hunt

Jina·2020년 5월 9일
0

Django

목록 보기
11/11

Intro

Color hunt 사이트 API 두 개 만들기

  • main page color set 목록 api
  • 상세 페이지의 color 정보 가져오기

Crawling

color hunt 사이트의 api를 만들기 전, 해당 홈페이지의 color set들의 정보를 crawling을 이용해서 가져와야한다.

한 페이지에 모든 내용이 있는 경우 Beautifulsoup만 사용해서 정보를 가져올 수 있으나 color hunt페이지는 스크롤을 내려서 아래쪽의 정보도 가져와야하기 때문에 selenium을 이용해야한다.

무한 스크롤 crawling하기

implicitly_wait vs time.sleep

time.sleep

지정한 시간동안 프로세스를 기다려 주는 역할 (무조건 지연)
만약 time.sleep(5)를 하면 코드 실행을 정확히 5초간 멈춘다.

보통 페이지 이동 후 에 쓰인다.

implicity_wait

브라우저에서 사용되는 엔진 자체에서 파싱되는 시간을 기다려주는 메소드 (셀레늄에서만 사용하는 특수한 메소드) 보통 webdriver 첫 로딩시에 쓰인다.

implicitly_wait(10)은 요소가 존재할 때까지 최대 10초 동안 대기한다.
즉, 10초 전에 브라우저에서 파싱이 완료되면 10초를 기다리지 않고 바로 다음 코드로 넘어간다. 반대로 10초를 기다렸는데 파싱이 끝나지 않았으면 에러가 발생하고 바로 종료된다.

위의 내용은 이 곳의 답변을 정리하여 작성하였다.

execute_script("document")

Synchronously Executes JavaScript in the current window/frame

색상 코드 가져오기

import time
import csv
from selenium import webdriver
from bs4      import BeautifulSoup

file = open("color_hunt.csv", mode="w")
wr = csv.writer(file)
wr.writerow(["c4","c3","c2","c1"])

# 1
driver = webdriver.Chrome('/Users/jinachoi/jina/chromedriver')
driver.implicitly_wait(3)
driver.get('https://colorhunt.co/')

# 2
last_height = driver.execute_script("return document.body.scrollHeight")

while True:
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(3)
    new_height = driver.execute_script("return document.body.scrollHeight")

    if new_height == last_height:
        break

    last_height = new_height

# 3
html = driver.page_source
soup = BeautifulSoup(html,'html.parser')

# 4
palette=soup.find_all('div',{'class':'palette'})
for color in palette:
    c4 = color.find('div',{'class':'place c4'}).find('span').string
    c3 = color.find('div',{'class':'place c3'}).find('span').string
    c2 = color.find('div',{'class':'place c2'}).find('span').string
    c1 = color.find('div',{'class':'place c1'}).find('span').string
    
    wr.writerow((c4,c3,c2,c1))

1. selenium으로 사이트 열기

driver = webdriver.Chrome('/Users/jinachoi/jina/chromedriver')
위의 명령어를 이용하여 driver라는 webdriver객체를 만들어주었다. Chrome뒤에 있는 경로는 내 컴퓨터에 chromedriver가 존재하는 장소의 위치이다.

driver.implicitly_wait(3)를 이용하여 웹사이트가 로드될 때 까지 기다린다.
driver.get('https://colorhunt.co/')을 이용하여 사이트를 열었다.

2. 스크롤 내리기

driver.execute_script("return document.body.scrollHeight")를 이용하여 스크롤을 동작시킬 수 있다.

2-1. last_height = driver.execute_script("return document.body.scrollHeight")
스크롤 높이를 last_height로 저장한다.

2-2. driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
Scroll을 아래로 내린다.

2-3. time.sleep(3)
로드될 때까지 3초 기다리기

2-4. new_height = driver.execute_script("return document.body.scrollHeight")
스크롤이 된 후의 높이를 계산하고 new_height로 지정한다.

2-5. 그 이후
이후 last_height와 new_height를 비교하고 높이가 같으면 (더 이상 내려가지 않은 것 이므로) 스크롤 종료하기

스크롤 내리기는 여기를 참고했다.

3. selenium으로 렌더링한 페이지 가져오기

html = driver.page_source 을 이용하여 스크롤을 내리고 렌더링한 페이지를 가져온다.
이 문서에 bs4를 이용하여 크롤링을 진행할 것이다.

4. crawling 및 csv파일 만들기

bs4를 이용하여서 원하는 내용을 찾아서 csv에 저장하도록 하였다.

models.py

 # color/models.py
 
 from django.db import models

 class Color(models.Model):
     c4 = models.CharField(max_length=50)
     c3 = models.CharField(max_length=50)
     c2 = models.CharField(max_length=50)
     c1 = models.CharField(max_length=50)

     class Meta:
        db_table = "colors"

이후 makemigrations, migrate를 해주었다.

CSV to database

upload.py를 만들어서 위에서 크롤링하여 csv로 저장한 내용을 database에 옮겨주었다.

 # upload.py
 
 import csv
 import os
 import django
 import sys

 os.chdir(".")
 print("Current dir=", end=""), print(os.getcwd())

 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 print("BASE_DIR=", end=""), print(BASE_DIR)

 sys.path.append(BASE_DIR)

 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "color_hunt.settings")
 django.setup()

 from color.models import Color

 # color
 CSV_PATH = './color_hunt.csv'

 with open(CSV_PATH, newline='') as csvfile:
     data_reader = csv.DictReader(csvfile)
     for row in data_reader:
         Color.objects.create(
             c4 = row['c4'],
             c3 = row['c3'],
             c2 = row['c2'],
             c1 = row['c1']
         )

views.py

 # color/views.py
 
 import json

 from django.views  import View
 from django.http   import HttpResponse, JsonResponse
 from .models       import Color

 class ColorSetView(View):
     def get(self,request):
         colors = Color.objects.select_related('id','c4','c3','c2','c1').values()
         color_list =[
                 {"id":color.get('id'),
                  "set":[color.get('c4'),color.get('c3'),color.get('c2'),color.get('c1')]
                  }for color in colors]

         return JsonResponse({"data":color_list}, status=200)

 class DetailView(View):
     def get(self,request,id):
         colors=Color.objects.select_related('c4','c3','c2','c1').filter(id=id).values()
         color_set = [
             {"id": id,
              "set": [color.get('c4'),color.get('c3'),color.get('c2'),color.get('c1')]
              }for color in colors]

         return JsonResponse({"data":color_set},status=200)

urls.py

아래의 내용은 app에 추가된 urls이다.

 # color/urls.py
 
 from django.urls import path
 from .views      import ColorSetView,DetailView

 urlpatterns = [
         path('/colors',ColorSetView.as_view()),
         path('/<int:id>',DetailView.as_view())
         ]

아래의 내용은 project에 추가된 urls이다.

 # color_hunt/urls.py
 
 from django.contrib import admin
 from django.urls import path, include

 urlpatterns = [
     path('color', include('color.urls')),
 ]

API 확인하기

위의 내용을 토대로 api를 만들고 확인하였다.

확인한 api는 여기서 확인할 수 있다.

0개의 댓글