HTML : 웹 페이지를 구성하는 마크업 언어
html 태그 : 웹 페이지 표현
head 태그 : 눈에 보이지 않지만 문서에 필요한 헤더 정보 보관
body 태그 : 눈에 보이는 정보 보관
Beautiful Soup 테스트를 위해 html 작성
Beautiful Soup : 태그로 이루어진 문서를 해석하는 기능을 가진 모듈
html.parser
: Beautiful Soup의 html을 읽는 엔진 중 하나(lxml도 많이 사용)
html.parser, lxml, lxml-xml, xml, html5lib 등
prettify()
: html을 태그 기준으로 들여쓰기를 통해 정리해주는 기능
모듈 설치 : pip install beautifulsoup4
, conda install -c beautifulsoup4
공식 문서 : https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.html
from bs4 import BeautifulSoup
page = open('../data/03. test_firdt.html','r').read()
soup = BeautifulSoup(page, 'html.parser')
print(soup.prettify())
head
키워드 : head 태그를 선택body
키워드 : body 태그를 선택p
키워드 : p 태그를 선택soup.body
soup.find('p')
soup.find_all('p')
soup.find_all(class='outer-text')
soup.find('p', class_='inner-text') # p태그중에서 클래스가 inner-text인 태그 출력
soup.find(id='first')
for each_tag in soup.find_all('p'):
print('-------------')
print(each_tag.get_text())
텍스트 키워드 : 태그에 포함된 텍스트 반환
text
키워드 : 태그에 포함된 텍스트를 반환string
키워드 : 태그에 포함된 텍스트를 반환(단, 단일 태그인 경우에만 동작)get_text()
: 태그에 포함된 텍스트를 반환strip
옵션 : 데이터의 양끝 공백 제거 설정(True / False)stripped_strings
키워드 : for을 사용하여 리스트, 튜플 등의 형태로 반환할 수 있다.links = soup.find_all('a')
for each in links:
href = each['href']
text = each.string
print(text + '->' + href)
외부로 연결되는 링크의 주소 알아내기
find("a")
로 링크 태그를 찾는다.attrs
키워드 : 해당 태그의 속성 dcit형으로 반환태그[속성]
, 태그.get(속성)
: 해당 태그의 속성 값 반환크롬 개발자 도구를 활용해 찾고자 하는 데이터의 태그, 클래스, 아이디 등 정보 확인
urllib
의 request
모듈 : 웹주소에 접근하기 위해 사용하는 모듈urlopen()
: url로 웹 페이지를 요청하는 함수status
키워드 : http 상태 코드로 200일 경우 정상적으로 정보를 받았다는 의미from urllib.request import urlopen
from bs4 import BeautifulSoup
url = 'https://finance.naver.com/marketindex/'
page = urlopen(url) # page대신 response나 res 변수명도 많이 사용
soup = BeautifulSoup(page, 'html.parser')
print(page.status)
print(soup.prettify())
# .status 키워드 : http 상태 코드로 200일 경우 정상적으로 정보를 받았다는 의미
# 환율 정보
soup.find_all("span", class_="value"), len(soup.find_all("span", class_="value"))
# 미국 환율 정보
# string/text/get_text()/getText() : 텍스트
soup.find_all("span", class_="value")[0].string
requests
모듈 사용get(), post()
모드가 있다.text
키워드 : 웹 페이지의 내용 반환import requests
# from urllib.requests.Request 위에와 사실상 동일한 기능
from bs4 import BeautifulSoup
# 환전 고시 환율
# 환율, 등락, 링크
url = 'https://finance.naver.com/marketindex/'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
print(soup.prettify())
find
, select_one
: 단일 선택find_all
, select
: 다중 선택대체로 requests를 많이 쓴다
reqeusts.get()방법이 HTTP method와 연관되어 있으니 직관적으로 이해하기 쉽다
select가 수행시간도 더 빠르며 메모리 소모량도 더 적다
exchangeList = soup.select('#exchangeList > li')
len(exchangeList), exchangeList
데이터 추출
soup.find_all("li", class_ = 'on')
: 태그와 클래스는 중복이 가능하므로class
: .클래스 (>)
태그 - 클래스 소속 중 해당 태그 선택id
: #아이디 (>)
태그 - 아이디 소속 중 해당 태그 선택tag
: 태그 - 태그 선택꺽쇠(>)
: 해당 클래스, 아이디의 바로 하위를 의미(>가 없다면 깊이 우선 탐색을 하게 된다.)exchangeList
하위의 li
태그 추출title = exchangeList[0].select_one('.h_lst').text
print(title)
exchange = exchangeList[0].select_one('.value').text
print(exchange)
change = exchangeList[0].select_one('.change').text
print(change)
# head_info point_(up/dn)
# 공백이 들어간 클래스는 공백을 기준으로 클래스가 2개임을 의미한다.
# 따라서 head_info, point_(up/dn) 2가지를 가지게 된다.
# 여기서 point_up 또는 point_dn은 값에 따라 다르게 되므로 head_info로만 값을 찾는다.
updown = exchangeList[0].select_one('div.head_info > .blind').text
print(updown)
baseUrl = 'https://finance.naver.com'
link = baseUrl + exchangeList[0].select_one('a').get('href')
print(link)
baseUrl = 'https://finance.naver.com'
baseUrl + exchangeList[0].select_one('a').get('href')
# 4개의 데이터 수집
exchange_datas = []
for item in exchangeList:
data = {
'title': item.select_one('.h_lst').text,
'change': item.select_one('.value').text,
'change': item.select_one('.change').text,
'updown': item.select_one('div.head_info.point_dn > .blind').text,
'link': baseUrl + item.select_one('a').get('href')
}
exchange_datas.append(data)
exchange_datas
import pandas as pd
df = pd.DataFrame(exchange_datas)
df
https://ko.wikipedia.org/wiki/%EC%97%AC%EB%AA%85%EC%9D%98_%EB%88%88%EB%8F%99%EC%9E%90
여명의 눈동자 위키백과
목표 : 주요인물에 대한 정보
주소가 꺠지는 이유
-주소는 utf-8로 인코딩되어 있는데 그게 utf-8이 아니게 풀어지니까 위처럼 깨진다
string에서 '{}'를 이용해서 변수화할 수 있다
import urllib
from urllib.request import Request,urlopen
html = 'https://ko.wikipedia.org/wiki/{search_words}'
req = Request(html.format(search_words=urllib.parse.quote('여명의_눈동자')))
response = urlopen(req)
soup = BeautifulSoup(response, 'html.parser')
soup
n = 0
for each in soup.select('ul'):
print('=>' + str(n) + '=============')
print(each)
n += 1
soup.select('ul')[32]
soup.select('ul')[32].text.replace('\xa0','').split('\n')
User-Agnet 생성 모듈
User-Agnet 생성 모듈
from fake_useragent import UserAgent
url_base = 'https://www.chicagomag.com/'
url_sub = '/Chicago-Magazine/November-2012/Best-Sandwiches-Chicago/'
url = url_base + url_sub
# html = urlopen(url) : Error 403 서버에서 접근 차단(자동 봇 차단)
# 크롬 개발자 도구 -> Network 상세 내용 확인
# User-Agent 생성
# ua = UserAgent()
# ua.ie
# req = Request(url, headers={'User-Agent' : ua.ie})
from bs4 import BeautifulSoup
from urllib.request import Request, urlopen
url_base = 'http://www.chicagomag.com'
url_sub = '/Chicago-Magazine/November-2012/Best-Sandwiches-Chicago/'
url = url_base + url_sub
req = Request(url, headers={'User-Agent':'Chrome'})
html = urlopen(req).read()
soup = BeautifulSoup(html, 'html.parser')
soup
print(soup.select('div.sammy'))
soup.select('div.sammy')[0]
tmp_one = soup.select('div.sammy')[0]
type(tmp_one)
tmp_one.find(class_='sammyRank').get_text()
tmp_one.select_one('.sammyListing').text
tmp_one.select_one('a')['href']
import re
tmp_string = tmp_one.select_one('.sammyListing').text
re.split(('\n|\r\n'), tmp_string)
re
모듈의 split으로 쉽게 구분할 수 있다from urllib.parse import urljoin
url_base = 'http://www.chicagomag.com'
# 각 정보를 담을 빈 리스트
# 모든 정보를 얻은 후 DataFrame으로 합치면 된다.
rank = []
main_menu = []
cafe_name = []
url_add = []
# div의 sammy 태그 추출
list_soup = soup.find_all("div", class_="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_name.append(re.split('\n|\r\n', tmp_string)[1])
# url 주소
# urljoin(a, b) : b가 상대 주소라면 a와 결합하고 b가 절대 주소라면 결합하지 않는다.
url_add.append(urljoin(url_base, item.find('a').get('href')))
import pandas as pd
data = {'Rank':rank, 'Menu': main_menu, 'Cafe': cafe_name, 'URL': url_add}
df = pd.DataFrame(data, columns=['Rank','Cafe','Menu','URL'])
df.head()
df['URL'][0]
req = Request(df['URL'][0], headers={'user-agent':'Chrome'})
html = urlopen(req).read()
soup_tmp = BeautifulSoup(html,'html.parser')
soup_tmp.select_one('p.addy')
price_tmp = soup_tmp.select_one('p.addy').text
import re
price_tmp = re.split('.,', price_tmp)[0]
price_tmp
tmp = re.search('\$\d+\.(\d+)?', price_tmp).group()
tmp
# >>> '$10.'
price_tmp[len(tmp) + 2:]
# >>> '2109 w. chicago Ave'
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,'html.parser')
gettings = soup_tmp.select_one('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:])
df['Price'] = price
df['Address'] = address
df = df.loc[:,['Rank','Cafe','Menu','Price','Address']]
df.set_index('Rank', inplace=True)
df.head()
import googlemaps
import numpy as np
gmaps_key = 'API키'
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()
import folium
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']).add_to(mapping)
mapping