1. beautiful 모듈
BeautifulSoup이란 스크래핑을 하기위해 사용하는 패키이다.
즉, BeautifulSoup은 response.text를 통해 가져온 HTML 문서를 탐색해서 원하는 부분을 뽑아내는 역할을 하는 라이브러리이다.
response.text로 가져온 HTML문서는 단순히
string에 지나지 않으니, 의미있는(?) 분석에 용이한 HTML문서로 변환이 필요하다.
결론적으로, response.text로 가져온 string type의 HTML문서를 BeautifulSoup을 이용해 원하는 부분을 탐색할 수 있게 된다.
이제 beautiful 모듈을 사용해보자.
from bs4 import BeautifulSoup
beautifulsoup을 사용하기 전 html 문자열 파싱에 대해서 공부해보겠다.
html 문자열 파싱이란..
💡 파싱(parsing)은 구문 분석이라고 한다. 문장이 이루고 있는 구성 성분을 분해하고 분해된 성분의 위계 관계를 분석하여 구조를 결정하는 것이다. 즉 데이터를 분해 분석하여 원하는 형태로 조립하여 다시 빼내는 프로그램을 말한다.
beautifulsoup은 html문서의 파싱을 도와주는 모듈이다.
예시의 html 문자열에서 데이터를 파싱해보자.
html = '''
<html>
<head>
<title>BeautifulSoup test</title>
</head>
<body>
<div id='upper' class='test' custom='good'>
<h3 title='Good Content Title'>Contents Title</h3>
<p>Test contents</p>
</div>
<div id='lower' class='test' custom='nice'>
<p>Test Test Test 1</p>
<p>Test Test Test 2</p>
<p>Test Test Test 3</p>
</div>
</body>
</html>'''
요런 html 문서가 있다. 첫번째로 find 함수를 사용해보겠다.
- find 함수
- 특정 html tag를 검색
- 검색 조건을 명시하여 찾고자하는 tag를 검색한다.
soup = BeautifulSoup(html)
soup.find('h3')
출력을 보면 h3태그에 있는 내용임을 확인할 수 있다.
soup.find('p')
동일한 방법으로 p태그의 내용도 찾아 낼 수 있고
soup.find('div', custom = 'nice')
soup.find('div', id = 'lower')
soup.find('div', id = 'upper')
parameter를 추가하여 검색 조건의 범위를 좁힐 수도 있다.
find 함수에서 class를 명시하여 검색결과를 좁히기 위해서는 class_ 를 사용한다.
soup.find('div', class = 'test') # x
soup.find('div', class_ = 'test')
soup.find('div', class_ = 'test2') # None 반환 / test2를 class로 갖는 div tag가 없기 때문
마지막 예시와 같이 없는 조건을 명시하면 None 을 반환한다.
find 함수는 전체 문자열에서 처음으로 조건을 만족하는 내용을 찾아내면 첫번째 내용만을 출력해낸다.
태그의 속성을 조건으로도 find함수를 활용할 수 있다.
- find 함수에 속성 넣기 : attrs
attrs = {'id':'upper', 'class' : 'test'}
soup.find('div', attrs = attrs)
# 속성의 이름과 값을 dict로 객체화 시킨다.
- find_all 함수
앞서 find가 조건에 만족하는 첫번째 하나의 tag만을 찾아준다고 배웠다. 하지만 find_all은 조건에 맞는 모든 tag를 리스트로 반환한다.
soup.find_all('p')
soup.find_all('div', class_ = 'test')
# div tag에 test class인 항목 모두 찾아내서 리스트화
- get_text 함수
- tag안의 value를 추출한다.
- 부모tag의 경우, 모든 자식 tag의 value를 추출한다.
- find_all 이나 select의 결과물인 리스트일 때는 for문을 사용하여 value를 추출할 수 있다.
tag = soup.find('h3')
print(tag)
tag.get_text()
tag = soup.find('div')
print(tag)
tag.get_text()
tag = soup.find('div', id = 'upper')
print(tag)
tag.get_text().strip()
- tag의 attribute 값 추출하기
- 경우에 따라 추출하고자 하는 값이 (value가 아닌) attribute 에도 존재함
- 이 경우에는 검색한 tag에 attribute 이름을 [ ] 연산을 통해 추출가능
- 예) div.find(’h3’)[’title’]
tag = soup.find('h3')
print(tag)
tag['title']
BeautifulSoup 모듈을 이용하여 다양한 방법으로 원하는 tag를 찾을 수 있었다. 그 중에서 가장 빈번히 사용되는 것은 id와 class 속성을 활용하는 것이다.
인터넷 뉴스의 뉴스기사의 제목, 작성자, 작성일, 댓글 개수 등을 추출해보자.
우선 앞에서의 과정처럼 개발자 도구를 열어 elements를 살펴 보겠다.
기사의 큰 제목은 h3태그의 class는 tit-view임을 확인할 수 있다.
다음은 requests로 url을 요청하고 응답으로 받아온 html 문서를 BeautifulSoup 을 이용해서 열어보자
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
resp.text
soup = beautifulSoup(resp.text)
soup
이제 find 함수를 써서 우리가 찾고싶은 태그를 찾아보자. 위에서 확인한 기사의 큰 제목은 h3태그에 tit_view class였다.
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
soup = BeautifulSoup(resp.text)
soup.find('h3', class_ = 'tit_view')
h3 tag + class = tit_view의 결과가 잘 나왔다. 그럼 여기서 태그 안에 있는 텍스트만을 추출해보자.
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
soup = BeautifulSoup(resp.text)
title = soup.find('h3', class_ = 'tit_view')
title.get_text()
제목(태그안에 있는 텍스트)만을 잘 뽑아왔다.
위 과정의 경우는 운이 좋게도(?) h3태그가 큰 제목 하나만을 담당하고 있다. 그럼 여러가지 내용을 갖고 있는 다른 태그를 살펴보자.
기사를 작성한 기자님과 입력한 날짜가 있는 태그를 보자.
작성자와 입력일자는 span이라는 태그와 txt_info 클래스에 있다.
find함수를 사용하여 불러보자.
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
soup = BeautifulSoup(resp.text)
soup.find('span', class_ = 'txt_info')
역시 두 가지 내용물 중 첫번째 내용인 작성자까지만 찾을 수 있다. find함수를 이렇게 처음 만족하는 내용이 있으면 해당 내용을 뱉어내고 멈춘다.
이럴 때엔 find_all 함수를 사용해야 할 것이다.
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
soup = BeautifulSoup(resp.text)
soup.find_all('span', class_ = 'txt_info')
이렇게 두 가지의 결과값이 list화되어 출력되는 것을 확인할 수 있다.
list의 index number를 이용하여 두 개의 결과를 각각 불러낼 수도 있다.
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
soup = BeautifulSoup(resp.text)
soup.find_all('span', class_ = 'txt_info')
print(soup.find_all('span', class_ = 'txt_info')[0])
print(soup.find_all('span', class_ = 'txt_info')[1])
또 다른 방법은 찾으려고 하는 것의 부모 태그를 먼저 찾아줌으로써 필터링 후에 찾는 방법이다.
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
soup = BeautifulSoup(resp.text)
info = soup.find('vid', class_ = 'info_view')
info.find('span', class_ = 'txt_info')
span 태그의 부모 태그가 vid임을 확인하고 먼조 vid 태그 내의 내용을 추린후 span 태그로 한 번더 추리는 방법이다. 이 방법은 부모 태그가 데이터를 많이 담고 있을 때 유용하다.
이번엔 기사의 본문을 전부 크롤링해보자.
url = 'https://news.v.daum.net/v/20190728165812603'
resp = requests.get(url)
soup = BeautifulSoup(resp.text)
article = soup.find('div', class_ = 'article_view')
url에 주소를 객체화시키고 requests.get함수로 url을 불러주어 resp에 객체화해준다.그리고 BeautifulSoup 모듈을 사용해 soup에 utml문서를 객체화한다.
div 태그의 article_view class가 기사의 큰 전체부분을 담당하는 것을 볼 수 있다. 이것을 부모태그로 취급하여 필터링 해보았다.
그런데 article html 문서를 열어보면 해당 본문은 3가지의 dmcf_pid로 분류되는 것을 볼 수 있다. 때문에 attribute을 세가지로 나누어 dict로 만들어야 할 것이다.
attrs1 = {'dmcf_pid' : 'NU9K9rfDgA'}
attrs2 = {'dmcf_pid' : 'NpkG4d9A6j'}
attrs3 = {'dmcf_pid' : 'NUOWg8Qxyd'}
따라서 attrs dict를 3개를 만들어준다.
article_top = article.find('div', attrs = attrs1)
article_mid = article.find('div', attrs = attrs2)
article_bot = article.find_all('p', attrs = attrs3)
top, mid, bottom 에 각각의 attrs dict를 넣어준다. bottom의 경우는 태그를 div가 아닌 p로 받기 때문에 p를 넣어줬다.
top = article_top.find_all('p')
mid = article_mid.find_all('p')
bot = article_bot
각각의 top, mid, bottom article의 p태그 내용을 find all 함수를 사용하여 리스트화 한다.
💡
1. find_all 함수를 사용하여 추출된 리스트가 단순 list가 아닌 ResultSet type 이라는 것을 간과했다.
2. str type이 들어있는 리스트와 ResultSet은 합치기가 불가능하다.
3. find_all()함수는 단일 elements에는 사용이 불가하다.
contents = top + mid + bot
article_contents = ''
for text in contents:
article_contents += text.text
article_contents
ResultSet type의 top, mid, bot object를 합친 contents를 만들어주고, article_contents라는 공백 객체를 만든 뒤 for문을 사용해 contents 안에 각각 tag문을 text함수로 컨텐츠만 뽑아내 article_contents에 넣어주었다.
결과가 잘 나왔다.
참고 : hi-guten-tag.tistory
https://hi-guten-tag.tistory.com/7
[웹 크롤링 - Python] BeautifulSoup4 라이브러리, lxml 모듈
1. BeautifulSoup과 lxml이란? BeautifulSoup이란 스크래핑을 하기위해 사용하는 패키지이고, lxml은 구문을 분석하기 위한 parser이다. 즉, BeautifulSoup은 response.text를 통해 가져온 HTML 문서를 탐색해서 원하는
hi-guten-tag.tistory.com
'python > data crawling' 카테고리의 다른 글
정규표현식으로 tag 찾기 & 정규표현식 정리 (0) | 2023.02.14 |
---|---|
5. CSS selector를 이용하여 tag 찾기 (0) | 2023.02.14 |
3. requests 모듈 (http 통신) (0) | 2023.02.14 |
2. http method (GET, POST) (0) | 2023.02.13 |
1. 개발자 도구, Elements, Network (0) | 2023.02.13 |