들어가며..
비정형 데이터인 웹페이지 데이터를 수집하기 위해 Python진영에는 BeautifulSoup이라는 강력한 라이브러리가 존재한다. 이를 사용하여 어떻게 웹 스크래핑을 하는지 간단히 살펴보도록 한다.
목적 데이터를 자동화 프로그램을 통해 수집, 가공하는 행위를 "스크래핑" 혹은 "크롤링" 이라한다.
(웹 크롤링은 자동화된 시스템에 의해 최신의 정보를 수집, 가공하는 조직화된 시스템으로 해당 의미에서 스크래핑과는 약간의 의미 차이가 있다.)
1. Library install
- Anaconda( https://www.anaconda.com/distribution/#download-section ) 가 설치 되어있거나 pip 패키지를 사용할 수 있는 환경
- Requests & BeautifulSoup4 설치
pip install beautifulsoup4
pip install requests
2. Python 공식 홈페이지 스크래핑
2.1. Python 홈페이지 url HTTP 요청
import requests
from bs4 import BeautifulSoup
r = requests.get('https://www.python.org/');
r.encoding
Out[3]: 'utf-8'
r.status_code # HTTP 응답코드
Out[4]: 200
2.2. BeautifulSoup 사용하여 text 를 html 로 파싱
soup = BeautifulSoup(r.text, 'html.parser')
print(soup.prettify())
Out [1]: .....생략
</script>
<!--[if lte IE 7]>
<script type="text/javascript" src="/static/js/plugins/IE8-min.798605d5f7a2.js" charset="utf-8"></script>
<![endif]-->
<!--[if lte IE 8]>
<script type="text/javascript" src="/static/js/plugins/getComputedStyle-min.c3860be1d290.js" charset="utf-8"></script>
<![endif]-->
</body>
</html>
2.3. BeautifulSoup 셀렉터 기능을 사용하여 목적데이터 가져오기
- BeautilfulSoup 셀렉터
soup.title
Out[1]: <title>Welcome to Python.org</title>
soup.title.name
Out[2]: 'title'
soup.title.string
Out[3]: 'Welcome to Python.org'
soup.p
Out[4]: <p><strong>Notice:</strong> While Javascript is not essential for this website, your interaction with the content will be limited. Please turn Javascript on for the full experience. </p>
- A 태그 모두 조회
soup.find_all('a')
Out[1]:
[<a href="#content" title="Skip to content">Skip to content</a>,
<a aria-hidden="true" class="jump-link" href="#python-network" id="close-python-network">
<span aria-hidden="true" class="icon-arrow-down"><span>▼</span></span> Close
</a>,
<a class="current_item selectedcurrent_branch selected" href="/" title="The Python Programming Language">Python</a>,
<a href="/psf-landing/" title="The Python Software Foundation">PSF</a>
... 생략
len(soup.find_all('a')) # 모든 A 태그 수
Out[2]: 206
- A 태그의 href 속성값이 정규식 http[s]? 와 매칭되는 요소 찾기
import re
result = [x for x in list_a if re.search('http[s]?', x['href'])]
result
Out[1]:
[<a href="https://docs.python.org" title="Python Documentation">Docs</a>,
<a href="https://pypi.python.org/" title="Python Package Index">PyPI</a>,
<a href="https://www.facebook.com/pythonlang?fref=ts"><span aria-hidden="true" class="icon-facebook"></span>Facebook</a>,
<a href="https://twitter.com/ThePSF"><span aria-hidden="true" class="icon-twitter"></span>Twitter</a>,
<a href="http://brochure.getpython.info/" title="">Python Brochure</a>,
<a href="https://docs.python.org/3/license.html" title="">License</a>,
... 생략 ]
- 본 예제에서는 Upcoming Event HTML 요소에 접근한다.
- 크롬의 개발자도구( F12 ) 를 사용하여 HTML Element 요소에 접근한다. 목적한 데이터를 찾을 때 용이한 방법으로 목적데이터의 HTML / CSS 구조를 파악한다. 해당 구조를 알아야 BeautifulSoup 셀렉터를 어떻게 사용할 것인지 판단 할 수 있다.
- 목적데이터( Upcoming Events ) 에 접근하기 위해 상위 엘리먼트인 "main-content"를 class로 갖는 section 태그 셀렉트
main_container = soup.find('section', attrs={"class":"main-content"})
Out[25]:
<section class="main-content" role="main">
<div class="row">
(중략...)
<div class="medium-widget event-widget last">
<div class="shrubbery">
<h2 class="widget-title"><span aria-hidden="true" class="icon-calendar"></span>Upcoming Events</h2>
<p class="give-me-more"><a href="/events/calendars/" title="More Events">More</a></p>
<ul class="menu">
<li>
<time datetime="2020-02-18T00:00:00+00:00"><span class="say-no-more">2020-</span>02-18</time>
<a href="/events/python-events/880/">PyCon Namibia 2020</a></li>
<li>
<time datetime="2020-02-20T00:00:00+00:00"><span class="say-no-more">2020-</span>02-20</time>
<a href="/events/python-events/946/">Open Source Festival</a></li>
<li>
<time datetime="2020-02-21T00:00:00+00:00"><span class="say-no-more">2020-</span>02-21</time>
<a href="/events/python-events/890/">PyCon Belarus 2020</a></li>
<li>
<time datetime="2020-02-25T23:00:00+00:00"><span class="say-no-more">2020-</span>02-25</time>
<a href="/events/python-user-group/944/">Visual Diagnostics for Machine Learning with Python</a></li>
<li>
<time datetime="2020-02-29T06:00:00+00:00"><span class="say-no-more">2020-</span>02-29</time>
<a href="/events/python-user-group/950/">Python Mauritius User Group Meetup</a></li>
</ul>
(중략...)
</p>
</div>
</section>
- 위에서 가져온 main_container의 내용을 살펴보면 목적데이터 Upcoming Events가 존재하는 것을 확인할 수 있다. find 태그로 목적데이터에 접근하여 상위(parent) 태그로 위치한 후 findNext 로 ul 엘리먼트 요소를 가져온다.
target_data = main_container.find(text="Upcoming Events").parent.findNext('ul')
print(target_data)
Out[34]:
<ul class="menu">
<li>
<time datetime="2020-02-18T00:00:00+00:00"><span class="say-no-more">2020-</span>02-18</time>
<a href="/events/python-events/880/">PyCon Namibia 2020</a></li>
<li>
<time datetime="2020-02-20T00:00:00+00:00"><span class="say-no-more">2020-</span>02-20</time>
<a href="/events/python-events/946/">Open Source Festival</a></li>
<li>
<time datetime="2020-02-21T00:00:00+00:00"><span class="say-no-more">2020-</span>02-21</time>
<a href="/events/python-events/890/">PyCon Belarus 2020</a></li>
<li>
<time datetime="2020-02-25T23:00:00+00:00"><span class="say-no-more">2020-</span>02-25</time>
<a href="/events/python-user-group/944/">Visual Diagnostics for Machine Learning with Python</a></li>
<li>
<time datetime="2020-02-29T06:00:00+00:00"><span class="say-no-more">2020-</span>02-29</time>
<a href="/events/python-user-group/950/">Python Mauritius User Group Meetup</a></li>
</ul>
- Selector 기능으로 HTML내의 목적한 데이터를 쉽게 접근하여 수집
2.4 목적데이터를 dictionary데이터 구조에 적재.
upcoming_events_dict = {}
for li in target_data.findAll('li'):
upcoming_events_dict[li.time.text] = li.a.text
print(upcoming_events_dict)
Out[1]:
{'2020-02-18': 'PyCon Namibia 2020',
'2020-02-20': 'Open Source Festival',
'2020-02-21': 'PyCon Belarus 2020',
'2020-02-25': 'Visual Diagnostics for Machine Learning with Python',
'2020-02-29': 'Python Mauritius User Group Meetup'}
마치며..
웹스크래핑을 위한 Python의 강력한 외부 라이브러리 bs4 ( BeautifulSoup )와 Requests 를 사용하여 간단한 스크래핑 진행해보았다. bs4는 목적데이터를 가져오기 위해 많은 함수를 제공하고 있고, 이를 활용하여 개발자는 손쉽게 데이터를 수집하고 가공할 수 있다.
Reference