python을 활용한 웹툰 크롤링 4단계 - 최종 과제
30 May 2018 | python 크롤링패스트캠퍼스 웹 프로그래밍 수업을 듣고 중요한 내용을 정리했습니다.
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
이 포스팅에서는 크롤링하는 방법에 대해 설명합니다. 본 스크립트는 학습 목적으로만 사용해주세요.
import os
from urllib.parse import urlencode
import requests
from bs4 import BeautifulSoup
def webtoon_crawler(webtoon_id):
"""
webtoon_id 를 매개변수로 입력받아서
웹툰 title, author,description을 딕셔너리로 넘겨주는 함수
:param webtoon_id:
:return:
"""
file_path = 'data/episode_list-{webtoon_id}.html'.format(webtoon_id=webtoon_id)
# file_path 바뀐부분 참고!
url_episode_list = 'http://comic.naver.com/webtoon/detail.nhn'
params = {
'titleId': webtoon_id,
}
if os.path.exists(file_path):
html = open(file_path, 'rt').read()
else:
response = requests.get(url_episode_list, params)
print(response.url)
html = response.text
open(file_path, 'wt').write(html)
soup = BeautifulSoup(html, 'lxml')
h2_title = soup.select_one('div.detail > h2')
title = h2_title.contents[0].strip()
author = h2_title.contents[1].get_text(strip=True)
description = soup.select_one('div.detail > p').get_text(strip=True)
# print(title)
# print(author)
# print(description)
info = dict()
# 웹툰의 정보를 딕셔너리 형태로 받아준다.(title, author, description)
info['title'] = title
info['author'] = author
info['description'] = description
return info
# return된 info안에는 웹툰의 title, author, description정보가 들어가있다.
# 이렇게 return된 info값은 class Webtoon에 활용된다.
def episode_crawler(webtoon_id):
"""
webtoon_id를 매개변수로 입력받아서
webtoon_id, title, url_thumbnail, no, rating, created_date의 정보를 가져오는 크롤러 함수
:param webtoon_id:
:return:
"""
file_path = 'data/episode_list-{webtoon_id}.html'.format(webtoon_id=webtoon_id)
# file_path 바뀐 부분 참고
if os.path.exists(file_path):
html = open(file_path, 'rt').read()
soup = BeautifulSoup(html, 'lxml')
table = soup.select_one('table.viewList')
tr_list = table.select('tr')
episode_lists = list()
# for문 바깥에서 episode_lists라는 빈 리스트를 만든다.
for index, tr in enumerate(tr_list[1:]):
if tr.get('class'):
continue
url_thumbnail = tr.select_one('td:nth-of-type(1) img').get('src')
from urllib import parse
url_detail = tr.select_one('td:nth-of-type(1) > a').get('href')
query_string = parse.urlsplit(url_detail).query
query_dict = parse.parse_qs(query_string)
no = query_dict['no'][0]
title = tr.select_one('td:nth-of-type(2) > a').get_text(strip=True)
rating = tr.select_one('td:nth-of-type(3) strong').get_text(strip=True)
created_date = tr.select_one('td:nth-of-type(4)').get_text(strip=True)
# print(url_thumbnail)
# print(title)
# print(rating)
# print(created_date)
# print(no)
new_episode = Episode(
# 크롤링한 결과를 class Episode의 new_episode 인스턴스로 생성
# class Episode의 인스턴스들을 만든것 (이 부분 이해가 잘 안가는데, 용어가 헷갈려서 그런듯)
# 클래스에 인스턴스 생성 부분 공부가 필요하다.
webtoon_id= webtoon_id,
no=no,
url_thumbnail =url_thumbnail,
title=title,
rating = rating,
created_date = created_date,
)
episode_lists.append(new_episode)
# episode_lists에 Episode인스턴스를 추가
# print(episode_lists)
# 이 정보들을 넣고 새로운 인스턴스들을 만든다.
return episode_lists
# ep = episode_crawler(703845)
# print(q)
# for item in ep:
# print(item.no, item.title)
class Episode:
def __init__(self, webtoon_id, no, url_thumbnail, title, rating, created_date):
self.webtoon_id = webtoon_id
self.no = no
self.url_thumbnail = url_thumbnail
self.title = title
self.rating = rating
self.created_date = created_date
@property
def url(self):
url = 'https://comic.naver.com/webtoon/detail.nhn?'
params = {
'titleId': self.webtoon_id,
# 내가 원하는 webtoon의 id
'no': self.no,
# 내가 원하는 webtoon의 no
}
episode_url = url + urlencode(params)
# urllib, parse, urlencode 다시 읽어보기
return episode_url
class Webtoon:
def __init__(self, webtoon_id):
self.webtoon_id = webtoon_id
info = webtoon_crawler(webtoon_id)
# 위에 만들어놓았던 webtoon_id를 매개변수로 받은 webtoon_crawler함수에서
# return했던 info를 가져온다.
self.title = info['title']
self.author= info['author']
self.description = info['description']
self.episode_list = list()
def update(self):
"""
update함수를 실행하면
해당 웹툰 아이디에 따른 에피소드 정보들을 에피소드 인스턴스로 저장
:return:
"""
result = episode_crawler(self.webtoon_id)
# self.webtoon_id를 하는 이유는 해당 웹툰의 id(즉 내가 얻고 싶은 웹툰의 id)를 가져와야 하니까 -> self
# print(result)
self.episode_list = result
# 내가 원하는 episode의 list = result
# print(self.episode_list) 역시 위의 print(result)와 같은 결과를 가져오는 것을 볼 수 있었다.
if __name__ == '__main__':
# __name__ =='__main__'이 무엇인지는 알겠는데, 이걸 왜 여기서 써야하는 지 모르겠다.
webtoon2 = Webtoon(651673)
print(webtoon2.title)
webtoon2.update()
# print(webtoon2.title)
for episode in webtoon2.episode_list:
# webtoon2의 episode_list를 쭉 돌아서
print(episode.url)
# 결국 우리가 출력하고자 하는 것은 각 episode들의 url