티스토리 뷰

728x90
반응형

항상 HTML 태그를 파싱하기 까다로운 것들이 있는 것 같아요.

그런 것들을 모아서 정리해볼 생각입니다!

크롤링 공부하면서 계속 추가해 나갈 생각입니다.

 

1. HTML 코드 사이에 태그로 구성되어 있지 않은 텍스트 가져오기

<div>
	<h1>1번 글</h1>
	2021.11.23
	<p>미리보기 내용</p>
	<h1>2번 글</h1>
	2021.11.27
	<p>미리보기 내용</p>
</div>

위 HTML 코드에서 날짜 부분만 가져오기 위한 코드를 작성해보겠습니다.

import requests
from bs4 import BeautifulSoup
html = '''
<div>
<h1>1번 글</h1>
2021.11.23
<p>미리보기 내용</p>
<h1>2번 글</h1>
2021.11.27
<p>미리보기 내용</p>
</div>
'''
soup = BeautifulSoup(html, 'lxml')
titles = soup.find_all('h1')
for title in titles:
	print(title.next_sibling.strip())

next_sibling 을 이용해서 특정 요소 다음에 있는 요소를 선택하는 방식을 사용하였습니다.

실제로 이런 요소들은 CSS Selector 로 가져오기에는 정말 까다롭기 그지 없는 것 같습니다.

그래서 위와 같은 방식을 사용한다면 보다 쉽게 가져올 수 있을 것 같습니다.

 

실행 결과는 아래와 같습니다.

2021.11.23
2021.11.27

 


 

2. Class 이름이 계속 유동적으로 변하는 요소에서 텍스트만 가져오기

<div>
<span class="txt_01">hello</span>
<div class="txt_04">how</div>
<em class="txt_13">are</em>
<pre class="txt_10b">you</pre>
<h3 class="txt_21">my name is</h3>
<h4 class="txt_38a">domdomi</h4>
</div>

위와 같이 태그도 다양하고 class 도 불규칙적으로 변할 때 text들의 list 를 가져오려면 어떻게 해야할까요?

상상력을 발휘해보면 사실 다양한 방법이 있을 수 있는데요.

 

일단 저는 아래와 같이 코드를 작성해보았습니다.

import requests
from bs4 import BeautifulSoup
html = '''
<div>
<span class="txt_01">hello</span>
<div class="txt_04">how</div>
<em class="txt_13">are</em>
<pre class="txt_10b">you</pre>
<h3 class="txt_21">my name is</h3>
<h4 class="txt_38a">domdomi</h4>
</div>
'''
soup = BeautifulSoup(html, 'lxml')
texts = list(soup.find('div').stripped_strings)
print(texts)

div 태그의 자식요소들에 대한 문자열을 가져오기 위해서 stripped_strings 를 사용했습니다. 그냥 strings 를 사용해도 되지만 태그 사이 사이에 존재하는 불필요한 개행문자들도 가져와지기 때문에 stripped_strings를 사용하였습니다.

결과는 아래와 같습니다.

['hello', 'how', 'are', 'you', 'my name is', 'domdomi']

딱 저희가 원하는 문자열만 가져와진 것을 볼 수 있습니다.

stripped_string는 generator object로 반환하기 때문에 별도로 리스트 형태로 보기 위해서 list() 형변환을 해주었습니다.

리스트 내부에 있는 요소를 출력할 때에는 별도로 list() 형변환을 해줄 필요 없습니다.

texts = soup.find('div').stripped_strings
for text in texts:
	print(text)

그저 위와 같이 반복문으로 받아와서 출력해주면 됩니다.

hello
how
are
you
my name is
domdomi

 


3. Class 이름이 계속 유동적으로 변하는 요소에서 텍스트만 가져오기 (2)

이번엔 위에서 활용한 예시에서 class 의 반복되는 패턴을 이용해서 가져와 보겠습니다.

<div>
<span class="txt_01">hello</span>
<div class="txt_04">how</div>
<em class="txt_13">are</em>
<pre class="txt_10b">you</pre>
<h3 class="txt_21">my name is</h3>
<h4 class="txt_38a">domdomi</h4>
</div>

보시다시피 위의 class 에서는 txt_ 가 반복되는 것을 알 수 있습니다.

이를 정규식을 사용해서 가져와보겠습니다.

import re
import requests
from bs4 import BeautifulSoup
html = '''
<div>
<span class="txt_01">hello</span>
<div class="txt_04">how</div>
<em class="txt_13">are</em>
<pre class="txt_10b">you</pre>
<h3 class="txt_21">my name is</h3>
<h4 class="txt_38a">domdomi</h4>
</div>
'''
soup = BeautifulSoup(html, 'lxml')
texts = soup.find_all(attrs={'class':re.compile('^txt\_')})
for text in texts:
	print(text.string)

class 가 txt_ 로 시작하는 요소만 가져오도록 정규식을 사용해서 가져와 보았습니다.

실행 결과는 역시 아래와 같습니다.

hello
how
are
you
my name is
domdomi

 


 

4. 한번에 여러 요소를 가져오기

<div>
<span class="txt_01">hello</span>
<div class="txt_04">how</div>
<em class="txt_13">are</em>
<pre class="txt_10b">you</pre>
<h3 class="txt_21">my name is</h3>
<h4 class="txt_38a">domdomi</h4>
</div>

위 HTML 코드에서 em 태그, pre 태그, h4 태그만 가져와 보도록 하겠습니다.

import re
import requests
from bs4 import BeautifulSoup
html = '''
<div>
<span class="txt_01">hello</span>
<div class="txt_04">how</div>
<em class="txt_13">are</em>
<pre class="txt_10b">you</pre>
<h3 class="txt_21">my name is</h3>
<h4 class="txt_38a">domdomi</h4>
</div>
'''
soup = BeautifulSoup(html, 'lxml')
texts = soup.find_all(['em', 'pre', 'h4'])
for text in texts:
	print(text.string)

결과는 아래와 같습니다.

are
you
domdomi

 


 

5. 한번에 여러 요소를 가져오기 (2)

<div>
<span class="txt_01">hello</span>
<div class="txt_04">how</div>
<em class="txt_13">are</em>
<pre class="txt_10b">you</pre>
<h3 class="txt_21">my name is</h3>
<h4 class="txt_38a">domdomi</h4>
</div>

이번에는 위 모든 요소들 중에서 class를 가지고 있고, class가 txt_ 로 시작하는 요소들 모두를 가져오는 코드를 작성해보겠습니다.

이번에는 위 예시와 달리 함수를 사용해보았습니다.

함수를 사용하게 되면 장점으로는 좀 더 커스터마이징 할 수 있다는 것입니다.

import re
import requests
from bs4 import BeautifulSoup
html = '''
<div>
<span class="txt_01">hello</span>
<div class="txt_04">how</div>
<em class="txt_13">are</em>
<pre class="txt_10b">you</pre>
<h3 class="txt_21">my name is</h3>
<h4 class="txt_38a">domdomi</h4>
</div>
'''

def are_you_domdomi(class_name):
	return class_name and re.compile('^txt\_')
soup = BeautifulSoup(html, 'lxml')
texts = soup.find_all(attrs={'class':are_you_domdomi})
for text in texts:
	print(text.string)

실행결과는 아래와 같습니다.

hello
how
are
you
my name is
domdomi

좀 더 커스터마이징 해서 are, you, domdomi 라는 단어가 포함될 경우만 추출하도록 해보겠습니다.

import re
import requests
from bs4 import BeautifulSoup
html = '''
<div>
<span class="txt_01">hello</span>
<div class="txt_04">how</div>
<em class="txt_13">are</em>
<pre class="txt_10b">you</pre>
<h3 class="txt_21">my name is</h3>
<h4 class="txt_38a">domdomi</h4>
</div>
'''

def are_you_domdomi(name):
	class_name = ''
	if name.attrs.get('class'):
		class_name = name.attrs.get('class')[0]
		if class_name.startswith('txt_'):
			if name.string == 'are' or \
			name.string == 'you' or \
			name.string == 'domdomi':
				return name
                
soup = BeautifulSoup(html, 'lxml')
texts = soup.find_all(are_you_domdomi)
for text in texts:
	print(text.string)

응용하기 위해서 조금 복잡하게 작성하기는 했는데, are_you_domdomi 라는 함수가 하는 역할은 아래와 같습니다.

 

1. class 가 존재하는 요소인가?

2. class가 존재한다면 txt_ 로 시작하는 class를 가지고 있는가?
3. 태그 요소의 문자열이 are 또는 you 또는 domdomi 인가?

 

만약 위 3가지 조건에 해당된다면 가져오게 하였습니다.

출력 결과를 같이 보도록 하겠습니다.

are
you
domdomi

 


추후 꿀팁으로 작성할만한 것들이 더 있다면 이어서 작성하도록 하겠습니다.

혹시 위의 예제 말고도 더 궁금한 예시가 있다면 댓글로 말씀해주시면 추가하도록 하겠습니다!

 

- 끝 -

728x90
반응형
댓글