[웹크롤링] 인터넷에서 캐릭터 정보를 가져오자 - 던파모아 활용(5)

2022. 4. 28. 20:00던전앤파이터/던파 타임라인 훔쳐보기

타임라인 불러오기에 들어가기 앞서,

여태껏 작성해왔던 코드 전문을 보완하고 정리하는 작업을 진행해보자.

우선, 우리는 여태껏 "말걸면"이라는 검색어로만 검색을 진행해왔다.

하지만 해당 검색어를 통해 찾을 수 있는 데이터는 너무나도 적다.

우리는 최대한 많은 데이터를 확보하는 것이 목표이므로,

생각나는 검색어를 리스트(List)로 저장하여 그것을 검색어로 활용하여

캐릭터의 서버와 이름까지 추출하는 과정을 진행해보겠다.

 

 

KEYWORDS = ["말걸면", "레이드돌고", "케처", "buffalo", "카쉬파"]

먼저, 검색어를 리스트 타입의 전역변수로 초기화한다.

실제로 사용할 경우는, 훨씬 더 많은 검색어를 생각하여 초기화하면 된다.

 

 

# 이전까지 사용했던 URL 인코딩 함수 #
def urlEncoding(keyword_):
    encoded_keyword = parse.quote(keyword_)
    return encoded_keyword
# 우리가 새로 사용할 URL 인코딩 함수 #
def urlEncoding(keyword_):
    encoded_keyword = []
    for word in keyword_:
        encoded_keyword.append(parse.quote(word))
    return encoded_keyword

encoded_keyword 디버깅

 

이전에는 "말걸면"이라는 검색어 하나만을 사용한다는 가정 하에 코드 작성이 진행되었으나,

이제는 여러 개의 검색어를 사용할 것이므로 검색어를

URL 인코딩 시켜주는 함수를 수정해보도록 하자.

먼저, urlEncoding 함수로 전달되는 인자(keyword_)는

당연히 리스트 타입일 것이다. (이전에는 "말걸면"이라는 문자열 한 개였다.)

이 리스트 타입(keyword_)에 들어있는 문자열 각각(word)을

for문을 이용하여 (for word in keyword_)

URL 인코딩 시킨 뒤 반환하는 작업을 수행시키자.

수정한 함수를 사용하면 위의 디버깅 사진과 같이 KEYWORDS에 저장한

리스트 타입인 encoded_keyword에는 5 개의 단어가 URL 인코딩된 상태로

저장되어 있음을 확인할 수 있다.

 

 

# 이전까지 사용했던 모험단 URL 추출 함수 #
def advURL(adv_keyword):
    adv_url_ = []
    encoded_keyword = urlEncoding(adv_keyword)
    try:
        source_code = urlopen(ADV_SEARCH + encoded_keyword).read()
        soup = BeautifulSoup(source_code, "html.parser")
        tag_a = soup.find('div', class_='Wrapper-sc-1noqpd4-1 gfctoN').find_all('a')
        for value_a in tag_a:
            if 'href' in value_a.attrs and 'class' in value_a.attrs:
                if value_a.attrs['class'] == ["Row-sc-jrfk9d-1", "hiKazl"]:
                    adv_url_.append(HOMEPAGE_URL + value_a.attrs['href'])
    except:
        print("Error occurred on extracting adventure's URL process.")
    return adv_url_
# 우리가 새로 사용할 모험단 URL 추출 함수 #
def advURL(adv_keyword):
    adv_url_ = []
    encoded_keyword = urlEncoding(adv_keyword)
    for word in encoded_keyword:
        try:
            source_code = urlopen(ADV_SEARCH + word).read()
            soup = BeautifulSoup(source_code, "html.parser")
            tag_a = soup.find('div', class_='Wrapper-sc-1noqpd4-1 gfctoN').find_all('a')
            for value_a in tag_a:
                if 'href' in value_a.attrs and 'class' in value_a.attrs:
                    if value_a.attrs['class'] == ["Row-sc-jrfk9d-1", "hiKazl"]:
                        adv_url_.append(HOMEPAGE_URL + value_a.attrs['href'])
        except:
            print("Error occurred on extracting adventure URL process.")
    return adv_url_
ADV_SEARCH = "https://dunfamoa.com/characters/adventure?search="

 

이번에는 모험단 URL 추출 함수 부분이다.

여기서는 이미 우리가 모험단 URL을 리스트 형식으로 저장해왔기 때문에

따로 리스트 변수를 추가로 선언할 필요 없이 이전 사용하던 리스트(adv_url_)를

그대로 사용하면 된다.

수정된 URL 인코딩 함수에서 리스트 타입의 값이 반환될 것이기 때문에

(encoded_keyword = urlEncoding(adv_keyword))

반환된 값을 갖고 있는 encoded_keyword의 문자열 각각(word)에 대하여

(for word in encoded_keyword)

ADV_SEARCH에 이어붙여서(ADV_SEARCH + word)

이전과 같은 작업들을 진행해나가면 된다.

캐릭터 추출 부분은 복수의 사이트를 크롤링한다는 가정 하에

작성되었기 때문에 수정할 필요가 없다.

 

 

현재까지의 과정 도식화 및 기능 대분류

 

마지막으로, 향후 코드가 계속 추가되며 지금보다 난잡해질 수 있기 때문에

미리 코드를 정리하는 작업을 진행해보자.

먼저, 우리는 코드가 어떻게 동작하는지 생각해볼 필요가 있다.

위 사진과 같이 하나하나 나누어보면,

 

  1. 검색어를 입력으로 한다.
  2. 검색어가 URL로써 기능할 수 있도록 검색어를 URL 인코딩한다. (def urlEncoding())
  3. 인코딩된 입력을 사용하여 모험단의 URL을 추출한다. (def advURL())
  4. 추출한 모험단 URL 내 존재하는 캐릭터들의 서버와 이름을 추출한다. (def charList())
  5. 추출한 서버와 이름들을 dictionary 타입으로 저장한다. (def charDict())

 

결국 위 사진과 같이 2~3번은 모험단 URL을 추출하기 위한 과정,

4~5번은 캐릭터들의 서버와 이름을 추출하기 위한 과정이기 때문에

이 두 가지 과정을 각각 하나의 클래스로 묶도록 하자.

 

 

class extractAdv:
    def __init__(self, adv_keyword):
        self.adv_keyword = adv_keyword

    def urlEncoding(self, keyword_):
        encoded_keyword = []
        for word in keyword_:
            encoded_keyword.append(parse.quote(word))
        return encoded_keyword
        #---------검색어를 URL 인코딩----------#

    def advURL(self):
        adv_url_ = []
        encoded_keyword = self.urlEncoding(self.adv_keyword)
        for word in encoded_keyword:
            try:
                source_code = urlopen(ADV_SEARCH + word).read()
                soup = BeautifulSoup(source_code, "html.parser")
                tag_a = soup.find('div', class_='Wrapper-sc-1noqpd4-1 gfctoN').find_all('a')
                for value_a in tag_a:
                    if 'href' in value_a.attrs and 'class' in value_a.attrs:
                        if value_a.attrs['class'] == ["Row-sc-jrfk9d-1", "hiKazl"]:
                            adv_url_.append(HOMEPAGE_URL + value_a.attrs['href'])
            except:
                print("Error occurred on extracting adventure URL process.")
        return adv_url_
        #-----------------------던파모아활용(1~3)-----------------------#

 

urlEncoding 함수와 advURL 함수를 extractAdv라는 클래스의 메소드로 만들었다.

클래스를 초기화할 때, 검색어(adv_keyword)를 인자로 받아

이를 self.adv_keyword에 저장하여

클래스 내 어느 함수에서라도 self.adv_keyword를

사용할 수 있도록 __init__ 함수에서 초기화하였다.

기존 advURL 함수가 인자로 요구했던 부분은 extractAdv가 초기화될 때,

(extractAdv 클래스 객체를 만들 때)

인자로 받기 때문에 필요가 없어졌으니 지우도록 하자. 

 

 

class extractChar:
    def __init__(self, adv_url):
        self.adv_url = adv_url

    def charDict(self, char_url_):
        char_info = char_url_.split('/')
        char_dict = {'serverId':char_info[2], 'characterName':char_info[3]}
        return char_dict
        #------------------캐릭터 이름 및 서버 저장-------------------#

    def charList(self):
        char_list_ = []
        for url in self.adv_url:
            try:
                source_code = urlopen(url).read()
                soup = BeautifulSoup(source_code, "html.parser")
                tag_a = soup.find('div', class_='Outer-sc-1cu42wh-3 kCIUEG').find_all('a')
                for value_a in tag_a:
                    if 'href' in value_a.attrs:
                        char_url = value_a.attrs['href']
                        char_list_.append(self.charDict(char_url)) 
            except:
                print("Error occurred on extracting character's name process.")
        return char_list_
        #---------------------------던파모아활용(4)----------------------------#

 

charDict 함수와 charList 함수를 extractChar라는 클래스의 메소드로 만들었다.

클래스를 초기화할 때, 모험단 주소 리스트(adv_url)를 인자로 받아

이를 self.adv_url에 저장하여

클래스 내 어느 함수에서라도 self.adv_url을

사용할 수 있도록 __init__ 함수에서 초기화하였다.

기존에 charList 함수가 인자로 요구했던 부분은 extractChar가 초기화될 때,

(extractChar 클래스 객체를 만들 때)

인자로 받기 때문에 필요가 없어졌으니 지우도록 하자.

 

 

 

그리고 더욱 더 깔끔하게 정리하기 위해

홈페이지 주소같은 const들과( ex) consts.py 만들어 저장. )

더 이상 확인할 필요가 사라진 두 개의 클래스를 동일 디렉토리 내

새로운 파일로 만들어서 저장하자. ( ex) extracting.py 만들어 저장. )

 

 

# consts.py
ADV_SEARCH = "https://dunfamoa.com/characters/adventure?search="
HOMEPAGE_URL = "https://dunfamoa.com"
# extracting.py

from consts import*
from urllib import parse
from urllib.request import urlopen
from bs4 import BeautifulSoup

class extractAdv:
    def __init__(self, adv_keyword):
        self.adv_keyword = adv_keyword

    def urlEncoding(self, keyword_):
        encoded_keyword = []
        for word in keyword_:
            encoded_keyword.append(parse.quote(word))
        return encoded_keyword
        #---------검색어를 URL 인코딩----------#

    def advURL(self):
        adv_url_ = []
        encoded_keyword = self.urlEncoding(self.adv_keyword)
        for word in encoded_keyword:
            try:
                source_code = urlopen(ADV_SEARCH + word).read()
                soup = BeautifulSoup(source_code, "html.parser")
                tag_a = soup.find('div', class_='Wrapper-sc-1noqpd4-1 gfctoN').find_all('a')
                for value_a in tag_a:
                    if 'href' in value_a.attrs and 'class' in value_a.attrs:
                        if value_a.attrs['class'] == ["Row-sc-jrfk9d-1", "hiKazl"]:
                            adv_url_.append(HOMEPAGE_URL + value_a.attrs['href'])
            except:
                print("Error occurred on extracting adventure URL process.")
        return adv_url_
        #-----------------------던파모아활용(1~3)-----------------------#

class extractChar:
    def __init__(self, adv_url):
        self.adv_url = adv_url

    def charDict(self, char_url_):
        char_info = char_url_.split('/')
        char_dict = {'serverId':char_info[2], 'characterName':char_info[3]}
        return char_dict
        #------------------캐릭터 이름 및 서버 저장-------------------#

    def charList(self):
        char_list_ = []
        for url in self.adv_url:
            try:
                source_code = urlopen(url).read()
                soup = BeautifulSoup(source_code, "html.parser")
                tag_a = soup.find('div', class_='Outer-sc-1cu42wh-3 kCIUEG').find_all('a')
                for value_a in tag_a:
                    if 'href' in value_a.attrs:
                        char_url = value_a.attrs['href']
                        char_list_.append(self.charDict(char_url)) 
            except:
                print("Error occurred on extracting character's name process.")
        return char_list_
        #---------------------------던파모아활용(4)----------------------------#
# main.py
# Authorized by 죤씨나
from consts import*
from extracting import*

KEYWORDS = ["말걸면", "레이드돌고", "케처", "buffalo", "카쉬파"]

if __name__ == "__main__":
    adv_url = extractAdv(KEYWORDS).advURL()
    char_list = extractChar(adv_url).charList()

    for data in char_list:
        print(data)

 

지금까지 작성했던 코드들을

consts.py, extracting.py, main.py

3 개로 분할하였다.

동작이 잘 되는지 확인해보자.

 

출력된 리스트의 일부분

 

동작이 잘 된다.

이제 진짜 타임라인 불러오기로 가보자.

 

 

바칼 - 죤씨나