코딩하는 타코야끼

[스터디 노트] Week6_2일차 [unit14 ~ 20] - EDA(웹데이터) 본문

zero-base 데이터 취업 스쿨/스터디 노트

[스터디 노트] Week6_2일차 [unit14 ~ 20] - EDA(웹데이터)

가스오부시 2023. 8. 16. 02:03
728x90
반응형

1 . 웹페이지 분석


📍 방법 1

url = "<https://www.youtube.com>"
page = urlopen(url)
soup = BeautifulSoup(page, "html.parser")

soup.text

📍 방법 2

url = "<https://www.youtube.com>"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")

soup.text

📍분석 과정

📎  HTML 문서에서 클래스명이 "sammy"인 모든 div 태그들을 찾아 리스트로 반환합니다.

print(soup.find_all("div", "sammy"))

len(soup.find_all("div", "sammy"))
>>>
50
soup.find_all("div", "sammy")[0]
>>>
<div class="sammy" style="position: relative;">
<div class="sammyRank">1</div>
<div class="sammyListing"><a href="/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Old-Oak-Tap-BLT/"><b>BLT</b><br/>
Old Oak Tap<br/>
<em>Read more</em> </a></div>
</div>

📎  type이 bs4.element.Tag 라는 것은 find 명령을 사용할 수 있다는 뜻

tmp_one = soup.find_all("div", "sammy")[0]
type(tmp_one)
>>>
bs4.element.Tag
tmp_one.find(class_ = "sammyListing").get_text()
>>>
'BLT\\nOld Oak Tap\\nRead more '

📎  연결된 홈페이지 주소는 "상대경로" 이다.

tmp_one.find("a")["href"]
>>>
'/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Old-Oak-Tap-BLT/'

📎  가게 이름과 메뉴는 re모듈의 split으로 쉡게 구분할 수 있다.

import re

tmp_string = tmp_one.find(class_= "sammyListing").get_text()
re.split(("\\n|\\r\\n"), tmp_string)

📍 반복문

from urllib.parse import urljoin

url_base = "http:///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 = ['BLT', 'Old Oak Tap', 'Read more ']
    tmp_string = item.find(class_ = "sammyListing").get_text()
    cafe_name.append(re.split("\\n|r\\n", tmp_string)[0])
    main_menu.append(re.split("\\n|r\\n", tmp_string)[1])
    url_add.append(urljoin(url_base, item.find("a")["href"]))
len(rank), len(main_menu), len(cafe_name), len(url_add)
>>>
(50, 50, 50, 50)

📍 데이터프레임 정의

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()


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

df["URL"][0]
>>>
'<http://www.chicagomag.com/Chicago-Magazine/November-2012/Best-Sandwiches-in-Chicago-Old-Oak-Tap-BLT/>'

req = Request(df["URL"][0], headers = {"User-Agent": "Mozilla/5.0"})
html = urlopen(req).read()

soup_tmp = BeautifulSoup(html, "html.parser")
soup_tmp.find("p", "addy")
>>>
<p class="addy">
<em>$10. 2109 W. Chicago Ave., 773-772-0406, <a href="http://www.theoldoaktap.com/">theoldoaktap.com</a></em></p>

📎  가격만 가져고오 싶으나 주소와 같이 있다.

price_tmp = soup_tmp.find("p", "addy").get_text()
price_tmp
>>>
'\\n$10. 2109 W. Chicago Ave., 773-772-0406, theoldoaktap.com'

📍 정규 표현식

re.search("\\$\\d+\\.(\\d+)?", price_tmp).group()
>>>
'$10.'
  • 이 코드는 Python의 re 모듈을 사용하여 문자열 price_tmp에서 특정 패턴에 일치하는 부분을 검색합니다.
    • \$: $ 문자와 일치합니다. 기본적으로 $는 정규 표현식에서 문자열의 끝을 나타내는 메타문자이므로, 리터럴 $ 문자와 일치시키려면 탈출 문자 \를 앞에 두어야 합니다.
    • \d+: 하나 이상의 숫자와 일치합니다.
    • \. : 리터럴 점(.) 문자와 일치합니다. .는 어떤 문자와도 일치하는 메타문자이므로, 리터럴 점 문자와 일치시키려면 탈출 문자 \를 앞에 두어야 합니다.
    • (\d+)? : 하나 이상의 숫자와 일치하는 그룹을 의미하며, ?는 이 그룹이 0번 또는 1번 나타날 수 있음을 나타냅니다. 이것은 소수점 뒤의 숫자가 옵션이라는 것을 의미합니다.
    • 이 정규 표현식은 "$" 문자로 시작하고, 그 다음에 하나 이상의 숫자, 그리고 선택적으로 점 뒤에 숫자가 오는 문자열 패턴을 찾습니다. 예를 들면 $10, $99.99와 같은 문자열과 일치합니다.
    • re.search() 함수는 주어진 문자열에서 패턴에 처음으로 일치하는 부분을 찾아 Match 객체를 반환합니다. group() 메서드는 일치하는 문자열을 반환합니다.
tmp = re.search("\\$\\d+\\.(\\d+)?", price_tmp).group()
price_tmp[len(tmp) + 2:]
>>>
'2109 W. Chicago Ave'

3. iterrows( ) 를 활용한 반복분

price = []
address = []

for idx, row in df[:3].iterrows():
    req = Request(row["URL"], headers = {"User-Agent": "Chrome"})
    html = urlopen(req).read()
    
    soup_tmp = BeautifulSoup(html, "lxml")
    gettings = soup_tmp.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(price)
print(address)
>>>
['$10.', '$9.', '$9.50']
['2109 W. Chicago Ave', '800 W. Randolph St', ' 445 N. Clark St']

4. tqdm

  • Python에서는 진행률 표시 막대(progress bar)를 콘솔이나 주피터 노트북 등에 출력하기 위한 패키지입니다. tqdm은 반복 가능한 객체(예: 리스트, 사전, 파일 등)에 대한 반복을 감싸서 진행률과 예상 남은 시간을 표시합니다.
  • 예를들어, for 문을 사용할 때 이게 동작중인지 혹은 얼마나 시간이 남은건지 알 수 있습니다.

❗️ 주의할 점:

  • tqdm을 사용할 때는 다음과 같은 사항에 주의해야 합니다:
    • 출력이 너무 빈번하게 업데이트되면, 실제 연산에 영향을 줄 수 있으므로 mininterval 및 maxinterval 파라미터를 조절하는 것이 좋습니다.
    • 코드 내에서 여러 곳에 tqdm이 사용될 경우 진행률 표시 막대가 혼동될 수 있습니다.
    • 진행률 표시 막대를 사용하면 코드의 가독성이 떨어질 수 있으므로, 필요할 때만 사용하는 것이 좋습니다.
    • 이외에도 tqdm은 다양한 설정과 확장 기능을 제공하므로 공식 문서나 관련 자료를 참조하면 더 많은 정보를 얻을 수 있습니다.
from tqdm import tqdm

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, "lxml")
    gettings = soup_tmp.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:])

df["Price"] = price
df["Address"] = address

df = df.loc[:,["Rank", "Cafe", "Menu", "Price", "Address"]]
df.set_index("Rank", inplace = True)
df.head()


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

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()


📍 folium 시각화

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([row["lat"], row["lng"]], popup = row["Cafe"]).add_to(mapping)
        
mapping

반응형