매우 간단한 웹크롤링 - 이마트 휴점일

2018. 4. 17. 03:48Programming/Python

파이썬의 장점에는 배우기 쉽다, 인터프리터 기반의 스크립트 언어다 뭐 여러가지가 있지만 그 중 가장 큰 장점은 라이브러리가 많다는 점 아닐까 싶습니다.


자취하는 입장으로서 일요일에 아무 생각없이 이마트에 갔다가 휴점일이라서 낭패를 봤던 적이 많은데요. 그래서 나갈 때마다 폰으로 휴점일을 직접 검색해보는 게 불편하다 생각해서, 이를 따로 크롤링해서 띄워주면 어떨까 하는 생각을 했습니다.


찾아보니 그렇게까지 어렵지는 않더라구요.


이마트 휴점일은 다음 페이지에 게시되어 있습니다.


https://store.emart.com/main/holiday.do



여기서 점포명을 검색하면 나오지만, 웹 크롤링에서는 조금 다르게 할 생각입니다.


파이썬에서 기본적으로 HTMLParser를 지원합니다만, 여기에선 더 많이 쓰이는 BeautifulSoup라는 라이브러리를 설치해보도록 하겠습니다.


pip로 간단히 설치가 가능합니다. (저는 Python 3.5 버전을 사용합니다.)


$ pip3 install beautifulsoup4

pip는 파이썬 라이브러리를 간단하게 받을 수 있는 프로그램입니다. 윈도우 명령 프롬프트나 맥, 리눅스의 쉘에서 실행해서 받을 수 있습니다.


파이썬 스크립트 상에서 실행하기 위해서는 다음과 같이 사용하면 됩니다.


from bs4 import BeautifulSoup

참 쉽죠?


자세한 사용법은 생략하고, 일단 위 URL에서 데이터를 가져와 보도록 하겠습니다.


import requests
from bs4 import BeautifulSoup

session = requests.session()
r = session.get("https://store.emart.com/main/holiday.do")
print(r.text)


위 코드를 실행시키면 HTML로 되어있는 데이터가 일제히 출력됩니다.

여기서 우리는 원하는 지점과, 그 지점의 휴점일을 찾아야합니다.


HTML을 읽다 보니, 적당한 부분을 찾았습니다.


<div class="dtable_list mt20" id="d-store-list">
							               			
    <table id="d-store-A"  border="1">
	<caption>04월 휴점일 서울 지역 테이블 (이 표는 04월 휴점일 서울지역에 대한 리스트로 점포명, 휴점일, 전화에 대한 내용을 포함하고 있습니다.)</caption>
		<colgroup><col style="width:150px;" /><col /><col style="width:170px;" /></colgroup>
			<thead>
				<tr>
				<th scope="col">점포명</th>
				<th scope="col">휴점일</th>
				<th scope="col">전화</th>
				</tr>
			</thead>
			<tbody>
	                   
	               		<tr>
				<td>SSG 청담점</td>
				<td>04월 
						08일, 22일
					                
				</td>
				<td>02-6947-1200</td>
				</tr>
	               			
	               			
	               		<tr>
				<td>가든5점</td>
				<td>04월 
						08일, 22일
					                
				</td>
				<td>02-411-1234</td>
				</tr>
	               			
	               			
	               		<tr>
				<td>가양점</td>
				<td>04월 
						08일, 22일
					                
				</td>
				<td>02-2101-1234</td>
				</tr>


HTML을 보니, 크롤링하게 좋게 아예 지역별로 카테고라이즈 해놓은 걸 볼 수 있습니다. table 태그로 구성되어 있고, id는 d-store-# 식으로 정렬해놓았습니다.


여기에서 d-store-A는 서울 지역의 지점들을 의미합니다.

저는 이문동에 살고 있으므로 이문점의 정보가 필요한데요, 그렇다면 이 d-store-A id를 가진 table 태그 내부를 탐색하면 되겠군요.


먼저, 분석을 위해서 얻어낸 html의 내용을 BeautifulSoup 객체로 변환합니다.


soup = BeautifulSoup(r.text, "html.parser")

첫번째 인자로는 html 텍스트 내용을, 두번째 인자는... 분석 방법의 종류? 를 의미하는 것 같습니다. html.parser를 써도 좋고, lxml 같은 것을 써도 좋습니다. 정확한 차이점은 아직 저도 잘 모릅니다.


BeautifulSoup 객체가 만들어지고 나면, 메소드를 불러서 원하는 결과만 따로 필터링해서 뽑아낼 수 있습니다.


다음은 find_all() 메소드 실행 결과입니다. find_all()은 인자로 들어간 조건에 만족하는 부분을 전부 다 찾아서 resultSet으로 반환해주는 메소드입니다.


>>> find_all("table", id="d-store-A")
[<table border="1" id="d-store-A">
<caption>04월 휴점일 서울 지역 테이블 (이 표는 04월 휴점일 서울지역에 대한 리스트로 점포명, 휴점일, 전화에 대한 내용을 포함하고 있습니다.)</caption>
<colgroup><col style="width:150px;"/><col/><col style="width:170px;"/></colgroup>
<thead>
<tr>
<th scope="col">점포명</th>
<th scope="col">휴점일</th>
#... 너무 많아서 중간 생략합니다
</tr>
</tbody>
</table>]


정상적으로 id가 d-store-A인 table 태그 부분만 따로 필터링되었습니다.

이 때, 주의해야 할 점은 앞 뒤로 [ ]가 나온단 것입니다. find()는 일치하는 결과를 하나만 찾아주지만, find_all은 모두 다 찾아서 resultSet이란 객체로 돌려줍니다. 사실상 list와 똑같습니다.


이 결과도 너무 많으므로, 이곳에서 다시 데이터 전처리를 해야합니다.

일단 정보들은 전부 <td> 태그 속에 들어있으므로, td만 따로 뽑아내는 작업을 해봅시다.


>>>soup.find_all("table", id="d-store-A")[0].find_all("td")
[<td>SSG 청담점</td>, <td>04월 
								 	08일, 22일
					                
									 </td>, <td>02-6947-1200</td>, <td>가든5점</td>, <td>04월 
								 	08일, 22일
					                
									 </td>, <td>02-411-1234</td>, <td>가양점</td>, <td>04월 
								 	08일, 22일
					                
									 </td>, <td>02-2101-1234</td>, <td>구로점</td>, <td>04월 
								 	08일, 22일
					                
									 </td>, <td>02-2009-1234</td>, <td>마포점</td>, <td>04월 
								 	08일, 22일
					                
									 </td>, <td>02-2197-1234</td>, <td>명일점</td>, <td>04월 
								 	08일,
#너무 많아서 이하 생략

이번에는 td를 기준으로 필터링 했으므로, 여러 개의 데이터가 반환되었습니다.

이제 태그를 가지치기 하고, 데이터만 얻어내면 됩니다.

이렇게 얻어낸 BeautifulSoup 객체에서 텍스트만 잘라내고 싶으면 getText()를 사용하면 태그를 제외한 텍스트만 반환이 됩니다.


아래는 완성된 코드입니다.

import requests
from bs4 import BeautifulSoup

session = requests.session()
r = session.get("https://store.emart.com/main/holiday.do")
soup = BeautifulSoup(r.text, "html.parser")
tag = soup.find_all("table", id="d-store-A")[0].find_all("td")
count = 0
for td in tag:
    if td.get_text()=="이문점":
        count = 1
    if count >= 1:
        if count == 2:
            text = td.get_text().split()
            print(text[0] + " " + text[1] + " " + text[2])
        else:
            print(td.get_text())
        count += 1
        if count == 3:
            count = 0



이제 데이터는 얻어내는 데 성공했습니다. 웹 서버같은 곳에 넣어서 매 달 1일마다 갱신하도록 만들면 좀 덜 불편하겠군요.