크롤링이라는 용어를 참 많이 들었는데, 할 기회가 없어서 무엇인지도 몰랐고, 어떻게 하는지는 더더욱 몰랐다. 면접을 준비하면서 무엇인지 알게 되었고, 그러다가 과제에서 데이터를 수집할때 필요하게 되어 크롤링을 하게 되었다.
웹페이지에서 원하는 데이터를 추출하는 것을 말한다. 나는 이번에 울산장애인시설현황에서 시설 이름과, 그 시설의 위치(구/군)을 추출해야 했다.
크롤링 시 파이썬을 많이 이용하는데, 많은 사람들이 이용하는 BeautifulSoup라는 파이썬 라이브러리가 크롤링을 도와준다. 시작은 다음과 같이 하면 된다.
import re
from bs4 import BeautifulSoup
url = "https://www.ulsan.go.kr/u/welfare/contents.ulsan?mId=001007003000000000"
# url 주소 받기
res = requests.get(url)
# 상태 확인
res.raise_for_status()
# 응답받은 HTML 내용을 BeautifulSoup 클래스의 객체 형태로 생성/반환
soup = BeautifulSoup(res.text, "lxml")
크게 많이 쓰는 함수는 find와 find_all이다.
# 태그가 tbody인 모든 요소를 다 찾기. 리스트 형태로 반환된다.
item = soup.find_all("tbody")
# 태그가 th인 첫번째 요소 찾기
real_name = name.find("th")
그리고 유용하게 많이 썼던 것은 find_all로 받은 리스트를 for문으로 돌려서 그 자식 객체에서 필요한 요소를 가져오는 것이다.
item = soup.find_all("tbody")
for it in item:
names = it.find_all("tr") # 하나의 tbody 안에 있는 모든 tr을 찾는다.
그리고 find_all을 통해 가져온 리스트는 당연히 인덱스를 통해 하나씩 접근 할 수 있다.
데이터를 추출할 때는 get_text()를 사용해주어야 태그가 제외되고 순수 text만 추출된다.
real_name = name.find("th").get_text() # th태그로 감싸져있는 text가져오기
도중에 AttributeError: 'NoneType' object has no attribute 'get_text' 에러가 났다. 이 경우에는 태그로 감싸져있는 텍스트가 없다는 것이다. 따라서 오류가 날 경우는 pass 하도록 try/except문을 사용해 주었다.
추출한 데이터를 csv파일에 담을 수 있다. 기본적인 작성 방법은 다음과 같다.
import csv
file_name = "ulsan_disabled_facility.csv"
f = open(file_name, "w", encoding="utf-8-sig", newline="")
writer = csv.writer(f)
# 컬럼명 지정하기
title = ["시설명", "위치"]
writer.writerow(title)
#데이터 작성하기
writer.writerow(...)
import requests
from bs4 import BeautifulSoup
import csv
url = "https://www.ulsan.go.kr/u/welfare/contents.ulsan?mId=001007003000000000"
res = requests.get(url)
res.raise_for_status()
file_name = "ulsan_disabled_facility.csv"
f = open(file_name, "w", encoding="utf-8-sig", newline="")
writer = csv.writer(f)
title = ["시설명", "위치"]
writer.writerow(title)
soup = BeautifulSoup(res.text, "lxml")
item = soup.find_all("tbody")
for it in item:
names = it.find_all("tr")
for name in names:
item_info = []
try:
# Todo: 계 빼기, /t/n/r 없애기
real_name = name.find("th").get_text().replace("\t", "").replace("\n", "").replace("\r", "")
if real_name == "계":
continue
else:
item_info.append(real_name)
address = name.find_all("td")
for add in address:
adds = add.get_text().split()
if adds[0] in ['북구', '남구', '동구', '중구', '울주군']:
item_info.append(adds[0])
except:
pass
if item_info != []:
writer.writerow(item_info)