EDA, 웹크롤링, 파이썬 프로그래밍의 일부분인 웹데이터, 유가분석을 정리했다.
cd DS_study/ds_study/WebData
conda activate ds_study
code .
ctrl + shfit + P
from ba4 import BeautifulSoup
page = open("파일주소.html", 'r').read()
soup = BeautifulSoup(page, "html.parser')
print(soup.prettify())
_blink
soup.body
soup['body']
soup.find('p')
soup.find_all('p')
soup.find_all(class_='클래스명')
soup.find_all({'class' : '클래스명'})
soup.find_all(id_='아이디명')
soup.find('p').text
.strip()
을 붙이면 됨soup.find('p').string
soup.find_all('p').get_test()
soup.find_all('a')[0].get('href')
<a href = "주소" id = "아이디값">출력글자</a>
soup.find_all('a').['href'].string
<span class='value>
를 기억해야 한다from urllib.request import urlopen
urlopen('url주소')
를 beautifulsoup으로 해석하기ulropen('url주소').status
pip list | findstr 라이브러리명
requests.get()
requests.post()
find
와 비슷한 것 select one
find_all
와 비슷한 것 select
~~.select("#아이디명 > li")
import urllib
from urllib.request import urlopen, Request
html = 'http://ko.wikipedia.org/wiki/{search_words}'
req = Request(html.format(search_words = urllib.parse.quote('여명의_눈동자'))) # 글자를 URL로 인코딩
response = urlopen(req)
soup = BeautifulSoup(response, 'html.parser')
soup.find_all('ul')[15],text.strip().replace('\xa0', '').replace('\n', '')
# 바꾸고 싶은 문자를 찾아 replace함수에 넣고
# 찾고 싶은 부분의 순서를 찾아 15 자리에 넣는다.
isinstance(변수명, list)
from bs4 import BeautifulSoup
from urllib.request import Request, urlopen
# 메인페이지와 서브페이지 분석을 따로 해야할 수 있기 때문에 주소를 두 개로 분리
url_base = 'https://www.chicagomag.com'
url_sub = '/Chicago-Magazine/November-2012/Best-Sandwiches-Chicago/'
url = url_base + url_sub
# http 403은 서버에서 유저가 문제가 있다고 하는 것
#
req = Request(url, headers = {'User-Agent': 'Chrome'})
html = urlopen(req).read()
soup = BeautifulSoup(html, 'html.parser')
soup.prettify()
soup.find_all('div', _class = 'sammy'), len(soup.find_all('div', class_ = 'sammy')) # 갯수확인
# select로도 가능
soup.select(".sammy"), len(soup.select(".sammy"))
# 전체 코드 긁어오기 위한 샘플 코드 테스트 중...
tmp_one = soup.find_all('div', 'sammy')[0]
type(tmp_one)
# 위 값이 bs4.element.Tag 이면 bs4 요소이므로 bs4 함수를 적용할 수 있다
tmp_one.find(class_ = 'sammyRank').get_text()
tmp_one.find('div', {'class' : 'sammyRank'}).get_text()
tmp_one.select_one('.sammylisiting').text()
tmp_one.find("a")['href']
tmp_one.select_one("a").get('href')
import re
# \n 혹은 \r\n이 보이면 분리해라
re.split(()'\n|\r\n'), 문자열)
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')
for item in list_soup:
rank.append(item.find(class_= 'sammyRank').get_text())
tmp_string = item.find(class_= 'sammyListing').get_text()
main_menu.append(re.split(('\n|\r\n'), tmp_string)[0])
cafe_menu.append(re.split(('\n|\r\n'), tmp_string)[1])
url_add.append(urljoin(url_base, item.find('a')['href']))
# 두 번째 인자(주소)가 절대 주소라면 그냥 사용하고, 상대 주소라면 첫 번째 인자(주소)를 붙여서 사용하라
# 확인용
len(rank), len(main_manu), len(cafe_name), len(url_add)
rank[:5], ...
# 데이터 정리
data = {
"Rank": rank,
"Menu": main_menu,
"Cafe": cafe_name,
"URL": url_add
}
df = pd.DataFrame(data)
df = pd.Dataframe(data, columns = ['Rank', 'Cafe', 'Menu', 'URL])
df.to_csv("주소/이름.csv", sep = ",", encoding = 'utf-8')
from bs4 import BeautifulSoup
from urllib.request import urlopen
import pandas as pd
df = pd.read_csv('주소/이.csv', index_col = 0)
df['URL'][0]
req = Request(df["URL"][0], headers = {'User-Agent': 'Chrome'})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html, 'html.parser')
print(soup_tmp.find('p', 'addy'))
# 가격과 주소가 하나의 태그에 들어있다...!
price_tmp = soup_tmp.find('p', 'addy').get_text()
price_tmp = re.split(".,", price_tmp)[0]
# 끝에 .group()을 해주어야 값만 나온다
tmp = re.search("\$\d+\.(\d+)?", price_tmp).group()
# $가 반드시 있어야 하고, 숫자가 여러 개 있을 수 있고, .이 반드시 와야 하고, (숫자 여러개)의 것이 있을 수도 있고, 없을 수도 있다.
# 가격과 띄어쓰기 포함한 것의 길이 다음은 주소가 나타남
price_tmp[len(tmp) + 2:]
price = []
address = []
# 코드 작동 유무 확인 위해 세 번만 돌리기
for n in df.index[:3]:
# html = urlopen(df['URL'][n])
req = request(df['URL'][n], headers = {"User-Agent": "Mozilla/5.0"})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html, 'lxml')
gettings = soup_tml.find('p', 'addy').get_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)
for idx, row in df.iterrows():
print(row['URL'])
from tqdm import tqdm
price = []
address = []
# 코드 작동 유무 확인 위해 세 번만 돌리기
for idx, row in tqdm(df.index[:3].iterrows()):
# html = urlopen(df['URL'][n])
req = request(row['URL'], headers = {"User-Agent": "Mozilla/5.0"})
html = urlopen(req).read()
# 홈페이지 수정중에 html parser로 불가능할 수 있음
# conda 환경 사용 중에 lxml 환경이 없는 경우가 있다. 깔아주면 됨
soup_tmp = BeautifulSoup(html, 'lxml')
gettings = soup_tml.find('p', 'addy').get_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)
df['Price'] = price
df['Address'] = address
# price, address를 위해 link 데이터가 필요했던 것이므로 최종적으로는 삭제
df = df.loc[:, ['Rank', 'Cafe', 'Menu', 'Price', 'Address']]
df.set_index('Rank', inplace = True)
df.to_csv('', sep = ',', encoding = 'utf-8')
pd.read_csv('', index_col = 0)
import folium
import pandas as pd
import googlemaps
import numpy as np
from tqdm import tqdm
# 주소 체크
df_ pd.read_csv('', index_col = 0)
gmaps_key = "geocoding api key geocoding api key"
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"
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)
df["lat"] = lat
df["lng"] = lng
df.head()
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([row["lat"],
row["lng"]],
popup = row["Cafe"],
tooltip = row["Menu"],
icon = folium.Icon(
Icon = "coffee",
prefix = "fa"
)
).add_to(mapping)
mapping
from selenium import webdriver
# 더이상 드라이버를 다운받지 않아도 됨
driver = webdriver.Chrome()
# 적은 주소의 새 창 키기
driver.get("https://www.naver.com")
# 꺼짐
driver.quit()
from selenium import webdriver
from selenium import By
# 더이상 드라이버를 다운받지 않아도 됨
driver = webdriver.Chrome()
# 적은 주소의 새 창 키기
driver.get("https://www.pinkwink.kr/")
# 스크롤 가능한 높이 가져오기
last_height = driver.execute_script("return document.body.scrollHeight")
last_height
# 화면 스크롤 하단 이동
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);"))
# Xpath 카피해온 곳 까지 스크롤
from selenium.webdriver import ActionChains
some_tag = driver.find_element(By.XPATH, '//*[@id="paging"]/ul')
action = ActionChains(driver)
action.move_to_element(some_tag).perform()
# id로 찾아 입력 창에 글자 넣기
some_tag = driver.find_element(By.ID, 'gsc-i-idl')
some_tag.send_keys('data science')
# 버튼 클릭하는 코드 실행
xpath = '''//*[@id="___gcse_0"]/div/form/table/tbody/tr/td2/buttion'''
some_tag = driiver.find_element(By.XPATH, xpath).clink()
# 현재 화면 html 코드가져오기
from bs4 import BeutifulSoup
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser')
result = soup.find_all('div', 'gsc-webResult gsc-result')
result[0]
get()
함수execute_script('')
driver.maximize_window()
driver.minimize_window()
driver.set_window_size(600, 600)
driver.refresh()
driver.back()
driver.forward()
from selenium.webdriver.common.by import By
driver.find_element(by=BY.NAME, value="태그값")
driver.find_element(BY.CSS_SELECTOR, 'css값 복사해오기')
#
>
.~~~
(n)
driver.execute_script('window.open("주소")')
about:blank
창이 뜸driver.switch_to.window(driver.window_handles[1])
driver.close()
driver.quit()
driver.execte_script('return document.body.scrollHeight')
driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')
driver.save_screenshot('./파일이름.확장자')
./
driver.execute_script('window.scrollTo(0, 0);')
from selenium.webdriver import ActionChains
some_tag = driver.find_element(By.CSS_SELEECTOR, '태그 이름')
action = ActionChans(driver)
action.move_to_element(some_tag).perform()
//
*
/
div[n]
from selenium import webdriver
from selenium.webdriver.common.by import By
# 더이상 드라이버를 다운받지 않아도 됨
driver = webdriver.Chrome()
# 적은 주소의 새 창 키기
driver.get("https://www.naver.com/")
# css_selector
# 검색어 입력
keyword = driver.find_element(By.CSS_SELECTOR, '#query')
keyword.send_keys('파이썬')
# 버튼 클릭
search_btn = driver.find_element(by.CSS_SELECTOR, '#search_btn')
search_btn.click()
# 검색어 지우기
keyword = driver.find_element(By.CSS_SELECTOR, '#query')
keyword.clear()
keyword.send_keys('딥러닝')
# XPATH
# 검색어 입력
keyword = driver.find_element(By.XPATH, '//*[@id="query"]')
keyword.send_keys('파이썬')
# 버튼 클릭
search_btn = driver.find_element(by.XPATH, '//*[@id="search_btn"]')
search_btn.click()
# 검색어 지우기
keyword = driver.find_element(By.XPATH, '//*[@id="query"]')
keyword.clear()
keyword.send_keys('딥러닝')
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.chrome()
driver.get('http://pinkwink.kr')
# 돋보기 버튼 선택
driver.find_element(BY.CSS_SELECTOR, '#header > div.search').click()
# 처음부터 선택이 잘 안되고, 직접 눌러야 search on이라는 이름으로 바뀜
# 아래의 내용으로 진행
from selenium.webdriver import ActionChains
search_tag = driver.find_element(By.CSS_SELECTOR, '.search')
action = ActionChains(driver)
action.click(search_tag)
action.perform()
# 검색어 입력
driver.find_element(BY.CSS_SELECTOR, '#header > div.search > input[type=text]').send_keys('딥러닝')
# 검색 버튼 클릭
driver.find_element(BY.CSS_SELECTOR, '#header > div.search.on > button').click()
# 현재 화면 html 코드 가져오기
driver.page_source
from bs4 import Beautifulopti
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser')
soup.select('.post-item')
contents = soup.select('.post-item')
len(contents)
대한민국 주유 가격을 알아보는 사이트: https://www.opinet.co.kr/user/main/mainView.dos
- 싼 주유소 찾기 > 지역별
- https://www.opinet.co.kr/searRgSelect.do
- 사이트 구조 확인하기
- 지역 정보를 선택
- 웹페이지 주소도 안 바뀌고, HTML 소스에서 원하는 정보를 얻기도 쉽지 않다
- 검색한 내용의 하단에 엑셀 저장이 바로 존재함
- 목표 데이터
- 브랜드
- 가격
- 셀프 주요 여부
- 위치
문제 발생
1. 해당 URL로 한 번에 접근이 안 됨
2. 메인 페이지로 접속이 되고, 팝업창이 하나 나옴
3. 창 전환 시 에러가 남
- time.sleep(n) 으로 n초가 쉬어가면 selenium이 잘 따라감
from selenium impor webdriver
url = 'http://www.opinet.co.kr/searRgSlect.do'
driver = webdriver.Chrome()
driver.get(url)
time.sleep(3)
# 팝업창 뜸
# 팝업창으로 전환 후 닫아줌
driver.switch_to_window(driver.window_handles[-1])
driver.close()
time.sleep(3)
# 메인 화면 창으로 전환
driver.switch_to_window(driver.window_handles[-1])
driver.get(url)
# 새로 정리하기
import time
def main_get():
from selenium impor webdriver
# 페이지 접근
url = 'http://www.opinet.co.kr/searRgSlect.do'
driver = webdriver.Chrome()
driver.get(url)
time.sleep(3)
# 팝업창으로 전환
driver.switch_to_window(driver.window_handles[-1])
# 팝업창 닫기
driver.close()
time.sleep(3)
# 메인 화면 창으로 전환
driver.switch_to_window(driver.window_handles[-1])
# 접근 URL 다시 요청
driver.get(url)
main_get()
# 지역: 시/도
sido_list_raw = driver.find_element(BY.ID, 'SIDO_MNO')
sido_list_raw
sido_list = sido_list_raw.find_elements(BY.TAG_NAME, 'option')
sido_list
sido_names = [option.get_attribute('value') for option in sido_list]
sido_names.remove("")
sido_names
sido_list_raw.send_keys(sido_names[1])
gu_list_raw = driver.find_element(BY.ID, 'SIGUNGU_NM0')
gu_list = gu_list_raw.find_elements(BY.TAG_NAME, 'option')
gu_names = [opion.get_attribute('value') for option in gu_list]
gu_names.remove("")
gu_names
element_get_excel = driver.find_element(BY.ID, 'glopopd_excel').click()
import time
from tqdom import tqdm_notebook
for gu in tqdm_notebook(gu_names):
element = driver.find_element(BY.ID , 'SIGUNGU_NM0')
element.send_keys(gu)
time.sleep(2)
element_get_excel = driver.find_element(BY.ID, 'glopopd_excel').click()
time.sleep(1)
driver.close()
import pandas as pd
from glob import glob
stations_files = glob("../data/지역_*.xls)
stations_files
tmp_raw = []
for file_name in stations_files:
tmp = pd.read_excel(file_name, hedaer = 2)
tmp_raw.append(tmp)
station_raw = pd.concat(tmp_raw)
station_raw.info()
station_raw.head()
stations = pd.DataFrame(
{
"상호": station_raw["상호"],
"주소": station_raw["주소"],
"가격": station_raw["휘발유"],
"셀프": station_raw["셀프여부"],
"상표": station_raw["상표"],
}
)
stations['구'] = [eachAddress.split()[1] for eachAddress in stations['주소']]
len(stations['구'].unique())
stations[stations['구'] == '서울특별시', '구'] = '성동구'
stations[stations['구'] == '특별시', '구'] = '도봉구'
len(stations['구'].unique())
stations['가격'] = stations['가격'].astype('float')
# 가격이 없어서 -로 표시된 부분 버리기
stations = stations[stations['가격'] != '-']
stations['가격'] = stations['가격'].astype('float')
stations.reset_index(inplace = True)
del stations['index']
stations.head()
import matplotlib.pyplot as plt
import seaborn as sns
# pandas의 boxplot
stations.boxplot(column = '가격', by = '셀프', figsize = (12, 8));
# seaborn의 boxplot
plt.figure(figsize = (12, 8))
sns.boxplot(x = '셀프', y = '가격', data = stations, palette = 'Set3')
plt.grid()
plt.show()
plt.figure(figsize = (12, 8))
sns.boxplot(x = '상표', y = '가격', hue = '셀프', data = stations, palette = 'Set3')
plt.grid()
plt.show()
# 지도 시각화
import json
immport folium
import warnings
Warnings.simplefilter(acction = 'ignore', category = futrueWarning)
stations.sort_values(by = '가격', asceding = False).head(10)
stations.sort_values(by = '가격', asceding = True).head(10)
import numpy as np
gu_data = pd.pivot_table(stations, index = ['구'], values = ['가격'], aggfunc = np.mean)
gu_data.head()
geo_path = '../data/02. skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding = 'utf-8'))
my_map = folium.Map(location = [37.5502, 126.982],
zom_start = 10.5,
tiles = 'Stamen Toner')
my_map.choropleth(
geo_data = geo_str,
data = gu_data,
columns = [gu_data.index, '가격'],
fill_color = 'PuRd',
key_on = 'feature.id'
)
my_map
사용하던 노트북에 문제가 있어서 새 노트북을 사는데 시간이 좀 걸렸다. 나는 빨리 구매했지만, 그 분이 삼 주에 걸쳐서 우리 집으로 오심.. 그 과정에서 마음이 지치고, 와중에 웹데이터를 이용하는 내용도 어렵고. 언제나 생각하지만 교육 자체보다는 외적인 것에 더 흔들릴 수 있음을 잊지말아야겠다. 교육은 쉬지 않고 진행되는데, 내가 이걸 끝까지 해내는 것이 목표라면 내가 좀 잘 못하는 순간도 괜찮다 생각하고 넘길 줄 알아야겠다.
이 글은 제로베이스 데이터 취업 스쿨의 강의 자료 일부를 발췌하여 작성되었습니다.