본문 바로가기
쿠버네티스,쿠버플로우

던전앤파이터 시세 예측[장고]

by 세용용용용 2023. 9. 1.

이제 장고를 사용해 던파 사이트를 만들어 보자

먼저 로컬에서 테스트 해보자

 

로컬 마리아db에 테이블을 만들고 데이터를 넣어보자

 

장고로 테이블 불러와서 웹에 띄우자!!

# asgi.py
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_asgi_application()

----------------------------------------------------------------------------------------

# settings.py
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-u$l2=t)pk-4wsqmn6lu&=7u*jyjjqa%971&k-+(p8zfw(&j6)9'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'pybo',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'mysite.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'mysite.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

#DATABASES = {
    #'default': {
        #'ENGINE': 'django.db.backends.sqlite3',
        #'NAME': BASE_DIR / 'db.sqlite3',
    #}
#}
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME' : 'donpa_item',
        'USER' : 'root',
        'PASSWORD' : '1234', #MariaDB 로그인 시 비밀번호
        'HOST' : 'localhost', #디폴트는 로컬호스트
        'PORT' : '3306', #기본은 3306인데 저는 다른 DBMS가 3306을 쓰고 있어서 3307로 했습니다.
    }
}

# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = 'ko-kr'

TIME_ZONE = 'Asia/Seoul'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

----------------------------------------------------------------------------------------

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

urlpatterns = [
    # 기존의 pybo 앱 URL 매핑
    path('pybo/', include('pybo.urls')),
    # 추가적으로 루트 URL('/')로 접속시 pybo 앱의 URL 매핑을 처리하도록 설정
    path('', include('pybo.urls')),
    path('admin/', admin.site.urls),
]

----------------------------------------------------------------------------------------

# wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_wsgi_application()

python manage.py inspectdb donpa_item1 >>> 테이블 스키마 확인하는 코드!!

 

 

apps.py

from django.apps import AppConfig


class PyboConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'pybo'

 

models.py

from django.db import models


class DonpaItem1(models.Model):
    item_img = models.TextField(blank=True, null=True)
    item_name = models.TextField(blank=True, null=True)
    price = models.FloatField(blank=True, null=True)
    before_now = models.FloatField(blank=True, null=True)
    before_one = models.FloatField(blank=True, null=True)
    before_two = models.FloatField(blank=True, null=True)
    before_three = models.FloatField(blank=True, null=True)
    before_four = models.FloatField(blank=True, null=True)
    before_five = models.FloatField(blank=True, null=True)
    before_six = models.FloatField(blank=True, null=True)
    before_seven = models.FloatField(blank=True, null=True)
    before_eight = models.FloatField(blank=True, null=True)
    before_nine = models.FloatField(blank=True, null=True)
    before_ten = models.FloatField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'donpa_item1'

 

 

urls.py

urlpatterns = [
    path('', views.item_list),
    path('aabata/', views.aabata_view, name='aabata'),
    path('event/', views.events_view, name='event'),
    path('news/', views.news_view, name='news'),  # /news/ 경로에 news_view를 연결
    
    
    # 다른 URL 패턴들...
]

 

 

views.py

from django.shortcuts import render
from .models import DonpaItem1

def item_list(request):
    items = DonpaItem1.objects.all()
    return render(request, 'index.html', {'items': items})

 

 

index.html

<!DOCTYPE html>
<html>
<head>
    <title>Item List</title>
    <style>
        /* 표 스타일링 */
        table {
            width: 80%; /* 표의 너비를 조절하세요 */
            border-collapse: collapse;
            margin: 20px auto; /* 가운데 정렬 */
        }
        th, td {
            border: 1px solid #dddddd;
            text-align: left;
            padding: 8px;
        }
        th {
            background-color: #f2f2f2;
        }
        /* 이미지 크기 조정 */
        img {
            width: 100px; /* 이미지의 너비를 조절하세요 */
            height: auto; /* 이미지 높이 자동 조절 */
        }
        /* 제목 */
        h1 {
            font-size: 46px; /* 큰 제목 크기로 변경 */
            color: #f7e706; /* 게임 색상으로 변경 */
            text-align: center;/* 제목 가운데 정렬*/
        }
        /* 메뉴 스타일링 */
        ul.menu {
            list-style-type: none;
            margin: 0;
            padding: 0;
            text-align: center;
            
            padding: 10px 0; /* 메뉴 항목 위아래로 간격 추가 */
        }
        ul.menu li {
            display: inline;
            margin-right: 20px;
            font-size: 40px;
        }
        ul.menu li a {
            color: rgb(255, 255, 255);
            text-decoration: none;
            padding: 10px 20px; /* 각 메뉴 항목의 내부 여백 추가 */
            border-radius: 5px; /* 각 메뉴 항목을 둥글게 꾸미기 */
            /* background-color: #534747; /* 배경색 추가 */
            box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* 그림자 효과 추가 */
            transition: background-color 0.3s, transform 0.2s; /* 호버 효과를 위한 트랜지션 설정 */
        }
        ul.menu li a:hover {
            background-color: #4e2626; /* 호버 시 배경색 변경 */
            transform: translateY(-2px); /* 호버 시 약간 위로 이동 효과 */
        }
        /* 차트 컨테이너 스타일링 */
        .chart-container {
            width: 90%; /* 차트 컨테이너의 너비를 조절하세요 */
            margin: 20px /* 가운데 정렬 */
        }

        /* item-name 스타일 */
        .item-name {
            font-weight: bold; /* 굵은 글씨체 */
            color: #007BFF; /* 텍스트 색상 변경 */
            font-size: 18px; /* 글씨 크기 변경 */
        }

        /* item-price 스타일 */
        .item-price {
            font-weight: bold; /* 굵은 글씨체 */
            color: #28A745; /* 텍스트 색상 변경 */
            font-size: 18px; /* 글씨 크기 변경 */
        }

        /* 배경 이미지 추가 */
        body {
            background-image: url('https://mblogthumb-phinf.pstatic.net/20160909_66/neoplog_1473407324296hgvz4_JPEG/1_%B9%F6%BC%B8%C1%A4%BF%F8.jpg?type=w800');
            background-size: cover;
            background-repeat: no-repeat;
            background-attachment: fixed;
            background-position: center;
            position: relative;
            z-index: 0;
        }

        /* 표 스타일링 */
        table {
            width: 80%; /* 표의 너비를 조절하세요 */
            border-collapse: collapse;
            margin: 20px auto; /* 가운데 정렬 */
            position: relative; /* z-index를 사용하기 위해 설정 */
            z-index: 1; /* 표의 z-index를 배경 이미지 위로 설정 */
        }

        th, td {
            border: 4px solid #000000;
            background-color: #dddddd;
            text-align: left;
            padding: 18px;
        }

        th {
            background-color: #ff0000;
        }
    </style>
    <!-- Chart.js 추가 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
</head>
<body>
    <ul class="menu">
        <li><a href="/">아이템 시세</a></li>
        <li><a href="/aabata/">아바타 시세</a></li>
        <li><a href="/event/">진행 중 이벤트</a></li>
        <li><a href="/news/">던파 소식</a></li>
    </ul>
    <h1>던전앤파이터 아이템</h1>
    <table>
        <thead>
            <tr>
                <th>아이템 이미지</th>
                <th>아이템 명</th>
                <th>1시간뒤 예상 가격</th>
                <th>아이템 가격 변동</th>
            </tr>
        </thead>
        <tbody>
            {% for item in items %}
            <tr>
                <td><img src="{{ item.item_img }}" alt="Item Image"></td>
                <td><span class="item-name">{{ item.item_name }}</span></td>
                <td><span class="item-price">{{ item.price }}</span></td>
                <td>
                    <div class="chart-container">
                        <canvas class="line-chart" data-values="{{ item.before_ten }}, {{ item.before_nine }}, {{ item.before_eight }}, {{ item.before_seven }}, {{ item.before_six }}, {{ item.before_five }}, {{ item.before_four }}, {{ item.before_three }}, {{ item.before_two }}, {{ item.before_one }}, {{ item.before_now }}"></canvas>
                    </div>
                </td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    
    <!-- JavaScript로 그래프 그리기 -->
    <script>
        // 모든 차트를 가져와서 데이터를 설정하고 그립니다.
        const charts = document.querySelectorAll('.line-chart');
        charts.forEach(chart => {
            const values = chart.dataset.values.split(', ').map(value => parseFloat(value));
            new Chart(chart, {
                type: 'line',
                data: {
                    labels: ['10시간 전', '9시간 전', '8시간 전', '7시간 전', '6시간 전', '5시간 전', '4시간 전', '3시간 전', '2시간 전', '1시간 전', '0시간 전'],
                    datasets: [{
                        data: values,
                        borderColor: '#ff6600', /* 게임 스타일 색상으로 변경 */
                        backgroundColor: 'rgba(255, 102, 0, 0.2)', /* 배경색 추가 */
                        fill: true
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        y: {
                            beginAtZero: false
                        }
                    }
                }
            });

        });
    </script>
</body>
</html>

 

이제 던파 뉴스와 이벤트목록을 크롤링해와서 장고에 띄워보자
1. 크롤링해와서 마리아db테이블에 저장하고

던파 뉴스 부터 만들어보자

먼저 던파 뉴스 테이블을 생성해주자

테이블 생성 코드 : create table donpa_news ( photo TEXT, title TEXT, link TEXT );

 

크롤링해서 테이블에 데이터는 넣는 코드

import requests
from bs4 import BeautifulSoup
import pymysql

# MySQL 연결 설정
conn = pymysql.connect(
    host='localhost',  # 호스트명
    user='root',    # 데이터베이스 사용자명
    password='1234',  # 데이터베이스 암호
    db='donpa_item',      # 데이터베이스 이름
    port = 3306
)

# 커서 생성
cursor = conn.cursor()

# 크롤링 코드 작성
photo_list = []
title_list = []
link_list = []

# 검색 결과 페이지 URL
url = "https://search.naver.com/search.naver?where=news&query=%EB%8D%98%ED%8C%8C&sm=tab_opt&sort=1&photo=1&field=0&pd=0&ds=&de=&docid=&related=0&mynews=0&office_type=0&office_section_code=0&news_office_checked=&nso=so%3Add%2Cp%3Aall&is_sug_officeid=0&office_category=0&service_area=0"

# HTTP GET 요청
response = requests.get(url)

# 응답 데이터 파싱
soup = BeautifulSoup(response.text, "html.parser")

select_num = 1
while select_num<=10:
    # 이미지 선택자
    selector = f"#sp_nws{select_num} > div > a > img"
    # 선택자에 해당하는 이미지 요소를 찾음
    image = soup.select_one(selector)
    # 각 링크의 href 속성값을 출력
    photo = image.get("data-lazysrc")
    photo_list.append(photo)
    select_num+=1

# 뉴스 타이틀, 링크 선택자
selector = "#main_pack > section.sc_new.sp_nnews._prs_nws > div > div.group_news > ul > li> div > div > a"
# 선택자에 해당하는 요소들을 모두 찾음
title_link = soup.select(selector)

# 각 타이틀,링크 속성값을 출력
for link in links:
    title = link.get("title")
    href = link.get("href")
    title_list.append(title)
    link_list.append(href)



# 테이블 초기화 (기존 데이터 삭제)
truncate_sql = "TRUNCATE TABLE donpa_news"
cursor.execute(truncate_sql)
    
    
# 데이터베이스에 데이터 삽입
for i in range(len(photo_list)):
    photo = photo_list[i]
    title = title_list[i]
    link = link_list[i]

    sql = "INSERT INTO donpa_news (photo, title, link) VALUES (%s, %s, %s)"
    cursor.execute(sql, (photo, title, link))

# 변경사항 커밋
conn.commit()

# 연결 종료
conn.close()

 

models.py수정

from django.db import models


class DonpaItem1(models.Model):
    item_img = models.TextField(blank=True, null=True)
    item_name = models.TextField(blank=True, null=False, primary_key=True)  # null=False로 수정
    price = models.FloatField(blank=True, null=True)
    before_now = models.FloatField(blank=True, null=True)
    before_one = models.FloatField(blank=True, null=True)
    before_two = models.FloatField(blank=True, null=True)
    before_three = models.FloatField(blank=True, null=True)
    before_four = models.FloatField(blank=True, null=True)
    before_five = models.FloatField(blank=True, null=True)
    before_six = models.FloatField(blank=True, null=True)
    before_seven = models.FloatField(blank=True, null=True)
    before_eight = models.FloatField(blank=True, null=True)
    before_nine = models.FloatField(blank=True, null=True)
    before_ten = models.FloatField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'donpa_item1'



class DonpaNews(models.Model):
    photo = models.TextField(blank=True, null=True)
    title = models.TextField(blank=True, null=True)
    link = models.TextField(blank=True, null=False, primary_key=True)

    class Meta:
        managed = False
        db_table = 'donpa_news'

python manage.py makemigrations

python manage.py migrate

모델이 변경되었을떄 적용시켜주는 명령어

 

 

 

view.py

from django.shortcuts import render
from .models import DonpaItem1
from .models import DonpaNews

def item_list(request):
    items = DonpaItem1.objects.all()
    return render(request, 'index.html', {'items': items})

def aabata_view(request):
    # news.html 템플릿 렌더링
    return render(request, 'aabata.html')

def events_view(request):
    # news.html 템플릿 렌더링
    return render(request, 'event.html')    

def news_view(request):
    # news.html 템플릿 렌더링
    items = DonpaNews.objects.all()
    return render(request, 'news.html', {'items':items})

 

 

news.html

<!DOCTYPE html>
<html>
<head>
    <title>Item List</title>
    <style>
        
        /* 제목 */
        h1 {
            font-size: 46px; /* 큰 제목 크기로 변경 */
            color: #f7e706; /* 게임 색상으로 변경 */
            text-align: center;/* 제목 가운데 정렬*/
        }
        /* 메뉴 스타일링 */
        ul.menu {
            list-style-type: none;
            margin: 0;
            padding: 0;
            text-align: center;
            
            padding: 10px 0; /* 메뉴 항목 위아래로 간격 추가 */
        }
        ul.menu li {
            display: inline;
            margin-right: 20px;
            font-size: 40px;
        }
        ul.menu li a {
            color: rgb(255, 255, 255);
            text-decoration: none;
            padding: 10px 20px; /* 각 메뉴 항목의 내부 여백 추가 */
            border-radius: 5px; /* 각 메뉴 항목을 둥글게 꾸미기 */
            /* background-color: #534747; /* 배경색 추가 */
            box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* 그림자 효과 추가 */
            transition: background-color 0.3s, transform 0.2s; /* 호버 효과를 위한 트랜지션 설정 */
        }
        ul.menu li a:hover {
            background-color: #4e2626; /* 호버 시 배경색 변경 */
            transform: translateY(-2px); /* 호버 시 약간 위로 이동 효과 */
        }
        

        /* 배경 이미지 추가 */
        body {
            background-image: url('https://bbscdn.df.nexon.com/data7/commu/201712/205032_5a44da887e6a8.jpg');
            background-size: cover;
            background-repeat: no-repeat;
            background-attachment: fixed;
            background-position: center;
            position: relative;
            z-index: 0;
        }

        /* 테이블 스타일링 */
        table {
            margin: 0 auto; /* 가운데 정렬 */
            border-collapse: collapse; /* 테이블 셀 경계 표시 제거 */
            width: 70%; /* 테이블의 너비를 조절하세요 */
            height: 0%;
    
        }
        
        /* 테이블 셀 스타일링 */
        table, th, td {
            border: 5px solid #ddd; /* 테이블 셀 테두리 스타일 */
        }
        
        th, td {
            padding: 0px; /* 셀 내부 여백 추가 */
            text-align: center; /* 가운데 정렬 */
            vertical-align: middle; /* 셀 내용 세로 중앙 정렬 */
        }

        /* 뉴스 타이틀 스타일링 */
        .item-title {
            font-size: 280%; /* 글꼴 크기 설정 */
            color: #bee60d; /* 글꼴 색상 설정 */
            text-decoration: none; /* 밑줄 제거 */
            font-weight: bold; /* 글꼴 굵게 설정 */
        }

        /* 이미지 스타일링 */
        img {
            width: 100%;
            height: auto; /* 높이 자동 조정 */
        }
        
    </style>
    
</head>
<body>
    <ul class="menu">
        <li><a href="/">아이템 시세</a></li>
        <li><a href="/aabata/">아바타 시세</a></li>
        <li><a href="/event/">진행 중 이벤트</a></li>
        <li><a href="/news/">던파 소식</a></li>
    </ul>
    <h1>던전앤파이터 뉴스</h1>
    <table>
        <thead>
            <tr>

            </tr>
        </thead>
        <tbody>
            {% for item in items %}
            <tr>
                <td><img src="{{ item.photo }}" alt="Item Image"></td>
                <td><a href="{{ item.link }}" class="item-title">{{ item.title }}</a></td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    
    
    
</body>
</html>

 

 

이제 던파 이벤트를 띄워보자

테이블 생성 코드

MariaDB [donpa_item]> create table donpa_event(
    -> img TEXT,
    -> text TEXT,
    -> date TEXT,
    -> herf TEXT
    -> );

 

크롤링해서 테이블에 데이터 넣는 코드

import requests
from bs4 import BeautifulSoup
import re
import pymysql

# MySQL 연결 설정
conn = pymysql.connect(
    host='localhost',  # 호스트명
    user='root',    # 데이터베이스 사용자명
    password='1234',  # 데이터베이스 암호
    db='donpa_item',      # 데이터베이스 이름
    port = 3306
)

# 커서 생성
cursor = conn.cursor()

eve_img = []
eve_text = []
eve_date = []
eve_herf = []

# 검색 결과 페이지 URL
url = "https://df.nexon.com/community/news/event/list"

# HTTP GET 요청
response = requests.get(url)

# 응답 데이터 파싱
soup = BeautifulSoup(response.text, "html.parser")

# 이벤트 img 셀렉터
selector_img = "#wrap > section.content.news > article.board_eventlist > ul > li > img"
# 이벤트 b 셀렉터
selector_text = "#wrap > section.content.news > article.board_eventlist > ul > li > b"
# 이벤트 날짜 span 셀렉터
selector_date = "#wrap > section.content.news > article.board_eventlist > ul > li > span"
# 이벤트 링크가 포함된 li 태그들 선택
selector_href = "#wrap > section.content.news > article.board_eventlist > ul > li"

# 선택자에 해당하는 요소들을 모두 찾음
event_img = soup.select(selector_img)
# URL에 프로토콜을 추가하여 출력
for img in event_img:
    src = img.get('src')
    if src.startswith('//'):
        src = 'https:' + src
    eve_img.append(src)


# 선택자에 해당하는 요소들을 모두 찾음
event_text = soup.select(selector_text)
# 각 링크의 텍스트만 추출하여 리스트에 저장
event_text = [link.get_text(strip=True) for link in event_text]
# 결과 출력
for text in event_text:
    eve_text.append(text)

    

# 선택자에 해당하는 요소들을 모두 찾음
event_dates = soup.select(selector_date)
# 결과 출력
for span in event_dates:
    date_text = span.text.strip().replace('\n', '').strip().replace(' ','')  # span 태그 안의 텍스트에서 공백 및 줄바꿈 제거
    eve_date.append(date_text)

    

event_href = soup.select(selector_href)

# 결과 출력
for li in event_href:
    if li.get("data-no"):
        event_url = f"http://df.nexon.com/community/news/event/{li.get('data-no')}?categoryType=0"
        eve_herf.append(event_url)
        
    else:
        li = li.get("onclick")
        
        if 'window.location.href' in li:
            # 정규 표현식을 사용하여 값을 추출합니다.
            li = re.findall(r"'(.*?)';", li)
            
            eve_herf.append("https://df.nexon.com"+''.join(li))
        elif 'neople.openCouponPop()' in li:
            li = "https://df.nexon.com/community/news/event/list"
            eve_herf.append(li)
            
        elif 'window.open' in li:
            # 정규 표현식을 사용하여 URL 추출
            li = re.search(r"https://[a-zA-Z0-9./-]+", li).group()
            eve_herf.append(li)

            
            
# 테이블 초기화 (기존 데이터 삭제)
truncate_sql = "TRUNCATE TABLE donpa_event"
cursor.execute(truncate_sql)
    

# 데이터베이스에 데이터 삽입
for i in range(len(eve_img)):
    img = eve_img[i]
    text = eve_text[i]
    date = eve_date[i]
    herf = eve_herf[i]

    sql = "INSERT INTO donpa_event (img, text, date, herf) VALUES (%s, %s, %s, %s)"
    cursor.execute(sql, (img, text, date, herf))

# 변경사항 커밋
conn.commit()

# 연결 종료
conn.close()

 

 

이제 장고에서 데이터를 띄워보자!!!

event.html

<!DOCTYPE html>
<html>
<head>
    <title>던전앤파이터 이벤트</title>
    <style>
        /* 제목 */
        h1 {
            font-size: 46px; /* 큰 제목 크기로 변경 */
            color: #f7e706; /* 게임 색상으로 변경 */
            text-align: center;/* 제목 가운데 정렬*/
        }
        /* 메뉴 스타일링 */
        ul.menu {
            list-style-type: none;
            margin: 0;
            padding: 0;
            text-align: center;
            
            padding: 10px 0; /* 메뉴 항목 위아래로 간격 추가 */
        }
        ul.menu li {
            display: inline;
            margin-right: 20px;
            font-size: 40px;
        }
        ul.menu li a {
            color: rgb(255, 255, 255);
            text-decoration: none;
            padding: 10px 20px; /* 각 메뉴 항목의 내부 여백 추가 */
            border-radius: 5px; /* 각 메뉴 항목을 둥글게 꾸미기 */
            /* background-color: #534747; /* 배경색 추가 */
            box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* 그림자 효과 추가 */
            transition: background-color 0.3s, transform 0.2s; /* 호버 효과를 위한 트랜지션 설정 */
        }
        ul.menu li a:hover {
            background-color: #4e2626; /* 호버 시 배경색 변경 */
            transform: translateY(-2px); /* 호버 시 약간 위로 이동 효과 */
        }

        /* 배경 이미지 추가 */
        body {
            background-image: url('https://bbscdn.df.nexon.com/data7/commu/201712/205032_5a44da887e6a8.jpg');
            background-size: cover;
            background-repeat: no-repeat;
            background-attachment: fixed;
            background-position: center;
            position: relative;
            z-index: 0;
        }

        /* 아이템 스타일링 */
        .item {
            background-color: #fff;
            padding: 20px;
            margin: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
            box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
            width: calc(50% - 20px); /* 컨테이너의 절반 너비로 설정하고 간격을 추가합니다 */
            display: inline-block;
            vertical-align: top;
            box-sizing: border-box;
            float: left;
        }

        /* 컨테이너 스타일링 */
        .container {
            max-width: 1400px;
            margin: 0 auto;
            padding: 20px;
            text-align: center; /* 가운데 정렬 */
            overflow: hidden; /* float 된 아이템을 감싸기 위해 추가 */
        }

        .item img {
            max-width: 100%;
            height: auto;
            margin-bottom: 10px;
            transition: transform 0.2s; /* 호버 효과를 위한 트랜지션 설정 */
        }

        .item:hover img {
            transform: scale(1.1); /* 호버 시 이미지 확대 효과 */
        }

        .item-title {
            font-weight: bold;
            font-size: 20px;
            text-decoration: none;
            color: #333;
            transition: color 0.3s; /* 호버 효과를 위한 트랜지션 설정 */
        }

        .item-title:hover {
            color: #f7e706; /* 호버 시 제목 색상 변경 */
        }

        .date {
            color: #777;
        }
    </style>
</head>
<body>
    <ul class="menu">
        <li><a href="/">아이템 시세</a></li>
        <li><a href="/aabata/">아바타 시세</a></li>
        <li><a href="/event/">진행 중 이벤트</a></li>
        <li><a href="/news/">던파 소식</a></li>
    </ul>
    <h1>던전앤파이터 이벤트</h1>
    <div class="container">
        {% for item in items %}
        <div class="item">
            <a href="{{ item.herf }}" target="_blank">
                <img src="{{ item.img }}" alt="Item Image">
                <a href="{{ item.herf }}" target="_blank" class="item-title">{{ item.text }}</a>
                <p class="date">{{ item.date }}</p>
            </a>
        </div>
        {% endfor %}
    </div>
</body>
</html>

 

 

 

던파 이벤트와, 던파 뉴스 데이터를 수집해 테이블에 저장하는 코드를 도커 이미지로 만들어 놓자

 

1. 던파 뉴스 도커 파일

dockerfile

# 베이스 이미지 설정
FROM python:latest

# 작업 디렉토리 설정
WORKDIR /app

# main, requirements 두 파일 작업디렉토리로 복사
COPY main.py /app/main.py
COPY requirements.txt /app

# 필요한 종속성 설치
RUN pip install --no-cache-dir -r requirements.txt

CMD [ "python" , "main.py" ]

 

main.py

import requests
from bs4 import BeautifulSoup
import pymysql

# MySQL 연결 설정
conn = pymysql.connect(
    host='10.109.90.46',  # 호스트명
    user='root',    # 데이터베이스 사용자명
    password='1234',  # 데이터베이스 암호
    db='donpa_item',      # 데이터베이스 이름
    port = 3306
)

# 커서 생성
cursor = conn.cursor()

# 크롤링 코드 작성
photo_list = []
title_list = []
link_list = []

# 검색 결과 페이지 URL
url = "https://search.naver.com/search.naver?where=news&query=%EB%8D%98%ED%8C%8C&sm=tab_opt&sort=1&photo=1&field=0&pd=0&ds=&de=&docid=&related=0&mynews=0&office_type=0&office_section_code=0&news_office_checked=&nso=so%3Add%2Cp%3Aall&is_sug_officeid=0&office_category=0&service_area=0"

# HTTP GET 요청
response = requests.get(url)

# 응답 데이터 파싱
soup = BeautifulSoup(response.text, "html.parser")

select_num = 1
while select_num<=10:
    # 이미지 선택자
    selector = f"#sp_nws{select_num} > div > a > img"
    # 선택자에 해당하는 이미지 요소를 찾음
    image = soup.select_one(selector)
    # 각 링크의 href 속성값을 출력
    photo = image.get("data-lazysrc")
    photo_list.append(photo)
    select_num+=1

# 뉴스 타이틀, 링크 선택자
selector = "#main_pack > section.sc_new.sp_nnews._prs_nws > div > div.group_news > ul > li> div > div > a"
# 선택자에 해당하는 요소들을 모두 찾음
title_link = soup.select(selector)

# 각 타이틀,링크 속성값을 출력
for link in title_link:
    title = link.get("title")
    href = link.get("href")
    title_list.append(title)
    link_list.append(href)



# 테이블 초기화 (기존 데이터 삭제)
truncate_sql = "TRUNCATE TABLE donpa_news"
cursor.execute(truncate_sql)


# 데이터베이스에 데이터 삽입
for i in range(len(photo_list)):
    photo = photo_list[i]
    title = title_list[i]
    link = link_list[i]

    sql = "INSERT INTO donpa_news (photo, title, link) VALUES (%s, %s, %s)"
    cursor.execute(sql, (photo, title, link))

# 변경사항 커밋
conn.commit()

# 연결 종료
conn.close()

 

requirements.txt

requests
beautifulsoup4
pymysql

 

이미지 저장하는 명령어

docker build -t sy02229/donpa_news:latest .

이미지 푸쉬하는 명령어

docker push sy02229/donpa_news

 

 

 

2. 던파 이벤트 도커파일

dockerfile

FROM python:latest

# 작업 디렉토리 설정
WORKDIR /app

# main, requirements 두 파일 작업디렉토리로 복사
COPY main.py /app/main.py
COPY requirements.txt /app

# 필요한 종속성 설치
RUN pip install --no-cache-dir -r requirements.txt

CMD [ "python" , "main.py" ]

 

main.py

import requests
from bs4 import BeautifulSoup
import re
import pymysql

# MySQL 연결 설정
conn = pymysql.connect(
    host='10.109.90.46',  # 호스트명
    user='root',    # 데이터베이스 사용자명
    password='1234',  # 데이터베이스 암호
    db='donpa_item',      # 데이터베이스 이름
    port = 3306
)

# 커서 생성
cursor = conn.cursor()

eve_img = []
eve_text = []
eve_date = []
eve_herf = []

# 검색 결과 페이지 URL
url = "https://df.nexon.com/community/news/event/list"

# HTTP GET 요청
response = requests.get(url)

# 응답 데이터 파싱
soup = BeautifulSoup(response.text, "html.parser")

# 이벤트 img 셀렉터
selector_img = "#wrap > section.content.news > article.board_eventlist > ul > li > img"
# 이벤트 b 셀렉터
selector_text = "#wrap > section.content.news > article.board_eventlist > ul > li > b"
# 이벤트 날짜 span 셀렉터
selector_date = "#wrap > section.content.news > article.board_eventlist > ul > li > span"
# 이벤트 링크가 포함된 li 태그들 선택
selector_href = "#wrap > section.content.news > article.board_eventlist > ul > li"

# 선택자에 해당하는 요소들을 모두 찾음
event_img = soup.select(selector_img)
# URL에 프로토콜을 추가하여 출력
for img in event_img:
    src = img.get('src')
    if src.startswith('//'):
        src = 'https:' + src
    eve_img.append(src)
    
 # 선택자에 해당하는 요소들을 모두 찾음
event_text = soup.select(selector_text)
# 각 링크의 텍스트만 추출하여 리스트에 저장
event_text = [link.get_text(strip=True) for link in event_text]
# 결과 출력
for text in event_text:
    eve_text.append(text)



# 선택자에 해당하는 요소들을 모두 찾음
event_dates = soup.select(selector_date)
# 결과 출력
for span in event_dates:
    date_text = span.text.strip().replace('\n', '').strip().replace(' ','')  # span 태그 안의 텍스트>에서 공백 및 줄바꿈 제거
    eve_date.append(date_text)



event_href = soup.select(selector_href)

# 결과 출력
for li in event_href:
    if li.get("data-no"):
        event_url = f"http://df.nexon.com/community/news/event/{li.get('data-no')}?categoryType=0"
        eve_herf.append(event_url)

    else:
        li = li.get("onclick")

        if 'window.location.href' in li:
            # 정규 표현식을 사용하여 값을 추출합니다.
            li = re.findall(r"'(.*?)';", li)

            eve_herf.append("https://df.nexon.com"+''.join(li))
        elif 'neople.openCouponPop()' in li:
            li = "https://df.nexon.com/community/news/event/list"
            eve_herf.append(li)

        elif 'window.open' in li:
            # 정규 표현식을 사용하여 URL 추출
            li = re.search(r"https://[a-zA-Z0-9./-]+", li).group()
            eve_herf.append(li)
 
 # 테이블 초기화 (기존 데이터 삭제)
truncate_sql = "TRUNCATE TABLE donpa_event"
cursor.execute(truncate_sql)


# 데이터베이스에 데이터 삽입
for i in range(len(eve_img)):
    img = eve_img[i]
    text = eve_text[i]
    date = eve_date[i]
    herf = eve_herf[i]

    sql = "INSERT INTO donpa_event (img, text, date, herf) VALUES (%s, %s, %s, %s)"
    cursor.execute(sql, (img, text, date, herf))

# 변경사항 커밋
conn.commit()

# 연결 종료
conn.close()

 

req.txt

requests
beautifulsoup4
pymysql

 

 

이미지 저장하는 명령어

docker build -t sy02229/donpa_event:latest .

이미지 푸쉬하는 명령어

docker push sy02229/donpa_event