[Python/파이썬] BeautifulSoup, Selenium으로 웹크롤링
매장 홈페이지에서 정보를 스크래핑해야할 일이 많았다. 이 경험으로 깨달은 것들을 정리해보려 한다.
1. 크롤링하기 전에 내가 원하는 URL로 아래 코드를 먼저 돌려보기!
res = requests.post('https://www.elandretail.com/store02.do)
res.encoding = 'etf-8' # 또는 'euc-kr'
html = res.text
soup = BeautifulSoup(html, 'html.parser')
soup
- 위 코드를 돌리면 결과가 내가 원하는 태그가 포함되어 나오는 경우가 있고, 아닌 경우가 있다. 예를 들어, 추출 정보가 팝업창 형태로 되어있고, 클릭해도 URL주소가 바뀌지 않는 경우에는 보이는 URL로 가져오기가 어렵다.
이럴 땐 밑의 두 가지 방법으로 해결한다.
- Newtork 콘솔창에서 작동되는 동적 페이지의 우회주소를 통해 Beautifulsoup을 이용하는 방법
- Selenium을 이용하여 직접 웹을 동적으로 실행시키는 방법
경우에 따라 두 가지를 섞어쓰기도 한다. 난 시스템에 적용시킬 목적은 아니라 다양한 방법을 이용했다.
<이랜드리테일 매장정보 크롤링하기>
step1)
필자의 경우에는 주소를 하나씩 눌러보며 동적페이지의 주소가 어떻게 바뀌는 지 파악하였다. Priview버튼을 이용해 내가 원하는 정보가 나타나있는지 살펴봤다. 있다면 Header버튼 눌러 Request URL에 있는 주소를 가져온다. 위의 경우 'https://www.elandretail.com/store02.do?branchID=00110002&lang=000600KO'가 동적페이지로 동작하고 있다는 걸 알게 되어 URL을 수정하여 불러온 html에 정보가 담겨있는 지 다시 확인했다.
step2)
위의 이랜드리테일의 층별안내를 가져오는 데에 층을 하나씩 눌러서 정보를 확인해야했다. 이때 필요한 것이 Selenium 라이브러리이다. 웹페이지를 크롤링하는데 클릭하거나 로그인을 하는 작업등을 수행해준다.
만약 BeautfulSoup을 이용해 태그를 가져오고 싶다면 driver.Page_source를 이용하면 된다.
### TAB이 안먹혀서 INDENT는 알아서 수정하기...
for s in range(51):
driver.get('https://www.elandretail.com/store02.dobranchID=001100{}&lang=000600KO'.format(str(s+1).zfill(2)))
flo_total = driver.find_elements_by_tag_name('b')
store = driver.find_elements_by_tag_name('h2')
for k in range(len(flo_total)):
### 층버튼의 규칙 파악하기! 이랜드의 형태 -> //*[@id="id_00020B1F"]/a/b
driver.find_element_by_xpath('//*[@id="id_00020{}"]/a'.format(flo_total[k].text.zfill(3))).click()
time.sleep(3) # 클릭 후에는 대기를 걸어줘서 HTML이 잘 로딩되게끔..!
name = driver.find_elements_by_class_name('brand')
tel = driver.find_elements_by_class_name('tel')
step3)
가져온 HTML의 태그의 값을 불러올 때는 text함수를 이용한다. 유의할 점은 text는 List에서 사용불가!
따라서 for문을 이용한다. 또한 엑셀 형태로 반환하기 위해 DataFrame형태로 만들었다.
for i, j in zip(name,tel):
f = flo_total[k].text
n = i.text
t = j.text
d = store[len(store)-1].text
df = df.append(pd.DataFrame([[d,f,n,t]], columns=['brand','floor','name','tel']), ignore_index=True)
결과
2. 파이썬 짚고가기
- 새로운 함수
용도 | 함수 | 입력 | 결과 |
입력값을 일정한 길이로 정규화하고 싶을때! | s.zfill(int n) | '1'.zfill(3) | '001' |
for문에 2개 인자를 넣고 싶을때! (2개 인자가 인덱스와 값인 경우는 enumerate) |
zip(a,b) |
for i, j in zip('[1,2],[3,4]'): print(i,j) |
1,3 2,4 |
- BeautifulSoup에서 select함수 이용해 태그 가져오기
용도 | 특징 | 입력 |
태그명 | 태그명만 | soup.select('태그명') |
클래스명 | . 붙이기 | soup.select('.클래스명') |
상위태그와 하위태그 관계 |
> : 바로 아래 위치(자식관계) 띄어쓰기 : 자손관계일때 |
soup.select('상위태그명 > 하위태그명') soup.select('상위태그명 하위태그명') |
아이디명 |
#붙이기 |
soup.select('#아이디명') |
속성의 값 이용 |
태그명뒤에 속성 지정 |
soup.select('태그명[속성1=값1]') |
- Selenium에서 find_element~~이용해 태그 가져오기
한 개 태그를 가져오고 싶다 -> find_element
여러 개 태그를 가져오고 싶다 -> find_elements
로 나눠지며 아래의 표는 element 기준이다. 많이 쓰는 거만 정리했다.
driver.find_element_by_name("태그명") | 태그를 모두 가져오고 싶을 때 유용 |
driver.find_element_by_id("아이디명") | 태그 내에서 하나만 가져올 때 유용 |
driver.find_element_by_xpath("xpath경로") | 반복해서 가져올 때 규칙을 이용하면 유용 |