BeautifulSoup4 (1)

honeyricecake·2022년 7월 11일
0

웹스크래핑

목록 보기
2/2

시작 전 beautifulsoup4 와 lxml을 pip install 키워드를 이용하여 설치하여야 한다.

lxml은 xml을 해석하는 프로그램 즉, 파서이다.

기본1(네이버 웹툰)

import requests
from bs4 import BeautifulSoup  # from 모듈이름 import 묘듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

# 우리가 가져오는 html문서를 lxml파서를 통해서 BeautifulSooup객체로 만든 것
soup = BeautifulSoup(res.text, "lxml")
# soup객체 아래에 있는 title엘리먼트들에 접근
print(soup.title)

실행 결과

실재 네이버 웹툰 페이지에서 관리자 도구로 들어갔을 때 볼수 있는 title태그

위의 코드에서
마지막에 soup.title.get_text()만 추가해보자.

이렇게 텍스트만 따서 가져오는 것을 볼 수 있다.

그리고

import requests
from bs4 import BeautifulSoup  # from 모듈이름 import 묘듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

# 우리가 가져오는 html문서를 lxml파서를 통해서 BeautifulSooup객체로 만든 것
soup = BeautifulSoup(res.text, "lxml")
# soup객체 아래에 있는 title엘리먼트들에 접근
#print(soup.title)
#print(soup.title.get_text())
print(soup.a)

이렇게 하면 soup객체가 가지고 있는 모든 내용 중 처음으로 발견되는 element에 대한 내용을 출력하게 된다.

import requests
from bs4 import BeautifulSoup  # from 모듈이름 import 묘듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

# 우리가 가져오는 html문서를 lxml파서를 통해서 BeautifulSooup객체로 만든 것
soup = BeautifulSoup(res.text, "lxml")
# soup객체 아래에 있는 title엘리먼트들에 접근
#print(soup.title)
#print(soup.title.get_text())
#print(soup.a)
print(soup.a.attrs)

attrs는 attributes로

이렇게 딕셔너리 형태로 속성을 가져온다.

href만 가져오고 싶다면
print(soup.a["href"])를 하면 된다. (#menu가 출력)

위의 방식은 해당 웹페이지를 잘 알고 있을 때 사용하기 용이한데 우리는 그렇지 못한 경우가 더 많다.

이 때 사용하는 것이 soup.find() 이다.

Tip.
Ctrl + shift + c로 엘리먼트에 상응하는 코드를 쉽게 찾을 수 있다.

import requests
from bs4 import BeautifulSoup  # from 모듈이름 import 묘듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

# 우리가 가져오는 html문서를 lxml파서를 통해서 BeautifulSooup객체로 만든 것
soup = BeautifulSoup(res.text, "lxml")
# soup객체 아래에 있는 a태그 중 class가 N버튼 업로드인 녀석만 찾아줌
print(soup.find("a", attrs={"class":"Nbtn_upload"}))

출력:
<a class="Nbtn_upload" href="/mypage/myActivity">웹툰 올리기</a>

a 태그 없이 soup.find(attrs={"class":"Nbtn_upload"}) 도 가능하다.

Tip.
html에서 태그와 엘리먼트의 차이
https://opentutorials.org/module/966/6986

import requests
from bs4 import BeautifulSoup  # from 모듈이름 import 묘듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

# 우리가 가져오는 html문서를 lxml파서를 통해서 BeautifulSooup객체로 만든 것
soup = BeautifulSoup(res.text, "lxml")
print(soup.find("li", attrs={"class":"rank01"}))

네이버 인기 급상승 순위 1위 엘리먼트를 가져오는 코드

import requests
from bs4 import BeautifulSoup  # from 모듈이름 import 묘듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

# 우리가 가져오는 html문서를 lxml파서를 통해서 BeautifulSooup객체로 만든 것
soup = BeautifulSoup(res.text, "lxml")
rank1 = soup.find("li", attrs={"class":"rank01"})
print(rank1.a)

그 태그에서도 처음 나오는 a엘리먼트를 가져오는 코드

기본2(네이버 웹툰)

xpath에서 부모와 형제, 자식 노드가 있었고, 이는 bs에서도 그대로 적용할 수 있다.

import requests
from bs4 import BeautifulSoup  # from 모듈이름 import 묘듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

# 우리가 가져오는 html문서를 lxml파서를 통해서 BeautifulSooup객체로 만든 것
soup = BeautifulSoup(res.text, "lxml")
rank1 = soup.find("li", attrs={"class":"rank01"})
print(rank1.a.get_text())
print(rank1.next_sibling)  # 태그 사이에 개행이 있으면 출력이 안 될 수 있음
print(rank1.next_sibling.next_sibling.a.get_text())

next_sibling 이 있으면 previous_sibling도 있다.

또한 parent도 있는데 rank1의 parent는

이 ol태그이므로 내용이 상당히 많을 것임을 예상할 수 있다.

import requests
from bs4 import BeautifulSoup  # from 모듈이름 import 묘듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

# 우리가 가져오는 html문서를 lxml파서를 통해서 BeautifulSooup객체로 만든 것
soup = BeautifulSoup(res.text, "lxml")
rank1 = soup.find("li", attrs={"class":"rank01"})

print(rank1.parent)

그런데 next_sibling이 개행 때문에 좀 찾기가 애매했따.
이렇 때 쓸 수 있는 것이
rank1.find_next_sibling()이다.

import requests
from bs4 import BeautifulSoup  # from 모듈이름 import 묘듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

# 우리가 가져오는 html문서를 lxml파서를 통해서 BeautifulSooup객체로 만든 것
soup = BeautifulSoup(res.text, "lxml")
rank1 = soup.find("li", attrs={"class":"rank01"})

# sibling 중 "li" 정보가 있는 것만 가져옴
print(rank1.find_next_sibling("li").a.get_text())

출력 : 여신강림-216화

똑같이 find_previous_sibling("li") 도 가능하다.

그런데 매번 할 때마다 find_sibing보다는 한번에 쭉 찾아서 쓰는게 낫지 않을까?

당연하다. 그리고 이는 간단하다.
find_next_siblings("li")를 하면 된다.

1 - 2 가우스전자

import requests
from bs4 import BeautifulSoup  # from 모듈일음, import 모듈함수

url = "https://comic.naver.com/webtoon/weekday"
res = requests.get(url)
res.raise_for_status()

soup = BeautifulSoup(res.text, "lxml")
# lxml 파서를 이용해 뷰티풀수프 객체를 만든 것

# 조건에 해당하는 모든 항목을 찾는 find_all
cartoons = soup.find_all("a", attrs={"class":"title"})
# soup 문서 전체 중에서 엘리먼트 이름이 a, 클래스가 타이틀인 모든 항목 가져오기

for cartoon in cartoons:
    print(cartoon.get_text())  # 네이버 웹툰 전체 목록 가져오기
# 클래스 속성이 title인 모든 a항목 가져와서 get text하여 출력

위와 같이 하면 모든 웹툰 제목을 가져올 수 있다.

import requests
from bs4 import BeautifulSoup  # from 모듈일음, import 모듈함수

url = "https://comic.naver.com/webtoon/list?titleId=675554"
res = requests.get(url)
res.raise_for_status()

soup = BeautifulSoup(res.text, "lxml")
# lxml 파서를 이용해 뷰티풀수프 객체를 만든 것

ratings = soup.find_all("div", attrs = {"class":"rating_type"})  # 리스트 객체 리턴됨

sum = 0
count = 0

for rating in ratings:
    score = float(rating.strong.get_text())
    sum += score
    count += 1

print(sum/count)

처음에 모든 strong항목에서 get_text()를 하니 안되는 것들이 많아서
평점만을 가져와야겠다고 생각하여 div 엘리먼트 중 클래스가 rating type인 항목들만 가져오고 하위 항목 중 strong항목을 가져와서 get_text()를 했다.

그리고 count를 쓰는 것보다 ratings.len()을 쓰는게 낫다.

0개의 댓글