03. Web Data - BeautifulSoup
1. BeautifulSoup for web data : 설치 및 코드 불러오기
2. 예제 1 - 네이버 금융
3. 예제 2 - 위키백과 문서 정보 가져오기
4. 예제 3.1 - 시카고 맛집 메인 페이지 분석
5. 예제 3.2 - 시카고 맛집 하위 페이지 분석
6. 예제 3.3 - 시카고 맛집 데이터 지도 시각화
BeautifulSoup Basic
import
from urllib.request import urlopen
from bs4 import BeautifulSoup
페이지 읽기
page = open(path,'r').read()
soup = BeautifulSoup(page, 'html.parser') # html문법 규칙에 따라 문장 분석
print(soup.prettify())
출처 : Beautiful Soup Documentation
url = "https://finance.naver.com/marketindex/"
page = urlopen(url)
soup = BeautifulSoup(page, "html.parser")
print(soup.prettify)
response.status : 응답 상태를 숫자로 나타냄
url = "https://finance.naver.com/marketindex/"
response = urlopen(url)
response.status
#1xx (조건부 응답)
# 2xx (성공)
# 3xx (리다이렉션 완료)
# 4xx (요청 오류)
# 5xx (서버 오류)
import requests
url = 'https://finance.naver.com/marketindex/'
response = requests.get(url)
response
# requests.get(), requests.post() 두 방식이 있음
soup = BeautifulSoup(response.text,'html.parser')
print(soup.prettify())
soup.find_all('li','on') <li class='on'>
soup.select('.class'|'#id') class는 .사용 id는 #사용
exchangeList = soup.select('#exchangeList > li') # class는 .사용 id는 #사용
len(exchangeList), exchangeList
제목, 환율, 변화, 상승/하락 출력
title = exchangeList[0].select_one('.h_lst').text
exchange = exchangeList[0].select_one('.value').text
change = exchangeList[0].select_one('.change').text
updown = exchangeList[0].select_one('div.head_info.point_up > .blind').text # 띄어쓰기 두 개의 속성 값으로 간주
# title
# exchangeList
# change
# updown
title, exchange, change, updown
결과
('미국 USD', '1,337.50', '1.50', '상승')
미국, 일본, 유럽연합, 중국 4개 데이터 수집
1. 반복문 사용하여 각 아이템별 제목, 환율, 변화, 상승/하락 딕셔너리 형태로 저장.
2. exchange_datas 리스트에 추가
3. 데이터프레임 만들기
import pandas as pd
exchange_datas = []
baseUrl = 'http://finance.naver.com'
for item in exchangeList:
data = {
'title': item.select_one('.h_lst').text,
'exchange': item.select_one('.value').text,
'change': item.select_one('.change').text,
'updown': item.select_one('.head_info > .blind').text, # 상승/ 하락 섞여있음. head_info만 입력
'link' : baseUrl + item.select_one('a').get('href')
}
exchange_datas.append(data)
print(data)
df =pd.DataFrame(exchange_datas) # 리스트 안의 딕셔너리 형태
df.to_excel('../source_code/naverfinance.xlsx')
import urllib
from urllib.request import urlopen, Request
html = 'https://ko.wikipedia.org/wiki/{search_words}' # formatting
req = Request(html.format(search_words=urllib.parse.quote('여명의_눈동자'))) #글자를 URL로 인코딩
response = urlopen(req)
soup = BeautifulSoup(response, 'html.parser')
print(soup.prettify)
반복문 사용하여 각 클래스 텍스트, 행 출력
n = 0
for each in soup.find_all('ul'):
print('=>' + str(n) + '=====================')
print(each.get_text())
n += 1
35번째 ul 주요인물 텍스트
필요없는 코드 지우기
soup.find_all('ul')[35].text.strip().replace('\xa0','').replace('\n','')
결과
'채시라: 윤여옥 역 (아역: 김민정)박상원: 장하림(하리모토 나츠오) 역 (아역: 김태진)최재성: 최대치(사카이) 역 (아역: 장덕수)'
from urllib.request import Request, urlopen
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
url_base = 'https://www.chicagomag.com/'
url_sub = 'chicago-magazine/november-2012/best-sandwiches-chicago/'
url = url_base + url_sub
ua = UserAgent()
# {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'}
req = Request(url, headers={'User-Agent': ua.ie})
response = urlopen(req)
response.status
html = urlopen(req)
soup = BeautifulSoup(html, "html.parser")
print(soup.prettify)
text 추출하기
import re
tmp_string = tmp_one.find(class_='sammyListing').get_text()
re.split(('\n|\r'), tmp_string) #결괏값은 리스트에 담겨 있다.
print(re.split(('\n|\r'), tmp_string)[0]) #메뉴 이름
print(re.split(('\n|\r'), tmp_string)[1])#카페 이름
결과
BLT
Old Oak Tap
순위, 메인메뉴, 카페이름, Url 리스트
반복문 사용하여 순위, 메인메뉴, 카페이름, Url 리스트에 추가
#필요한 내용을 담을 빈 리스트
#리스트로 하나씩 컬럼을 만들고, DataFrame으로 합칠 예정
urljoin(url_base,url_sub) : url합치기
from urllib.parse import urljoin
url_base = 'https://www.chicagomag.com/'
rank = []
main_menu = []
cafe_name = []
url_add = []
list_soup = soup.find_all('div','sammy') #soup.select('.sammy')
for item in list_soup:
rank.append(item.find(class_='sammyRank').text)
tmp_string = item.find(class_='sammyListing').get_text()
main_menu.append(re.split(('\n|\r'), tmp_string)[0])
cafe_name.append(re.split(('\n|\r'), tmp_string)[1])
url_add.append(urljoin(url_base,item.find('a')['href']))
DataFrame 합치기
import pandas as pd
data = {
'Rank' : rank,
'Menu' : main_menu,
'Cafe' : cafe_name,
'URL' : url_add
}
df = pd.DataFrame(data)
df.tail()
순위 안의 리스트 하위 페이지 분석
import pandas as pd # import 먼저, 그 후 from절
from urllib.request import urlopen, Request
from fake_useragent import UserAgent
from bs4 import BeautifulSoup
데이터프레임URL컬럼 분석
req = Request(df["URL"][0], headers={'user-Agent':ua.ie})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html, 'html.parser')
soup_tmp.find('p','addy').text
가격, 주소, 번호 정보
req = Request(df["URL"][0], headers={'user-Agent':ua.ie})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html, 'html.parser')
soup_tmp.find('p','addy').text
결과
'\n$10. 2109 W. Chicago Ave., 773-772-0406, theoldoaktap.com'
price_tmp = soup_tmp.find('p','addy').text
price_tmp
.,를 기준으로 구분하기
import re
re.split('.,',price_tmp)
결과
['\n$6.85. 3351 N. Broadwa', ' 773-868-400', ' phoebesbakery.com']
리스트의 0번째 값(가격, 주소 가져오기)
price_tmp = re.split('.,', price_tmp)[0]
price_tmp
정규표현식
\d+ : 한 개 이상의 숫자
(\d+)? : 숫자 있을 수도 있고, 없을 수도 있음
.group()으로 묶기
주소(가격길이 +2부터 시작)
tmp = re.search('\$\d+\.(\d+)?', price_tmp).group() #가격
price_tmp[len(tmp)+2:]
주소 : ' 3351 N. Broadwa'
반복문 사용하여 가격, 주소 리스트 담기
for문 사용
1. row값 사용
price=[]
address=[]
for idx, row in tqdm(df.iterrows()):
req = Request(row["URL"], headers={'user-Agent':'chrome'})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html, 'html.parser')
gettings = soup_tmp.find('p','addy').text
price_tmp = re.split('.,', gettings)[0]
tmp = re.search('\$\d+\.(\d+)?', price_tmp).group()
price.append(tmp)
address.append(price_tmp[len(tmp)+2:])
print(idx)
2. df.index사용
price=[]
address=[]
for n in df.index[:]:
req = Request(df["URL"][n], headers={'user-Agent':'chrome'})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html, 'html.parser')
gettings = soup_tmp.find('p','addy').text
price_tmp = re.split('.,', gettings)[0]
tmp = re.search('\$\d+\.(\d+)?', price_tmp).group()
price.append(tmp)
address.append(price_tmp[len(tmp)+2:])
print(n)
DataFrame에 추가
df["Price"] = price
df["Address"] = address
df = df.loc[:,['Rank','Cafe','Menu','Price','Address']]
df.set_index('Rank', inplace=True)
df.head()
#Requirements
import folium
import pandas as pd
import googlemaps
import numpy as np
from tqdm import tqdm
#read
df = pd.read_csv('../data/03. best_sandwiches_list_chicago2.csv', index_col=0)
df.tail()
#gmap
gmaps = googlemaps.Client(key=gmaps_key)
lat = []
lng = []
for idx, row in tqdm(df.iterrows()):
if not row['Address'] == "Multiple location":
target_name = row["Address"] + ',' + 'Chicago'
# print(target_name)
gmaps_output = gmaps.geocode(target_name)
location_output = gmaps_output[0].get('geometry')
lat.append(location_output['location']['lat'])
lng.append(location_output['location']['lng'])
else:
lat.append(np.nan)
lng.append(np.nan)
DataFrame에 추가
df['lat'] = lat
df['lng'] = lng
df.tail()
for문 사용하여 지도에 표시
folium.Marker(location=[위도,경도],popup=,tooltip=,icon=foliumIcon(icon=,prefix=)).add_to(mapping)
mapping = folium.Map(location=[41.8781136, -87.6297982], zoom_start=11)
mapping
for idx, row in df.iterrows():
if not row['Address'] == 'Multiple location':
folium.Marker(
location=[row['lat'],row['lng']],
popup=row['Cafe'],
tooltip=row['Menu'],
icon=folium.Icon(
icon='coffee',
prefix = 'fa'
)
).add_to(mapping)
mapping
출처 : 제로베이스 데이터 취업 스쿨
여명의 눈동자 : https://ko.wikipedia.org/wiki/%EC%97%AC%EB%AA%85%EC%9D%98_%EB%88%88%EB%8F%99%EC%9E%90
시카고 맛집 :
https://www.chicagomag.com/chicago-magazine/november-2012/best-sandwiches-chicago/
네이버 금융 :
https://finance.naver.com/