1. ์น ํฌ๋กค๋ง(web crawling)์ ๋ฐฐ์ฐ๋ ์ด์
- ์น ํฌ๋กค๋ง์ด๋ ์นํ์ด์ง(๋๋ ์น ์ฌ์ดํธ, static document) ๋ด์ ์๋ ์ ๋ณด๋ฅผ ์ถ์ถํ๋ ํ์, ์ฆ ์ธํฐ๋ท ์ฝํ ์ธ ๋ฅผ ์์ธํํ๋ ๊ณผ์ ์ ์๋ฏธํจ
- ๋ฐ์ดํฐ ๋ถ์์ ํ์ฉํ๊ณ ์ถ์ ๋ฐ์ดํฐ๋ฅผ ์น ํ์ด์ง์์ ์ถ์ถํ ์ ์๊ธฐ๋๋ฌธ์ ์ค์ํจ
- Beautiful Soup ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ html๊ณผ xml ๋ฌธ์๋ฅผ parsing 1ํ ์ ์๊ณ , Selenium์ ๋์ ํฌ๋กค๋ง์ ํจ๊ณผ์ ์ผ๋ก ์ํํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ฐจ์ด๊ฐ ๊ถ๊ธํ๋ค๋ฉด ๋ค์ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ์ฐธ๊ณ
- HTML์ ๊ธฐ๋ณธ์ ์ธ ์ดํด๊ฐ ์์ด์ผํจ
2. requests & Beautiful Soup ํ์ฉ
- requests : ์ํ๋ ์น ํ์ด์ง์ html ๋ฌธ์๋ฅผ ์น ๊ธ์ด์จ๋ค.
- Beautiful Soup : html ๋ฌธ์์์ ์ํ๋ ๊ฒ์ ๊ณจ๋ผ์ ์ ๋ณด๋ฅผ ์ถ์ถํ๋ค.
[ํฐ๋ฏธ๋์ ์ ๋ ฅํ์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์นํ๋ ์ฝ๋]
pip install requests
[ํ์ด์ฌ ์ฝ๋]
# ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ถ๋ฌ์ค๊ธฐ
import requests
from bs4 import BeautifulSoup
# ์ํ๋ ์ฌ์ดํธ์ ์ ์ : requests.get
url = "http://www.google.com"
response = requests.get(url)
print(response) # <Response [200]>
print(response.text) # HTML ๋ฌธ์ ์ถ๋ ฅ
'''์๋ชป๋ ์์ฒญ์ ๋ํ ์์ธ ์ฒ๋ฆฌ'''
# case 1
if response.status_code == 200:
# ์๋ต๋ฐ์ text๋ฅผ HTML ํ์ฑ
html = response.text
soup = BeautifulSoup(html, 'html.parser')
# print(soup)
print(soup.prettify()) # HTML ๋ค์ฌ์ฐ๊ธฐํ์ฌ ๊น๋ํ๊ฒ ๋ณด์ด๊ฒ ํด์ฃผ๋ ํจ์
else:
print('์๋ชป๋ ์์ฒญ์
๋๋ค. ๋ค์ ํ์ธ ๋ฐ๋!')
# case 2
# ์์ if ์ ์ด๋ฌธ๊ณผ ๊ฐ์ ํจ๊ณผ
response.raise_for_status() # 200๋ฒ๋ ์ฝ๋๊ฐ ์๋๋ฉด ์ฝ๋๊ฐ ๋ฉ์ถค
print("๋ฌธ์ ๊ฐ ์์ผ๋ฉด ์ด ๋ถ๋ถ ์ถ๋ ฅ๋ฉ๋๋ค.", len(response.text))
'''ํ์ผ ์์ฑ ๋ฐ ์ ์ฅํ๊ธฐ'''
# with open('ํ์ผ๋ช
', '์ต์
', ์ธ์ฝ๋ฉ) as f
# ๊ฒฝ๋ก๋ฅผ ์ง์ ํ์ง์์ผ๋ฉด ํด๋น ๋๋ ํ ๋ฆฌ์ ์์ฑ
with open('mypage.html', 'w', encoding ='utf-8') as f:
f.write(response.text)
3. Beautiful Soup ํจ์ ์ ๋ฆฌ
- find(name, attrs, recursive, string, **kwargs) : ํด๋น ์กฐ๊ฑด์ ๋ง๋ ํ๋์ ํ๊ทธ ๊ฐ์ ธ์ด(์ค๋ณต์ด๋ฉด ๊ฐ์ฅ ์ฒซ๋ฒ์งธ ํ๊ทธ) โก๏ธ ๋จ์ผ๋ ธ๋ ๋ฐํ(1๊ฐ)
- find_all(name, attrs, recursive, string, limit, **kwargs) : ํด๋น ์กฐ๊ฑด์ ๋ง๋ ๋ชจ๋ ํ๊ทธ ๊ฐ์ ธ์ด โก๏ธ list ๋ฐํ(๋ณต์๊ฐ)
# example html
html = """
<html>
<body>
<div id="wrap">
<ul>
<li><a href="https://www.naver.com/">NAVER</a></li>
<li><a href="https://www.daum.net/">DAUM</a></li>
<li><a href="https://www.google.com/">GOOGLE</a></li>
</ul>
</div>
</body>
</html>
"""
soup = BeautifulSoup(html, "html.parser")
print(soup.prettify()) # ํ์ธ
# html ์ฝ๋ ์์์ aํ๊ทธ๋ฅผ ์ฐพ์์ ๋ฐํ
print("find : ", soup.find('a'), type(soup.find('a'))) # 1๊ฐ
print("find_all : ", soup.find_all('a'), type(soup.find_all('a'))) # 3๊ฐ
'''
out:
find : <a href="https://www.naver.com/">NAVER</a> <class 'bs4.element.Tag'>
find_all : [<a href="https://www.naver.com/">NAVER</a>, <a href="https://www.daum.net/">DAUM</a>, <a href="https://www.google.com/">GOOGLE</a>] <class 'bs4.element.ResultSet'>
'''
- get_text(), text : ํ ์คํธ๋ง ์ถ์ถ
# with find
print(soup.find('a').get_text())
print(soup.find('a').text)
'''
out:
NAVER
NAVER
'''
# with find_all
# ์ฌ๋ฌ๊ฐ์ ๊ฐ์ด ์์ ๋ : for ๋ฐ๋ณต๋ฌธ๊ณผ ํจ๊ป ํ์ฉ
names = soup.find_all('a')
for name in names:
print(name.text)
print(name.get_text())
# ๋ค๋ฅธ ์์
# GOO๋ก ์์ํ๋ ์ฌ์ดํธ๋ฅผ ๋ฆฌํด๋ฐ์์ ์ถ๋ ฅ
'''string.startswith() : ํน์ ๋ฌธ์์ด๋ก ์์ํ๋์ง ํ์ธ'''
names = soup.find_all('a')
for name in names:
if name.text.startswith('GOO'):
print(name.text)
- find('ํ๊ทธ', attrs = { id (class) :'์์ฑ'}) : ํ๊ทธ์ ์์ฑ์ ์ฐพ๋ ๋ฐฉ๋ฒ
# ํ๊ทธ์ ์์ฑ์ ์ฐพ๋ ๋ฐฉ๋ฒ
name = soup.find('a', attrs = {'id':'final'})
name
# out : <a href="https://www.google.com/" id="final">GOOGLE</a>
- copy selector๋ฅผ ์ฌ์ฉํ select_one, select : ํด๋น url ์น ์ฌ์ดํธ์์ ๋ง์ฐ์ค ์ฐํด๋ฆญ โก๏ธ ๊ฒ์ฌ โก๏ธ ์ํ๋ ๋ถ๋ถ์ ๋ํ html์์ selector copy! โก๏ธ select_one("๋ถ์ฌ๋ฃ๊ธฐ")
[example 1]
## example
url = "https://kin.naver.com/search/list.naver?query=ํ์ด์ฌ"
response = requests.get(url)
if response.status_code == 200:
html = response.text
soup = BeautifulSoup(html, "html.parser")
title = soup.select_one("#s_content > div.section > ul > li:nth-child(2) > dl > dt > a")
print(title.find('b').text)
else:
print(response.status_code)
print("๋ค์ ์๋ํด์ฃผ์ธ์ฅ!!!!!!!!")
# out: ํ์ด์ฌ
# -------------------------------------------------------------
# ๊ฒ์ฌ -> ์ํ๋ ํ๊ทธ ์ ํ -> ์ฐํด๋ฆญ -> copy -> copy selector
#s_content > div.section > ul > li:nth-child(2) > dl > dt > a
title = soup.select_one("#s_content > div.section > ul > li:nth-child(2) > dl > dt > a")
print(title.text)
# out : ํ์ด์ฌ ์๋ ค์ฃผ์ค ๋ถ์๋์?
# -------------------------------------------------------------
## ์ ๋ชฉ๋ง ์ถ๋ ฅํ๊ธฐ
# case 1
titles = soup.find_all('a', attrs = {'class':'_nclicks:kin.txt _searchListTitleAnchor'})
for title in titles:
print(title.text)
# case 2
title = soup.find_all('dt')
result = []
for tl in title:
result.append(tl.find('a').text)
print(*result, sep = "\n")
# case 3
titles = soup.select("li>dl>dt>a")
for title in titles:
print(title.text)
'''
out:
ํ์ด์ฌ ์ ์ฅ
ํ์ด์ฌ ์๋ ค์ฃผ์ค ๋ถ์๋์?
ํ์ด์ฌ ์ง๋ฌธ
ํ์ด์ฌ๋จธ์ ๋ฌ๋ ๊ณต๋ถ๊ฐ ํ์ํฉ๋๋ค.
ํ์ด์ฌ ์
๋ ๋์
ํ์ด์ฌ์ด๋ C์ธ์ด ๋ฐฐ์ฐ๋ ๋ฐฉ๋ฒ
ํ์ด์ฌ์์๋ i = 3 ์ด๋ฐ ๊ฒ ์๋ฌ๊ฐ ๋๋์?
ํ์ด์ฌ csvํ์ผ ๋ถ๋ฌ์ค๊ธฐ
ํ์ด์ฌ์ ์๋ฃํ ์ด๋ป๊ฒ ํํํ๋์?
ํ์ด์ฌ ์ค๋ฐ๊ฟ
'''
[example 2]
- ์๋ง์กด ๊ณ์ด์ฌ์ธ ์๋ ์ฌ(Alexa Internet.Inc) : ์ ์ธ๊ณ์ ์ผ๋ก ํน์ ๋๋ผ๋ณ๋ก ์น ์ฌ์ดํธ์ ์์ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ์ฌ์ดํธ
- ํด๋น ์ฌ์ดํธ์์ ์์ 5์๊น์ง์ ๋ญํน ์ฌ์ดํธ ์ด๋ฆ ์ถ์ถ
import requests
from bs4 import BeautifulSoup
url = "https://www.alexa.com/topsItes/countries/KR"
rank = requests.get(url).text
soup = BeautifulSoup(rank, 'html.parser')
# ํ๊ทธ๋ก ํธ์ถํ๊ธฐ
ranks = soup.select('p a')
ranks
# ์์ 5์๊น์ง์ ๋ญํน ์ฌ์ดํธ ์ด๋ฆ๋ง
for i in range(1, 6):
print(f"{i}. {ranks[i].text}")
'''
out:
1. Google.com
2. Naver.com
3. Youtube.com
4. Tistory.com
5. Daum.net
'''
## DataFrame์ผ๋ก ๋ณํํ๊ธฐ
import pandas as pd
rank_dic = {'Website':ranks}
df = pd.DataFrame(rank_dic, columns = ["Website"])
df.drop(0, inplace = True)
df.head(5)
๐ ํจ์ ์ด ์ ๋ฆฌ
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ตฌ๋ถ | ํจ์ | ๊ธฐ๋ฅ |
requests | response = requests.get({url}) | ์ํ๋ ์น ์ฌ์ดํธ(url)์ ์ ์ (๋ณดํต response ๋ณ์์ ์ ์ํจ) |
response.status_code | ์๋ต ์ฝ๋(Response) ๋ฐํ | |
response.text | HTML ๋ฌธ์ ๋ฆฌํด | |
response.raise_for_status() | 200๋ฒ๋ ์๋ต์ฝ๋๊ฐ ์๋๋ฉด ์์ธ๋ฅผ ๋ฐ์์ํด (2XX : ์ฑ๊ณต, 200 : ์๋ฒ๊ฐ ์์ฒญ์ ์ ๋๋ก ์ฒ๋ฆฌํ๋ค๋ ๋ป) |
|
Beautiful Soup |
soup = BeautifulSoup(html, 'html.parser') | ์๋ต๋ฐ์ HTML ํ์ฑ (๋ณดํต soup ๋ณ์์ ์ ์ํจ) |
soup.prettify() | ||
soup.find(name, attrs, recursive, string, **kwargs) | ํด๋น ์กฐ๊ฑด์ ๋ง๋ ํ๋์ ํ๊ทธ ๊ฐ์ ธ์ด (์ค๋ณต์ด๋ฉด ๊ฐ์ฅ ์ฒซ๋ฒ์งธ ํ๊ทธ) โก๏ธ ๋จ์ผ๋ ธ๋ ๋ฐํ(1๊ฐ) |
|
soup.find_all(name, attrs, recursive, string, limit, **kwargs) | ํด๋น ์กฐ๊ฑด์ ๋ง๋ ๋ชจ๋ ํ๊ทธ ๊ฐ์ ธ์ด โก๏ธ list ๋ฐํ(๋ณต์๊ฐ) |
|
soup.find('ํ๊ทธ', attrs = { id (class) :'์์ฑ'}) | ํ๊ทธ์ ์์ฑ์ ์ฐพ๋ ๋ฐฉ๋ฒ | |
soup.get_text(), soup.text | ํ ์คํธ๋ง ์ถ์ถ | |
soup.select_one, soup.select | copy selector์ ์ฌ์ฉํ์ฌ ์ฐพ๋ ํจ์ |
๐ Reference
- [Python] ์น ํฌ๋กค๋ง์ ์ฌ์ฉํ๋ Beautiful Soup(๋ทฐํฐํ ์ํ) ์ฌ์ฉ๋ฒ๊ณผ ์์ ๋ธ๋ก๊ทธ
- ๋ธ๋ก๊ทธ ๋ํ ์ด๋ฏธ์ง ์ถ์ฒโ
- HTML ์ฝ๋ ๊ตฌ๋ฌธ์ ์์๋ณ๋ก ๊ตฌ๋ถํ๊ณ ์ฒ๋ฆฌํ๋ ์์ [๋ณธ๋ฌธ์ผ๋ก]