스터디 노트(EDA 3-1)

zoe·2023년 3월 31일
0

BeatifulSoup

  • install
    - conda install -c anaconda beautifulsoup4
    - pip install Beautifulsuop4

  • data
    - 03.test_first.html
    - https://www.crummy.com/software/BeautifulSoup/bs4/doc/

  • prettyfy() : 들여쓰기가 된 소스로 볼 수 있다, html 출력을 예쁘게 만듦

  • find() : 입력한 태그 내용 확인, 입력한 태그와 동일한 태그들 중 처음 발견한 태그 출력

  • class_ = : 파이썬 예약어를 피하기 위해 이렇게 적는다

  • find_all() : 입력한 태그와 동일한 태그들 여러 개 모두 출력, 지정된 태그 모두 찾아준다, 리스트 형태로 반환

  • ["href"] : 태그에서 href 속성값에 있는 값 추출



# beautifulsoup4 설치

#pip install beautifulsoup4
#import
from bs4 import BeautifulSoup
# 파일 읽기
page = open("../data/03. html test.html", "r").read()

soup = BeautifulSoup(page, "html.parser")
print(soup.prettify()) # prettyfy() : 들여쓰기가 된 소스로 볼 수 있다, html 출력을 예쁘게 만듦
# head 태그 확인
soup.head
# body 태그 확인
soup.body
# p 태그 확인
# 처음 발견된 태그 내용 확인
soup.p
# find() : 입력한 태그 내용 확인, 입력한 태그와 동일한 태그들 중 처음 발견한 태그 출력
soup.find("p")
soup.find("p", class_="inner-text first-item") 
# class_ =  : 파이썬 예약어를 피하기 위해 이렇게 적는다
soup.find("p",{"class":"outer-text first-item"}).text.strip()
# 다중조건
soup.find("p",{"class":"inner-text first-item"},{"id":"first"})
# find_all() : 
# 입력한 태그와 동일한 태그들 여러 개 모두 출력, 지정된 태그 모두 찾아준다, 
# 리스트 형태로 반환

soup.find_all("p")
# 특정 태그 확인
soup.find_all(class_="outer-text")
soup.find_all(id = "pw-link")[0].text
soup.find_all("p", class_="inner-text first-item")
print(soup.find_all("p")[0].text)
print(soup.find_all("p")[1].string)
print(soup.find_all("p")[1].get_text())
# p 태그 리스트에서 텍스트 속성만 출력

for each_tag in soup.find_all("p"):
    print("-"*50)
    print(each_tag.text)
# a 태그에서 href 속성값에 있는 값 추출

links = soup.find_all("a")
links[0].get("href"), links[1]["href"]
for each in links:
    href=each.get("href") # each["href"] 도 같음
    text = each.get_text()
    print(text + "→" + href)
    



BeautifulSoup 예제 1-1 - 네이버 금융

# import

from urllib.request import urlopen
from bs4 import BeautifulSoup
url = "https://finance.naver.com/marketindex/"

#page = urlopen(url) #response, res 라는 변수도 많이 사용함, 
response = urlopen(url)

print(response.status) # http 상태코드를 받을 수 있음
# https://ko.wikipedia.org/wiki/HTTP_%EC%83%81%ED%83%9C_%EC%BD%94%EB%93%9C


soup = BeautifulSoup(response, "html.parser")
print(soup.prettify())
# 데이터 추출 방법 1
soup.find_all("span","value") , len(soup.find_all("span","value"))
# 데이터 추출 방법 2
soup.find_all("span",class_="value"), len(soup.find_all("span","value"))
# 데이터 추출 방법 3
soup.find_all("span",{"class":"value"}), len(soup.find_all("span","value"))
soup.find_all("span",{"class":"value"})[0].text, soup.find_all("span",{"class":"value"})[0].string, soup.find_all("span",{"class":"value"})[0].get_text()



BeautifulSoup 예제 1-2 - 네이버 금융

  • !pip install requests

  • find, find_all

  • select, select_one
    - id가 있을 경우엔 앞에 '#', class가 있을 경우엔 앞에 '.' ★

  • find, select_one : 단일선택

  • find_all, select : 다중선택

  • request.get(), request.post() 두 가지 방식이 있음

  • class = "head_info point_up" 와 같은 경우 두 개의 클래스가 있는 것이므로 가운데에 '.'을 입력

  • .select_one("div.head_info.point_up > .blind").text : >가 있으면 해당 태그의 바로 하위로 이동,

  • .select_one("div.head_info.point_up .blind").text : >가 없고 띄어쓰기로 구분할 경우 모든 하위 태그를 뜻함



#!pip install requests 
# request 모듈 설치
import requests
#from urllib.request.Request  # requests 모듈과 거의 동일
from bs4 import BeautifulSoup
import pandas as pd
url = "https://finance.naver.com/marketindex/"
response = requests.get(url) 
# request.get(), request.post() 두 가지 방식이 있음
# response.text

soup = BeautifulSoup(response.text, "html.parser")
print(soup.prettify())
#soup.find_all("li", "on")
exchangeList = soup.select("#exchangeList> li") # id가 있을 경우엔 앞에 '#', class는 '.'
# exchaneList 바로 아래의 li값을 가져오겠다


len(exchangeList)
exchangeList
# select를 사용할 경우 

title = exchangeList[0].select_one(".h_lst").get_text()
exchange = exchangeList[0].select_one(".value").text
change = exchangeList[0].select_one(".change").string
down = exchangeList[0].select_one("div.head_info.point_up > .blind").text
# class = "head_info point_up" 와 같은 경우 두 개의 클래스가 있는 것이므로 가운데에 '.'을 입력

# .select_one("div.head_info.point_up > .blind").text : >가 있으면 해당 태그의 바로 하위로 이동, 
# .select_one("div.head_info.point_up .blind").text : >가 없고 띄어쓰기로 구분할 경우 모든 하위 태그를 뜻함
baseURL = "https://finance.naver.com"
link =  baseURL + exchangeList[0].select_one("a").get("href") # ★


title, exchange, change, down, link
# find_all을 사용할 경우
findmethod = soup.find_all("ul", id="exchangeList")
findmethod[0].find_all("span", "value")
# 4개의 데이터 수정

exchange_datas = []
baseURL = "https://finance.naver.com"

for item in exchangeList:
    data = {
        "title" : item.select_one(".h_lst").get_text(),
        "exchange" : item.select_one(".value").text,
        "change" : item.select_one(".change").string,
        "down" : item.select_one("div.head_info.point_up > .blind").text,
        "link" : baseURL + item.select_one("a").get("href")
    }
    exchange_datas.append(data)

exchange_datas
df = pd.DataFrame(exchange_datas)
df
df.to_excel("./naverfinance.xlsx", encoding="utf-8")




BeautifulSoup 예제2 - 위키백과 문서 정보 가져오기

  • html = "https://ko.wikipedia.org/wiki/%EC%97%AC%EB%AA%85%EC%9D%98_%EB%88%88%EB%8F%99%EC%9E%90"
    - urldecode/encode 검색해서 관련 페이지로 들어가서 url 확인하면 인코딩 깨진 것이 수정된 url을 얻을 수 있음
    - 한글 인코딩이 깨지는 것을 포맷팅으로 변경해서 값 입력
    - 요청하기 전에 포맷팅 관련 작업 진행
    - 글자를 url로 인코딩
  • list형은 대괄호로 생성
    - list형을 반복문에(for) 적용
    - in 명령으로 조건문(if)에 적용
    - append() : list 제일 뒤에 추가
    - pop() : list 제일 뒤부터 자료를 하나씩 삭제
    - extend() : 제일 뒤에 자료 추가
    - remove() : 자료를 삭제
    - 슬라이싱 : [n : m : t] n번째부터 n - 1까지 t만큼씩 띄어서
    - insert() : 원하는 위치에 자료를 저장
    - isintance : 자료형 True / False


import urllib
from urllib.request import urlopen, Request

#html = "https://ko.wikipedia.org/wiki/%EC%97%AC%EB%AA%85%EC%9D%98_%EB%88%88%EB%8F%99%EC%9E%90"
# urldecode/encode 검색해서 관련 페이지로 들어가서 url 확인하면 인코딩 깨진 것이 수정된 url을 얻을 수 있음

html = "https://ko.wikipedia.org/wiki/{search_words}" #한글 인코딩이 깨지는 것을 포맷팅으로 변경해서 값 입력

req = Request(html.format(search_words=urllib.parse.quote("여명의_눈동자"))) 
# 요청하기 전에 포맷팅 관련 작업 진행
# 글자를 url로 인코딩

response = urlopen(req)
response.status


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
soup.find_all("ul")[32].text.strip().replace("\xa0","").replace("\n","")


colors = ["red","blue","green"]

colors[0], colors[1], colors[2]
b = colors
b
b[1] = "black"
b
colors
c = colors.copy()
c
c[1] = "yellow"
c
colors
# list형을 반복문에(for) 적용

for color in colors:
    print(color)
# in 명령으로 조건문(if)에 적용

if "white" in colors:
    print("True")
movies = ["라라랜드", "먼 훗날 우리","어벤져스", "다크나이트"]
print(movies)
# .append() : list 제일 뒤에 추가

movies.append("타이타닉")
movies
# .pop() : list 제일 뒤부터 자료를 하나씩 삭제
movies.pop()
movies.pop()
movies
# extend() : 제일 뒤에 자료 추가
movies.extend(["위대한쇼맨", "인셉션", "터미네이터"])
movies
# remove() : 자료를 삭제
movies.remove("어벤져스")
movies
# 슬라이싱 : [n : m : t] n번째부터 n - 1까지 t만큼씩 띄어서
movies[3:5]
favorite_movies = movies[3:5]
favorite_movies
# insert() : 원하는 위치에 자료를 저장
favorite_movies.insert(1, 9.60)
favorite_movies
favorite_movies.insert(3, 9.50)
favorite_movies
# list안에 list
favorite_movies.insert(5, ["레오나르도 디카프리오","홍길동"])
favorite_movies
# isintance : 자료형 True / False
isinstance(favorite_movies, list)
for each_item in favorite_movies:
    if isinstance(each_item, list):
        for nested_item in each_item:
            print("nested_item", nested_item)
    else:
        print("each_item", each_item)

시카고 맛집 데이터 분석

2. 시카고 맛집 데이터 분석 - 개요

최종목표
총 51개 페이지에서 각 가게의 정보를 가져온다

  • 가게이름
  • 대표메뉴
  • 대표메뉴의 가격
  • 가게주소

3. 시카고 맛집 데이터 분석 - 메인 페이지

  • ua.ie : userAgent를 랜덤하게 만들어주는 기능 (from fake_useragent import UserAgent 설치 후 사용)
  • re.split(("\n|\r\n"), tmp_string) : \n 이나 \r\n 기준으로 데이터 나누기
  • urljoin() : url 합치기, 설정한 url이 있으면 넣고 없으면 넣어주지 않음
from urllib.request import Request, urlopen
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
# 프롬프트에서 pip install fake-useragent 설치


url_base = "https://www.chicagomag.com/"
url_sub = "chicago-magazine/november-2012/best-sandwiches-chicago/"
url = url_base + url_sub

ua = UserAgent()
ua.ie # userAgent를 랜덤하게 만들어주는 기능

#req = Request(url, headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"}) 
# headers={"User-Agent":"Chrome"} 도 가능

req = Request(url, headers={"user-agent":ua.ie})
html = urlopen(req)
html.status # server error : 403 -> 서버가 나를 막은 것

soup = BeautifulSoup(html, "html.parser")
print(soup.prettify)
soup.find_all("div", "sammy"), len(soup.find_all("div", "sammy"))
#soup.select(".sammy"), len(soup.select(".sammy"))
tmp_one = soup.find_all("div","sammy")[0]
type(tmp_one)
tmp_one.find(class_="sammyRank").get_text()
#tmp_one.select_one(".sammyRank").get_text()
tmp_one.find("div", {"class":"sammyListing"}).get_text()
#tmp_one.select_one(".sammyListing").get_text()
tmp_one.find("a")["href"]
#tmp_one.select_one("a").get("href")
import re

tmp_string = tmp_one.find(class_="sammyListing").get_text()
re.split(("\n|\r\n"), tmp_string) # re.split(("\n|\r\n"), tmp_string) : \n 이나 \r\n 기준으로 데이터 나누기
print(re.split(("\n|\r\n"), tmp_string)[0])
print(re.split(("\n|\r\n"), tmp_string)[1])
from urllib.parse import urljoin

url_base = "https://www.chicagomag.com/"

# 필요할 내용을 담을 빈 리스트
# 리스트로 하나씩 컬럼을 만들고, DataFrame으로 합칠 예정
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").get_text())
    tmp_string = item.find("div", {"class":"sammyListing"}).get_text()
    main_menu.append(re.split(("\n|\r\n"), tmp_string)[0])
    cafe_name.append(re.split(("\n|\r\n"), tmp_string)[1])
    url_add.append(urljoin(url_base, item.find("a")["href"])) 
    # urljoin() : url 합치기, 설정한 url이 있으면 넣고 없으면 넣어주지 않음
len(rank), len(main_menu), len(cafe_name), len(url_add)
import pandas as pd

data = {"Rank" : rank, "Menu" : main_menu, "Cafe" : cafe_name, "URL" : url_add}
df = pd.DataFrame(data)
df.head()
# 컬럼 순서 변경
df = pd.DataFrame(data, columns=["Rank","Cafe","Menu","URL"])
df.head()
df.to_csv("../data/03. best_sandwiches_list_chicago.csv", sep=',',encoding="utf-8")

4. 시카고 맛집 데이터 분석 - 하위페이지

# requirements

import pandas as pd
from urllib.request import urlopen, Request
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
df = pd.read_csv("../data/03. best_sandwiches_list_chicago.csv", index_col=0)
df.tail()
df["URL"][1]
ua = UserAgent()
req = Request(df["URL"][1], headers={"user-agent":ua.ie})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html, "html.parser")
soup_tmp.find("p","addy") # soup_tmp.select_one(".addy")
# regular expression
price_tmp = soup_tmp.find("p","addy").text
price_tmp
import re
re.split(".,",price_tmp)
price_tmp = re.split(".,", price_tmp)[0]
price_tmp
tmp = re.search("\$\d+\.(\d+)?", price_tmp).group() # ★
price_tmp[len(tmp) + 2:] # 가격이 끝난 뒤 데이터를 가져와라
# pip install tqdm  # 설치
from tqdm import tqdm

price = []
address = []

for index, row in tqdm(df.iterrows()): #주소 
    req = Request(row["URL"], headers={"user-agent":ua.ie})
    html = urlopen(req).read()
    soup_tmp = BeautifulSoup(html, "html.parser")
    
    gettings = soup_tmp.find("p", "addy").get_text() # soup_tmp.select_one(".addy")
    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(index)
len(price), len(address)
price[:5], address[:5]
df["Price"] = price
df["Address"] = address
df = df.loc[:, ["Rank","Cafe","Menu", "Price", "Address"]]
df.set_index("Rank", inplace=True)
df.head()
df.to_csv("../data/03. best_sandwiches_list_chicago2.csv", sep=",", encoding="utf-8")
pd.read_csv("../data/03. best_sandwiches_list_chicago2.csv", index_col=0)

5. 시카고 맛집 데이터 지도 시각화

# requirements

import folium
import pandas as pd
import numpy as np
import googlemaps
from tqdm import tqdm
df = pd.read_csv("../data/03. best_sandwiches_list_chicago2.csv", index_col=0)
df.head(5)
gmaps_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"
        #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)
len(lat), len(lng)
df.tail()
df["lat"] = lat
df["lng"] = lng
df.tail()
mapping = folium.Map(location=[41.8781136, -87.6297982], zoom_start = 11)

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

💻 출처 : 제로베이스 데이터 취업 스쿨

profile
#데이터분석 #퍼포먼스마케팅 #데이터 #디지털마케팅

0개의 댓글