티스토리 뷰

728x90
반응형

이번에 가져올 부분은 특정 웹툰의 소개 부분과 회차 부분을 가져와보겠습니다.

이번에는 이전과 다르게 find, find_all 함수가 아닌 CSS Selector 만을 사용해서 가져와보도록 하겠습니다.

우선 웹툰에 대한 정보가 담긴 박스의 구성요소를 살펴보겠습니다.

이미지는 comicinfo 클래스의 thumb 클래스 내에 a 태그로 감싸져있는 img 태그에 해당합니다.

웹툰의 제목은 detail 클래스 안의 h2 태그의 텍스트에 해당되고, 웹툰 소개는 p 태그에 해당하고, 장르는 detail_info 클래스의 genre 클래스 내의 텍스트에 해당되고, 연령 대는 age 클래스 내의 텍스트이며, 각종 버튼 관련된 건 btn group 클래스에 정의되어 있다고 합니다.

 

이제 이를 CSS Selector 로 가져오도록 하겠습니다.

import requests
from bs4 import BeautifulSoup

url = "https://comic.naver.com/webtoon/list?titleId=183559&weekday=mon"
res = requests.get(url)
res.raise_for_status() # 오류 시 종료

soup = BeautifulSoup(res.text, 'lxml')

# 신의 탑 정보 가져오기
_ = 'https://comic.naver.com'
imgInfo = soup.select_one('.comicinfo > .thumb')
detailInfo = soup.select_one('.comicinfo > .detail')
likeInfo = soup.select_one('.u_likeit_module')

imgLink = imgInfo.select_one('a').attrs.get('href')
imgSrc = imgInfo.select_one('img').attrs.get('src')
detailTitle = detailInfo.select_one('h2 > .wrt_nm').previous_sibling.strip()
detailContent = detailInfo.select_one('p').text
detailGenre = detailInfo.select_one('.genre').text
detailAge = detailInfo.select_one('.age').text
# likeCnt = likeInfo.select_one('.u_cnt').text

print(_+imgLink)
print(imgSrc)
print(detailTitle)
print(detailContent)
print(detailGenre)
print(detailAge)
# print(likeCnt)
print(likeInfo)

위 코드를 실행해보면 잘 가져와질 겁니다. 하지만 단 하나가 안가져와집니다. 바로 likeCnt 인데요. likeCnt는 아래 부분을 뜻합니다.

바로 위 이미지에서는 태그가 보이지만, 실제로 Python에서 requests.get() 을 해보면 위 태그가 보이지 않습니다.

/webtoon/list?titleId=183559
https://shared-comic.pstatic.net/thumb/webtoon/183559/thumbnail/thumbnail_IMAG06_b1272b70-7eb4-4c1e-bc08-50bf924e73be.jpg
신의 탑
자신의 모든 것이었던 소녀를 쫓아 탑에 들어온 소년그리고 그런 소년을 시험하는 탑
스토리, 판타지
12세 이용가
<div class="u_likeit_module">
</div>

위 결과는 위에 코드를 실행시켰을 때 나오는 결과입니다.

아래 <div class="u_likeit_module"> 부분이 바로 print(likeInfo) 의 결과물인데요.

왜 Python으로 페이지를 요청했을 때는 나오지 않는 것일까요?

 

그것은 바로 javascript 로 동적으로 로딩되기 때문입니다.

보통 일반적으로 javascript에서는 페이지가 모두 로딩된 이후에 동작하도록 합니다. 아래 예시처럼 말이죠.

// javascript 동적 로딩 함수
window.onload = function(){
	// 코드 내용
}

 

그렇기 때문에 이런 상황에서는 requests.get 으로 요청하는 방법 외에 다른 방법을 사용해야합니다.

방법은 다양하고, 정말 다양한 모듈을 사용할 수 있는데 이후에 Selenium 파트에서 추후 별도의 포스팅으로 다루도록 하겠습니다.

 

이제 남은 부분은 웹툰의 회차 정보입니다.

회차정보에는 회차 대표 이미지와 회자 제목, 링크주소, 평점, 업데이트 날짜가 있습니다.

그리고 주목해야될 점으로는 이미 나온 지 오래된 웹툰의 경우 여러페이지로 회차정보가 이루어져있습니다.

여기서 다음 페이지로 이동하면서 제일 마지막 페이지까지 회차정보를 가져오는 것이 목표입니다.

 

우선 HTML 태그를 보면 회차 정보에 해당하는 부분은 viewList 라는 클래스로 묶여 있고, 각 회차별로 tr 태그로 구성된 것을 확인할 수 있습니다.

그리고 각 tr 태그는 다시 아래와 같은 요소들로 이루어져있습니다.

이제 위 내용을 코드로 가져와보겠습니다.

url = "https://comic.naver.com/webtoon/list?titleId=183559&weekday=mon"
res = requests.get(url)
soup = BeautifulSoup(res.text, 'lxml')

viewList = soup.select('.viewList tr')
_ = 'https://comic.naver.com/webtoon'
for view in viewList[3:]: # 배너 제외
	imgSrc = view.select_one('a > img').attrs.get('src')
	title = view.select_one('.title > a').text
	link = _ + view.select_one('.title > a').attrs.get('href')
	rating = view.select_one('.rating_type > strong').text
	date = view.select_one('td.num').text

위 코드를 실행하면 한 페이지에 대한 회차정보를 모두 가져옵니다.

각 imgSrc, title, link, rating, date 들을 출력하면 아래 같은 결과가 나옵니다.

https://shared-comic.pstatic.net/thumb/webtoon/183559/514/thumbnail_202x120_d342306b-4c73-41eb-b2e3-0e32616c39dc.jpg
3부 95화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=514&weekday=mon
8.99
2021.11.14
https://shared-comic.pstatic.net/thumb/webtoon/183559/513/thumbnail_202x120_2f3a9ebb-052d-4eb8-98f9-7d892fb5e49c.jpg
3부 94화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=513&weekday=mon
8.58
2021.11.07
https://shared-comic.pstatic.net/thumb/webtoon/183559/512/thumbnail_202x120_b2e8d322-f85a-4ee2-9288-f2d436181245.jpg
3부 93화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=512&weekday=mon
8.79
2021.10.31
https://shared-comic.pstatic.net/thumb/webtoon/183559/511/thumbnail_202x120_2134c66c-14e6-43c4-b89e-1d597ff312fd.jpg
3부 92화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=511&weekday=mon
9.51
2021.10.24
https://shared-comic.pstatic.net/thumb/webtoon/183559/510/thumbnail_202x120_8f8343a2-fe6a-425d-98c9-50ecdd1d84cc.jpg
3부 91화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=510&weekday=mon
8.53
2021.10.17
https://shared-comic.pstatic.net/thumb/webtoon/183559/509/thumbnail_202x120_2a95c7e9-939a-41cd-9f7a-1b7de0144942.jpg
3부 90화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=509&weekday=mon
9.37
2021.10.10
https://shared-comic.pstatic.net/thumb/webtoon/183559/508/thumbnail_202x120_d9fb0285-7770-4ef8-8c95-73d4ef3b3ac3.jpg
3부 89화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=508&weekday=mon
7.24
2021.10.03
https://shared-comic.pstatic.net/thumb/webtoon/183559/507/thumbnail_202x120_90d12507-e686-4860-8081-9a65b6a81cc9.jpg
3부 88화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=507&weekday=mon
8.70
2021.09.26
https://shared-comic.pstatic.net/thumb/webtoon/183559/506/thumbnail_202x120_879a253b-f677-47b6-bddb-8a2dc8dd01c4.jpg
3부 87화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=506&weekday=mon
8.39
2021.09.19
https://shared-comic.pstatic.net/thumb/webtoon/183559/505/thumbnail_202x120_bcc52d63-75a5-49bf-b1e2-145dbb835c90.jpg
3부 86화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=505&weekday=mon
7.92
2021.09.12

이제 한 페이지에 대한 회차 정보가 아니라 여러 페이지에 대한 회차정보를 가져오기만 하면 끝납니다.

페이지가 넘어갈 때의 패턴을 분석해보았더니,

2 페이지로 넘어가게 되면 URL이 아래와 같이 변경되는 것을 알 수 있습니다.

https://comic.naver.com/webtoon/list?titleId=183559&weekday=mon&page=2

기존의 URL에서 뒤에 &page=2 가 추가된 것입니다. 그럼 다른 페이지로의 이동 또한 기존 URL에 page 만 추가하면 되겠죠.

 

그럼 마지막 페이지는 어떻게 알 수 있을까요? 반복문을 돌리게 되면 마지막 페이지까지 크롤링 후 프로그램을 종료시켜야 하기 때문입니다. 현 시점 기준으로 마지막 페이지는 52페이지가 끝입니다.

보아하니 마지막 페이지일 경우 다음> 버튼이 사라진 것을 확인할 수 있습니다.

그럼 이제 다음 버튼이 사라졌을 경우에 마지막으로 페이지를 크롤링 후 프로그램을 종료하면 되겠죠.

바로 코드로 작성해보기 전에 다음> 버튼에 대한 HTML 코드를 살펴보겠습니다.

저는 여기서 a 태그의 next 클래스가 존재하지 않을 경우를 마지막 페이지로 판단하겠습니다.

 

이제 코드로 작성해보겠습니다.

import requests
from bs4 import BeautifulSoup

page = 1
while True:
	url = "https://comic.naver.com/webtoon/list?titleId=183559&weekday=mon&page="+str(page)
	res = requests.get(url)
	soup = BeautifulSoup(res.text, 'lxml')

	viewList = soup.select('.viewList tr')
	_ = 'https://comic.naver.com/webtoon'
	for view in viewList[3:]: # 배너 제외
		imgSrc = view.select_one('a > img').attrs.get('src')
		title = view.select_one('.title > a').text
		link = _ + view.select_one('.title > a').attrs.get('href')
		rating = view.select_one('.rating_type > strong').text
		date = view.select_one('td.num').text
		print(imgSrc)
		print(title)
		print(link)
		print(rating)
		print(date)
	if soup.select_one('a.next') == None:
		print('last page was ' + str(page))
		break
	page = page + 1

while문으로 무한루프를 돌면서 만약에 다음 버튼이 없어졌을 경우 마지막 페이지임을 알고, 마지막 페이지가 몇 페이지인지 출력 후 프로그램을 종료하도록 했습니다.

 

실행 결과는 좀 길어서 생략해서 출력하면 아래와 같습니다.

https://shared-comic.pstatic.net/thumb/webtoon/183559/514/thumbnail_202x120_d342306b-4c73-41eb-b2e3-0e32616c39dc.jpg
3부 95화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=514&weekday=mon
8.99
2021.11.14
https://shared-comic.pstatic.net/thumb/webtoon/183559/513/thumbnail_202x120_2f3a9ebb-052d-4eb8-98f9-7d892fb5e49c.jpg
3부 94화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=513&weekday=mon
8.58
2021.11.07
https://shared-comic.pstatic.net/thumb/webtoon/183559/512/thumbnail_202x120_b2e8d322-f85a-4ee2-9288-f2d436181245.jpg
3부 93화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=512&weekday=mon
8.79
2021.10.31

...중략...

https://shared-comic.pstatic.net/thumb/webtoon/183559/4/inst_thumbnail_20100719122044.jpg
3화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=4&weekday=mon
9.95
2010.07.19
https://shared-comic.pstatic.net/thumb/webtoon/183559/3/inst_thumbnail_20100709191720.jpg
2화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=3&weekday=mon
9.93
2010.07.12
https://shared-comic.pstatic.net/thumb/webtoon/183559/2/inst_thumbnail_20100702181825.jpg
1화
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=2&weekday=mon
9.91
2010.07.05
https://shared-comic.pstatic.net/thumb/webtoon/183559/1/inst_thumbnail_20100630151359.jpg
예고편
https://comic.naver.com/webtoon/webtoon/detail?titleId=183559&no=1&weekday=mon
9.88
2010.06.30
last page was 52

보시다시피 쭉 출력되다가 마지막에 last page was 52 로, 마지막 페이지가 52 페이지라는 것을 알려줍니다.

 

이렇게 이번에는 파이썬으로 네이버 웹툰 소개 및 회차 정보 가져오기를 해보았습니다.

 

 

 - 끝 -

728x90
반응형
댓글