언어/[python]

[web_crawling] span안에 text 가져오기

dayeonsheep 2024. 1. 25. 20:14

교보문고 지점별 주소 긁어오는 코드짜기

 

from requests import get
from bs4 import BeautifulSoup

url = f'https://www.kyobobook.co.kr/store?storeCode='
store_codes = {
    '001', '058', '015', '023', '041', '066', '033', '072', '068', '036',
    '046', '074', '029', '090', '056', '049', '070', '052', '013', '047',
    '042', '025', '038', '069', '057', '059', '087', '004', '002', '005',
    '024', '045', '039', '077', '031', '028', '034', '048', '043'
}

def get_info_desc_text(store_code):
    response = get(f"{url}{store_codes}")

    if response.status_code == 200:
        soup = BeautifulSoup(response.content, 'html.parser')
        info_desc_spans = soup.find_all('span', class_='info_desc')

        if info_desc_spans:
            for info_desc_span in info_desc_spans:
                info_desc_text = info_desc_span.text.strip()
                print(f"Store Code: {store_code} - Info Desc Text: {info_desc_text}")
        else:
            print(f"No 'info_desc' spans found for Store Code: {store_code}")
    else:
        print(f"Failed to retrieve data. Status code: {response.status_code}")


# Iterate through store codes and call the function
for store_code in store_codes:
    get_info_desc_text(store_code)

첫 시행착오 코드 

 

이런저런 시행착오를 겪고

<span class="info_title">매장주소</span>

<span class="info_desc">서울특별시 중구 장충단로13길 20, 현대시티아울렛 지하 1층</span>

info_desc가 많아서 매장주소 span 뒤에 있는 것만 출력하도록 해야돼서 그 코드 추가하고

entry로 서점명이랑 코드별 주소로 출력하는 거 추가하고

했는데 안되는거임

 

그래서 데이터천재 윤주언니에게 자문을 구해서 해결함

 

해결방법 : headers 넣기!!!

 

1. improt 밑에 hearders 호출해주고

headers = {
    'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'
}

 

 

2. response에 headers넣어주기

response = requests.get(url, headers=headers)

 

 

3. 이제 에러 안나는 코드

import requests
from bs4 import BeautifulSoup

headers = {
    'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'
}

url_base = 'https://www.kyobobook.co.kr/store?storeCode='
store_codes = [
    '001', '058', '015', '023', '041', '066', '033', '072', '068', '036',
    '046', '074', '029', '090', '056', '049', '070', '052', '013', '047',
    '042', '025', '038', '069', '057', '059', '087', '004', '002', '005',
    '024', '045', '039', '077', '031', '028', '034', '048', '043'
]

result_entries = []

for store_code in store_codes:
    url = f"{url_base}{store_code}"
    response = requests.get(url, headers=headers)

    result_entry = {
        "bookstore": "교보문고",
        "store_code": store_code,
        "info_desc": ""
    }

    if response.status_code == 200:
        soup = BeautifulSoup(response.content, 'html.parser')

        all_span_elements = soup.find_all('span')

        for span_element in all_span_elements:
            if '매장주소' in span_element.get_text():
                info_desc_span = span_element.find_next('span', class_='info_desc')
                if info_desc_span:
                    info_desc_text = info_desc_span.get_text(strip=True)
                    result_entry["info_desc"] = info_desc_text
                    break 
        result_entries.append(result_entry)

    else:
        print("Failed to retrieve data for Store Code: {store_code}. Status code: {response.status_code}")

for entry in result_entries:
    print(entry)

 

 

4. 더 깔끔하게 

{
	"bookstore": "교보문고",
	"branch": "광화문점",
	"address": "도로명주소"
}

 

더 수정할 것

 

1. 도로명 주소

, 전까지 

if ,이 없으면 한글이나 영어 바로 앞에 숫자가 나오는 거기까지 인데 띄어쓰기로 구분되어있는것까지만

 

근데 이거 적용했는데

서울특별시 마포구 월드컵로3길 14 딜라이트스퀘어 2차(지하 1층), 1차(지하 2층)

서울시 광진구 능동로 92 롯데백화점 스타시티점 지하1층

이런건 잘 안되는데 3개밖에 없길래

기본으로 , 전이랑 한글 전 숫자(13길 롯) 뭐 이런것까지 받아오고 쟤네는 걍 수동으로 고침

 

2. branch를 store_code에 맞는 지점명으로 바꾸기

 

3. csv 파일로 저장하기

 

1차 수정사항 반영해서 바꾼 코드 ↓

import csv
import requests
from bs4 import BeautifulSoup
import re

headers = {
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
}

url_base = 'https://www.kyobobook.co.kr/store?storeCode='

store_codes = [
    '001', '058', '015', '023', '041', '066', '033', '072', '068', '036',
    '046', '074', '029', '090', '056', '049', '070', '052', '013', '047',
    '042', '025', '038', '069', '057', '059', '087', '004', '002', '005',
    '024', '045', '039', '077', '031', '028', '034', '048', '043', '065'
]
#신논현역팝업스토어 ='0E9', 거제팝업스토어 ='065'

store_name = [
    '광화문', '가든파이브', '강남', '건대스타시티', '동대문', '신도림디큐브시티', '목동', '서울대', '수유', '영등포',
    '은평', '이화여대', '잠실', '천호', '청량리', '합정', '광교', '광교월드스퀘어', '부천', '분당',
    '송도', '인천', '일산', '판교', '평촌', '경성대ㆍ 부경대', '광주상무', '대구', '대전', '부산',
    '세종', '센텀시티', '울산', '전북대', '전주', '창원', '천안', '칠곡', '해운대 팝업 스토어', '거제팝업스토어'
]

result_entries = []

for idx, store_code in enumerate(store_codes):
    url = f"{url_base}{store_code}"
    response = requests.get(url, headers=headers)

    result_entry = {
        "bookstore": "교보문고",
        "branch": store_name[idx],
        "address": ""
    }

    if response.status_code == 200:
        soup = BeautifulSoup(response.content, 'html.parser')

        all_span_elements = soup.find_all('span')

        for span_element in all_span_elements:
            if '매장주소' in span_element.get_text():
                info_desc_span = span_element.find_next('span', class_='info_desc')
                if info_desc_span:
                    info_desc_text = info_desc_span.get_text(strip=True)
                
                    if ',' in info_desc_text:
                        info_desc_text = info_desc_text.split(',')[0].strip()
                    else:
                        match = re.search(r'\d[^\d\W]*', info_desc_text)
                        if match:
                            info_desc_text = match.group()
                    
                    result_entry["address"] = info_desc_text
                    break

        result_entries.append(result_entry)

    else:
        print(f"Failed to retrieve data for Store Code: {store_code}. Status code: {response.status_code}")

for entry in result_entries:
    print(entry)

csv_file_path = 'result_entries.csv'
csv_header = ["bookstore", "branch", "address"]

with open(csv_file_path, 'w', newline='', encoding='utf-8-sig') as csv_file:
    csv_writer = csv.DictWriter(csv_file, fieldnames=csv_header)
    csv_writer.writeheader()
    csv_writer.writerows(result_entries)

print(f"Results saved to {csv_file_path}")

 

 

 

저장된 csv 파일

 

도로명 주소... 이렇게 하려고 했는데 !

생각해보니 충정로 39 이면 그냥 그 길인거잖음... 그래서 그냥 변환하는 그 코드는 뺐다

그리고 위도 경도 코드변환 추가함

https://velog.io/@macang15/%EC%A3%BC%EC%86%8C%EB%A5%BC-%EC%9C%84%EB%8F%84-%EA%B2%BD%EB%8F%84%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0

 

주소를 위도 경도로 변환하기, 지오코딩(Geocoding)하는 법

1. 구글 스프레드 시트 이용하기 여기를 눌러서 ![](https://velog.velcdn.com/images/macang15/post/06d5a674-16ec-474e-b9a1-183

velog.io

이방법으로 하고 

위도 경도 안나오는건 구글맵에서 링크에서 따로 붙여넣어줬당... 

 

https://wonhwa.tistory.com/29

 

[python] 주소값만 있을 때 위경도 찾는 방법

안녕하세요! 데이터 분석을 하다보면 특히 folium으로 위치를 시각화 할 때는 숫자로 된 위경도 값이 필요한데 데이터 파일 자체에 주소만 나와있고 위경도 값이 누락되어 있는 경우가 발생합니

wonhwa.tistory.com

 

위도경도 추가하는 코드가

import requests
from bs4 import BeautifulSoup
from geopy.geocoders import Nominatim
import re

geo_local = Nominatim(user_agent='South Korea')

# 위도, 경도 반환하는 함수
def geocoding(address):
    try:
        geo = geo_local.geocode(address)
        x_y = [geo.latitude, geo.longitude]
        return x_y

    except:
        return [0, 0]


headers = {
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
}

url_base = 'https://www.kyobobook.co.kr/store?storeCode='

store_codes = [
    '001', '058', '015', '023', '041', '066', '033', '072', '068', '036',
    '046', '074', '029', '090', '056', '049', '070', '052', '013', '047',
    '042', '025', '038', '069', '057', '059', '087', '004', '002', '005',
    '024', '045', '039', '077', '031', '028', '034', '048', '043', '065'
]

store_name = [
    '광화문', '가든파이브', '강남', '건대스타시티', '동대문', '신도림디큐브시티', '목동', '서울대', '수유', '영등포',
    '은평', '이화여대', '잠실', '천호', '청량리', '합정', '광교', '광교월드스퀘어', '부천', '분당',
    '송도', '인천', '일산', '판교', '평촌', '경성대ㆍ 부경대', '광주상무', '대구', '대전', '부산',
    '세종', '센텀시티', '울산', '전북대', '전주', '창원', '천안', '칠곡', '해운대 팝업 스토어', '거제팝업스토어'
]

result_entries = []

for idx, store_code in enumerate(store_codes):
    url = f"{url_base}{store_code}"
    response = requests.get(url, headers=headers)

    result_entry = {
        "bookstore": "교보문고",
        "branch": store_name[idx],
        "address": "",
        "lati": 0,
        "long": 0
    }

    if response.status_code == 200:
        soup = BeautifulSoup(response.content, 'html.parser')

        all_span_elements = soup.find_all('span')

        for span_element in all_span_elements:
            if '매장주소' in span_element.get_text():
                info_desc_span = span_element.find_next('span', class_='info_desc')
                if info_desc_span:
                    info_desc_text = info_desc_span.get_text(strip=True)

                    if ',' in info_desc_text:
                        info_desc_text = info_desc_text.split(',')[0].strip()
                    else:
                        match = re.search(r'\d[^\d\W]*', info_desc_text)
                        if match:
                            info_desc_text = match.group()

                    result_entry["address"] = info_desc_text
                    result_entry["lati"], result_entry["long"] = geocoding(info_desc_text)

                    break

        result_entries.append(result_entry)

    else:
        print(f"Failed to retrieve data for Store Code: {store_code}. Status code: {response.status_code}")

for entry in result_entries:
    print(entry)

이거였는데

얘는 건물명이 들어가면 위경도가 안나와서 이전에 도로명주소 해줬던 것처럼 빼줘야됨...

{'bookstore': '교보문고', 'branch': '광화문', 'address': '서울특별시 종로구 종로 1', 'lati': 37.5710655, 'long': 126.9780349}
{'bookstore': '교보문고', 'branch': '가든파이브', 'address': '서울특별시 송파구 충민로 66', 'lati': 37.4781442, 'long': 127.1256943}
{'bookstore': '교보문고', 'branch': '강남', 'address': '서울특별시 서초구 강남대로 465', 'lati': 37.5039843, 'long': 127.0241817}
{'bookstore': '교보문고', 'branch': '건대스타시티', 'address': '9', 'lati': -6.3099187, 'long': 38.32619756583628}
{'bookstore': '교보문고', 'branch': '동대문', 'address': '서울특별시 중구 장충단로13길 20', 'lati': 37.5680707, 'long': 127.0066452}
{'bookstore': '교보문고', 'branch': '신도림디큐브시티', 'address': '서울특별시 구로구 경인로 662', 'lati': 37.4923714, 'long': 126.8353312}
{'bookstore': '교보문고', 'branch': '목동', 'address': '서울특별시 양천구 목동서로 159-1', 'lati': 37.5297485, 'long': 126.8734848}
{'bookstore': '교보문고', 'branch': '서울대', 'address': '서울특별시 관악구 관악로 1', 'lati': 37.45811495, 'long': 126.95216069831967}
{'bookstore': '교보문고', 'branch': '수유', 'address': '서울특별시 강북구 도봉로 348', 'lati': 37.636522, 'long': 127.0243777}
{'bookstore': '교보문고', 'branch': '영등포', 'address': '서울특별시 영등포구 영중로 15', 'lati': 37.5265476, 'long': 126.9057157}
{'bookstore': '교보문고', 'branch': '은평', 'address': '서울특별시 은평구 통일로 1050', 'lati': 37.6383051, 'long': 126.917868}
{'bookstore': '교보문고', 'branch': '이화여대', 'address': '서울특별시 서대문구 이화여대길 52', 'lati': 37.5613096, 'long': 126.9462164}
{'bookstore': '교보문고', 'branch': '잠실', 'address': '서울특별시 송파구 올림픽로 269', 'lati': 37.5303962, 'long': 127.1199025}
{'bookstore': '교보문고', 'branch': '천호', 'address': '서울특별시 강동구 올림픽로 664', 'lati': 37.5408112, 'long': 127.1246252}
{'bookstore': '교보문고', 'branch': '청량리', 'address': '서울특별시 동대문구 왕산로 214', 'lati': 37.5806693, 'long': 127.04820752761542}
{'bookstore': '교보문고', 'branch': '합정', 'address': '서울특별시 마포구 월드컵로3길 14 딜라이트스퀘어 2차(지하 1층)', 'lati': 0, 'long': 0}
{'bookstore': '교보문고', 'branch': '광교', 'address': '경기도 수원시 영통구 광교중앙로 145', 'lati': 37.2870393, 'long': 127.057709}
{'bookstore': '교보문고', 'branch': '광교월드스퀘어', 'address': '경기도 수원시 영통구 센트럴타운로 107', 'lati': 37.29177, 'long': 127.04914871486176}
{'bookstore': '교보문고', 'branch': '부천', 'address': '경기도 부천시  부천로 1', 'lati': 37.4840373, 'long': 126.7826449}
{'bookstore': '교보문고', 'branch': '분당', 'address': '경기도 성남시 분당구 황새울로312번길 26', 'lati': 37.3834972, 'long': 127.1213695}
{'bookstore': '교보문고', 'branch': '송도', 'address': '인천광역시 연수구 송도국제대로 123', 'lati': 37.3808249, 'long': 126.6575489}
{'bookstore': '교보문고', 'branch': '인천', 'address': '인천광역시 남동구 예술로 138', 'lati': 37.4647173, 'long': 126.7024524}
{'bookstore': '교보문고', 'branch': '일산', 'address': '경기도 고양시 일산동구 중앙로 1036', 'lati': 37.643017, 'long': 126.78996766559388}
{'bookstore': '교보문고', 'branch': '판교', 'address': '경기도 성남시 분당구 판교역로146번길 20', 'lati': 37.3927642, 'long': 127.1120517}
{'bookstore': '교보문고', 'branch': '평촌', 'address': '경기도 안양시 동안구 시민대로 180', 'lati': 37.3899769, 'long': 126.9503403}
{'bookstore': '교보문고', 'branch': '경성대ㆍ 부경대', 'address': '부산광역시 남구 수영로 324', 'lati': 35.13772845, 'long': 129.10171962839928}
{'bookstore': '교보문고', 'branch': '광주상무', 'address': '광주광역시 서구 상무중앙로 58', 'lati': 35.15209035, 'long': 126.84888425}
{'bookstore': '교보문고', 'branch': '대구', 'address': '대구광역시 중구 국채보상로 586', 'lati': 35.87049525, 'long': 128.59484231743414}
{'bookstore': '교보문고', 'branch': '대전', 'address': '대전광역시 서구 대덕대로 226', 'lati': 36.354418, 'long': 127.3794148}
{'bookstore': '교보문고', 'branch': '부산', 'address': '부산광역시 부산진구 중앙대로 658', 'lati': 35.151519, 'long': 129.059583675}
{'bookstore': '교보문고', 'branch': '세종', 'address': '세종특별자치시 갈매로 363', 'lati': 36.49632115, 'long': 127.26278109628697}
{'bookstore': '교보문고', 'branch': '센텀시티', 'address': '부산광역시 해운대구 센텀남대로 59', 'lati': 0, 'long': 0}
{'bookstore': '교보문고', 'branch': '울산', 'address': '울산광역시 남구 화합로 185', 'lati': 35.5331966, 'long': 129.3413619}
{'bookstore': '교보문고', 'branch': '전북대', 'address': '전라북도 전주시 덕진구 백제대로 567', 'lati': 35.84667515, 'long': 127.13372088978024}
{'bookstore': '교보문고', 'branch': '전주', 'address': '전라북도 전주시 완산구 전주객사5길 35', 'lati': 35.8191962, 'long': 127.1454321}
{'bookstore': '교보문고', 'branch': '창원', 'address': '경상남도 창원시 성산구 중앙대로 104', 'lati': 35.2254846, 'long': 128.681222}
{'bookstore': '교보문고', 'branch': '천안', 'address': '충청남도 천안시 동남구 만남로 43', 'lati': 36.8192537, 'long': 127.1542327}
{'bookstore': '교보문고', 'branch': '칠곡', 'address': '대구광역시 북구 동암로 104', 'lati': 35.9439693, 'long': 128.56239009787706}
{'bookstore': '교보문고', 'branch': '해운대 팝업 스토어', 'address': '8', 'lati': 47.5422877, 'long': 8.7495577}
{'bookstore': '교보문고', 'branch': '거제팝업스토어', 'address': '1', 'lati': -6.1665159, 'long': 106.8787959}

그러면 위도경도가 나오긴하는데

내가 원하는 건 이게 아니니까~

건물명까지 나오는 걸로 하고 csv파일 저장까지 했다

 

 

다음엔 꼭 더 효율적인 코드를 짜리... 

데이터가 적어서 망정이지 더 있었으면 내 코드는 말짱도루묵이야