사람이 살아가는 데 필수적인 의식주 중에서 주거는 가장 기본적인 생활 요소입니다.
그래서 '크롤링해볼까?' 생각이 들어하게 되었습니다.
- 직방 선정 이유
부동산 플랫폼은 네이버부동산, 다방, 직방, 부동산플래닛 등 다양하게 있습니다.
최초에는 특정 지역에서의 부동산 월세 매물을 가져오려 했으나,
특정 지역 한정해서 클릭과 크롤링을 진행하기란, 생각보다
어려운 일이었습니다.
고민을 하더 찰나 찾은 것이
매물번호를 기준으로 크롤링을 하는 것입니다.
https://www.zigbang.com/home/oneroom/items/43932924
매물번호로 url에 접속하면 다음과 같은 창이 뜨게 됩니다.
우측에 매물에 대한 정보가 존재하는데, 스크롤을 내리면 추가정보를 알 수 있습니다.
다행히 스크롤을 하지 않아도 html에 정보가 다 포함되어 있기에,
스크롤을 내리는 행위를 추가하지 않아도 됩니다.
- 크롤링
driver = uc.Chrome()
크롬창을 엽니다.
크롬 버전이 다르다면,
version_main=133 (숫자는 버전에 맞게)을 괄호 안에 넣어주면 문제없습니다.
driver.get('https://www.zigbang.com/home/oneroom/items/43862239')
직방의 특정 매물 페이지로 이동합니다.
location = driver.find_element(By.CSS_SELECTOR, 'div.css-1563yu1.r-aw03qq.r-1wbh5a2.r-1w6e6rj.r-159m18f.r-1b43r93.r-16dba41.r-rjixqe').text
location
매물이 위치한 주소를 태그를 통해 가져옵니다.
class_name = input()
class_name.replace(' ','.')
이때, 클래스 이름에 공백이 들어간다면, 전부.으로 바꿔줘야 합니다.
다른 원하는 정보들도 같은 방식으로 찾으면 됩니다.
- 예외처리
크롤링을 진행하는 과정에서, 데이터의 형식이 다른 예외가 발생했습니다.
예외의 대다수가 같은 태그명을 사용하는데,
특수한 경우에서 같은 태그명에 데이터가 더 들어있는 경우가 있었습니다.
manage_cost_s = driver.find_elements(By.CSS_SELECTOR, 'div.css-1563yu1.r-aw03qq.r-1wbh5a2.r-1w6e6rj.r-159m18f.r-1b43r93.r-16dba41.r-rjixqe.r-fdjqy7.r-13wfysu.r-q42fyq.r-1ad0z5i')
#원래 find_element 로 했으나 지역명이 관리비 앞에 존재하는 특수경우가 30% 발생
for i in manage_cost_s:
text = i.text
if text.startswith("관리비"):
manage_cost = text
manage_cost
첫 번째 예외는 find_element 로했으나,
지역명이 관리비 앞에 한 번 더 출몰하는 경우가 있어
elments로 다 가져와서 관리비만 뽑아내도록 바꾸었습니다.
money = driver.find_element(By.CSS_SELECTOR, 'div.css-1563yu1.r-aw03qq.r-1wbh5a2.r-1w6e6rj.r-159m18f.r-1x35g6.r-b88u0q.r-ueyrd6.r-fdjqy7.r-13wfysu.r-q42fyq.r-1ad0z5i').text.split()
money_type = money[0]
charge = " ".join(money[1:])
# 억 단위가 넘어갈을때, split 사용시 1억 에서 끊기는 8% 예외
print(money_type, charge)
2번째 예외는 money가 아래와 같이 존재하는데,
split 사용하여 [1]로 처리했더니,
뒷부분 7,388/16을 가져오지 못하는 경우가 생겨
[1:] 첫 번째 이후 모든 값으로 바꾸어 주었습니다.
월세 1억 7,388/16
3번째 예외는 특정매물에 사람들이 많은 관심이 있었을 때,
매물정보에 "이 집을 관심있게 보는 분들이 급격하게 증가했어요"라는 문구가 추가됩니다.
stats = driver.find_elements(By.CSS_SELECTOR, 'div.css-1dbjc4n.r-1mlwlqe.r-eqz5dr.r-16y2uox.r-1wbh5a2.r-1777fci')
# 원하지 않는 텍스트
unwanted = "이 집을 관심있게 보는 분들이 급격하게 증가했어요"
# '이 집을 관심있게 보는 분들이 급격하게 증가했어요' 라는 문구가 포함된 4% 예외가 발생
# unwanted 텍스트가 포함된 요소를 제외하고 필터링
filtered_stats = [data for data in stats if unwanted not in data.text]
# 필터링한 결과 중 원하는 6개의 요소만 선택
desired_data = filtered_stats[:6]
for data in desired_data:
print(data.text)
결과는 다음과 같습니다.
영등포시장역 도보 3분 컨디션 좋은 풀옵션 오피스텔
계약 26.4m² / 전용 15.03m²
분리형원룸 (욕실 1개)
주차 가능
10층/11층
즉시 입주 가능
마지막 예외는 매물번호로 사이트에 검색했을 때,
존재하지 않는 매물이라고 뜨는 경우가 11% 있었습니다.
try:
except:
continue
continue로 수집하지 않고, 다음으로 넘어가게 처리했습니다.
- 결과 및 전체코드
원하는 데이터를 성공적으로 가져왔습니다.
다음은 전체 코드입니다.
total = []
데이터를 담을 total을 따로 만들어 주었습니다.
#https://www.zigbang.com/home/oneroom/items/43862098
start_num = 43862098+1000
print(start_num)
for n in tqdm(range(start_num, start_num + 1000)):
driver.implicitly_wait(10)
driver.get(f'https://www.zigbang.com/home/oneroom/items/{n}')
#매물번호로 직방 사이트 방문
try:
box = [] #정보담는 박스
# 집 번호
house_num = driver.find_element(
By.CSS_SELECTOR,
"div.css-1563yu1.r-jwli3a.r-1wbh5a2.r-1w6e6rj.r-159m18f.r-1enofrn.r-majxgm.r-1cwl3u0.r-fdjqy7.r-13wfysu.r-q42fyq.r-1ad0z5i"
).text.split()[1].strip()
location = driver.find_element(
By.CSS_SELECTOR,
'div.css-1563yu1.r-aw03qq.r-1wbh5a2.r-1w6e6rj.r-159m18f.r-1b43r93.r-16dba41.r-rjixqe'
).text
money = driver.find_element(By.CSS_SELECTOR, 'div.css-1563yu1.r-aw03qq.r-1wbh5a2.r-1w6e6rj.r-159m18f.r-1x35g6.r-b88u0q.r-ueyrd6.r-fdjqy7.r-13wfysu.r-q42fyq.r-1ad0z5i').text.split()
money_type = money[0]
charge = " ".join(money[1:])
manage_cost_s = driver.find_elements(By.CSS_SELECTOR, 'div.css-1563yu1.r-aw03qq.r-1wbh5a2.r-1w6e6rj.r-159m18f.r-1b43r93.r-16dba41.r-rjixqe.r-fdjqy7.r-13wfysu.r-q42fyq.r-1ad0z5i')
for i in manage_cost_s:
text = i.text
if text.startswith("관리비"):
manage_cost = text
# box에 기본 정보 저장 (house_num, location, money 정보 등)
box = [house_num, location, money_type, charge, manage_cost]
stats = driver.find_elements(By.CSS_SELECTOR, 'div.css-1dbjc4n.r-1mlwlqe.r-eqz5dr.r-16y2uox.r-1wbh5a2.r-1777fci')
# 원하지 않는 텍스트
unwanted = "이 집을 관심있게 보는 분들이 급격하게 증가했어요"
# unwanted 텍스트가 포함된 요소를 제외하고 필터링
filtered_stats = [data for data in stats if unwanted not in data.text]
# 필터링한 결과 중 원하는 6개의 요소만 선택
desired_data = filtered_stats[:6]
for data in desired_data:
box.append(data.text)
total.append(box)
#어차피 고유한 housenum일테니 중복확인을 제거하는게 빠르겠지.
except Exception as e:
continue
크롤링 코드입니다.
column_names = ['등록번호', '위치', '월/전세', '금액', '관리비', '설명','면적', '상태', '주차', '층', '입주 여부']
df = pd.DataFrame(total, columns=column_names)
df.to_excel('직방_02.xlsx', index=False)
수집된 데이터를 데이터프레임으로 만들어 엑셀로 저장합니다.
- 마무리
매물번호를 기준으로 중복 여부를 확인하여 중복된 데이터는 넣지 않도록 처리했었는데,
생각해 보니 매물번호 자체가 이미 고유한 값이라 굳이 중복 검사를 하지 않아도 될 것 같습니다.
오히려 중복 처리를 생략하면 데이터를 더욱 빠르게 수집할 수 있는 장점도 있습니다.
또 적다 보니 생각난 게, 매물번호로 url에 접근을 하는데
굳이 url에 접근해서 'housenum'변수에 매물번호를 담아 가져올 필요가 없었네요.
start_num이 43862098+1000 인 이유는
43862098 매물부터 1000개씩 두 번 나누어 수집했기 때문입니다.
43862098이라는 숫자는 임의로 선택한 숫자입니다.
2000번의 결과로 만들어진 엑셀을 첨부하며 마무리하겠습니다.