본문 바로가기
Python

[장고웹개발] 웹스크래핑, 장고와 함께 하기

by 돈민찌 2022. 2. 17.
반응형

* 웹스크래핑에 대해서는 깊게 다루지 않습니다.

장고 하이

블로그에서 웹스크래핑에 대한 정보를 많이 다뤘기 때문에, 그 내용에 이어서 장고로 기반을 갖추면 좋은 이유, 그리고 어떻게 하면 장고의 데이터베이스에 우리가 원하는 데이터를 예쁘게 담을 수 있는 지에 대해 초점을 맞추겠다.

우선 ORM이라는 것이 있다. Object Relational Mapping, 즉 객체-관계 매핑이라는 것은 객체 지향 패러다임을 이용해 데이터베이스에서 데이터를 쿼리하고 조작할 수 있는 기술이다. 데이터를 효율적, 생산적으로 저장할 수 있는 데이터베이스, 그리고 그들의 언어인 SQL은 우리가 평소에 작성하는 프로그래밍 언어들과 접근 방법 등에서 차이가 있고, 비즈니스 로직에 의해 이것들을 다룰 때 그 복잡성이 깊어질 수록 프로그래밍 언어 - 데이터베이스 언어 사이의 간극이 벌어지는 상황을 최소화하고, 우리가 실제로 만나고 사용하고 동작하는 현실의 객체의 동작 방식과 유사하게 프로그램을 작성하는 객체 지향적 접근 방식으로 좀 더 현실의 언어로 프로그래밍을 하고, 또 프로그래밍의 언어에 더 집중한 코딩을 할 수 있게 해준다.

ORM을 사용하면 여러가지 장점이 있다.

  • DRY (don't repeat yourself): 코드 반복을 줄여준다.
  • 복잡한 SQL 쿼리와 완전히 분리되어 비즈니스 로직에 집중할 수 있다.
  • 디버깅이 힘든 SQL의 문제점을 대폭 커버합니다.
  • 트랜잭션을 구현하는 것이 훨씬 쉽습니다.
  • 데이터 베이스를 변경하는 등의 변화에 유연하게 대응할 수 있습니다.

개발자를 고통받게 하는 단점 역시 존재한다.

  • 프로그램(프로젝트)의 무게가 무거워지고 다소 설정할 것이 생긴다.
  • SQL을 직접 사용할 때보다 추상적인 만큼 약간의 성능 저하가 뒤따른다.
  • 라이브러리가 어떻게 동작하는지 모르는 이상 비효율적인(탐욕적인) 알고리즘을 구현하기 쉬운 초보 개발자에 의해 복잡한 쿼리가 되거나 불필요한 조회 등이 발생할 수 있다.

파이썬의 ORM에는 대표적으로 SQLAlchemy, DJANGO ORM이 있다. 플라스크 개발을 공부할 때 SQLAlchemy를 좀 더 공부해보지 않은 것이 아쉽지만, Django ORM의 장점 역시 무궁무진하니 이쪽을 더 공부해보자.

장고 프로젝트를 생성하고, 마이그레이션을 마치고 나면, 데이터베이스가 생긴다. 그리고 이 데이터베이스에 직접 개발한 크롤러를 연결하여 데이터를 쌓으려고 하면, 에러에 부딪힌다. 

장고 프로젝트에서 어떠한 동작을 하는 애플리케이션을 만들었을 때, 프로젝트에 그 사실을 알려야 한다. settings.py에 INSTALLED_APPS에 해당 프로젝트를 등록해주면 된다.

일정한 기간마다 데이터를 습득해야 하는 크롤러라면 Django-CRONTAB을 사용한다. 크론이란 특정 주기마다 동작하는 프로그램을 작성할 때 필수적인 스케줄 기능이다.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'crawler.apps.CrawlerConfig',
    'django_crontab',
]

CRONJOBS = [
    ('0 * * * *', 'crawler.ranking_bot_v2.hourly'),
    ('10 0 * * *', 'crawler.ranking_bot_v2.daily'),
]

이런 식으로 job을 등록해주면 된다. 그리고 배포 단계에서 

python manage.py crontab add

를 실행해주면 스케줄러 등록이 된다.

그리고 크롤러가 동작하는 파일 안에 django를 임포트해줘야 해당 코드의 결과물들이 장고에서 만들어주는 데이터베이스에 안정적으로 자리잡을 수 있다.

import os

from django.db import IntegrityError
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ranker.settings")
import django

if 'setup' in dir(django):
    django.setup()

import requests
from crawler.models import OneStoreDL  # django ORM

# 여기 위의 코드는 거의 필수적으로 들어가는 구성요소라 생각하면 된다.

def get_soup(appid):
    one_url = "https://m.onestore.co.kr/mobilepoc/web/apps/appsDetail/spec.omp?prodId="+appid
    from bs4 import BeautifulSoup  # 위에서 임포트해줘도 되지만 이 함수에서만 사용하는 모듈이라 여기서 임포트
    response = requests.get(one_url).text
    return BeautifulSoup(response, "html.parser")


def get_one_store_app_download_count(date: TimeIndex, appid: str, app: App):
    soup = get_soup(appid)
    download = soup.select_one("li:-soup-contains('다운로드수') > span").text
    d_string = soup.select_one("li:-soup-contains('출시일') > span").text
    genre = soup.select_one("li:-soup-contains('장르') > span").text
    volume = soup.select_one("li:-soup-contains('용량') > span").text
    style = soup.select_one("#header > div > div > div.header-co-right > span").get('style')
    icon_url = style.removeprefix("background-image:url(").removesuffix(")")

    import datetime
    array = [i for i in map(int, d_string.split("."))]
    released = datetime.date(year=array[0], month=array[1], day=array[2])
    d_counts = int(download.replace(",", ""))

    ones_app = OneStoreDL(
        date_id=date.id,
        app_id=app.id,
        market_appid=appid,
        downloads=d_counts,
        genre=genre,
        volume=volume,
        released=released,
        icon_url=icon_url,
        app_name=app_name,
    )
    ones_app.save()
    
    # 장고의 ORM을 이렇게 사용한다. 객체의 프로퍼티를 설정하고 .save()를 해주기만 하면 끝
    # 사용할 때에는 OneStoreDL.objects.filter(app_name="바람의나라연").first() 와 같이 사용하면 된다!

이런 식으로 코드를 작성해 주면 된다. 이제부터 장고의 프로젝트 안에서 내부적으로 동작하는 크롤러를 사용할 수 있다!! 참고로 해당 코드는 원스토어의 앱 아이디를 받아 다운로드 수 등의 수치를 크롤링하는 코드이다. 냠냠 끗

(개인 기록용이라 설명이 불친절합니다 죄송함니다)

반응형

댓글