2021. 1. 26. 17:02ㆍ교육과정/KOSMO
키워드 : 파이썬 스크래핑(크롤링) / 네이버 영화 평점 스크래핑 / 다음 영화 평점 스크래핑
****
1. 특정 요소만 찾아서 출력하기
(1)
http://www.pythonscraping.com/pages/warandpeace.html 에서 녹색 글자만 추출하여 출력하기 |
from urllib import request
from bs4 import BeautifulSoup
# url 을 가져와서 파싱한다.
site1 = request.urlopen('http://www.pythonscraping.com/pages/warandpeace.html')
soup1 = BeautifulSoup(site1, 'html.parser')
# 클래스가 green 인 요소를 찾기 - 리스트에 담아진다.
green = soup1.select('.green')
# 문자들을 자른 뒤, 공백을 기준으로 join 하여 한 단어로 만들어 출력한다.
for i in green:
j = i.text.split()
print(' '.join(j))
# 실행결과
Anna Pavlovna Scherer
Empress Marya Fedorovna
Prince Vasili Kuragin
....
(2)
http://www.pythonscraping.com/pages/page3.html 에서 아이템과 가격($ 제외)을 추출하여 출력하기 |
from urllib import request
from bs4 import BeautifulSoup
site2 = request.urlopen('http://www.pythonscraping.com/pages/page3.html')
soup2 = BeautifulSoup(site2, 'html.parser')
for i in soup2:
item = soup2.select('.gift > td:nth-child(1)')
price = soup2.select('.gift > td:nth-child(3)')
for item, price in zip(item, price):
print(item.text.strip(), price.text.strip().replace('$',''))
# 실행결과
Vegetable Basket 15.00
Russian Nesting Dolls 10,000.52
Fish Painting 10,005.00
....
(3)
https://wikidocs.net/ 에서 책 제목과 설명만 추출 / 이미지 다운로드 하기 |
from urllib import request
from bs4 import BeautifulSoup
import os, re
url3 = 'https://wikidocs.net'
site3 = request.urlopen(url3)
soup3 = BeautifulSoup(site3, 'html.parser')
# 도서 제목과 설명, 이미지가 포함된 요소 찾아서 각각 리스트에 담기
titles = [title.text for title in soup3.select('div#books a.book-subject')]
writers = [writer.text for writer in soup3.select('div#books div.book-detail div:nth-of-type(1) a')]
publish = [publish.text.strip().replace('-','') for publish in soup3.select('div#books div.book-detail div:nth-of-type(2)')]
recommend = [recommend.text.strip() for recommend in soup3.select('div#books div.book-detail div:nth-of-type(3) a')]
imgs = ['{}{}'.format(url3, img.attrs['src']) for img in soup3.select('div#books img')]
# 도서 제목, 설명 출력하기
for t, w, p, r, i in zip(titles, writers, publish, recommend, imgs):
print('{}\n{} |{}\n{}\n'.format(t, w, p, r))
# 이미지 파일을 다운받을 폴더 생성
if not os.path.exists('./bookImage'):
os.makedirs('./bookImage')
# 다운받을 파일명 지정
filedown = 'bookImage/{}.png'.format(t)
# 이미지 다운로드 (링크에 한글이 있을 경우 다운로드 제외)
if re.compile('[가-힣]+').findall(i):
print(i,'-> 다운로드되지 않음')
else:
request.urlretrieve(i, filedown)
# 실행결과
점프 투 파이썬
박응용 | 2021년 01월 12일
3,304 명이 추천
...
※ 이미지파일 주소가 한글일 때도 다운받는 스크립트
from bs4 import BeautifulSoup
from urllib.request import *
from urllib import parse
html = urlopen("https://wikidocs.net/")
soup = BeautifulSoup(html, 'html.parser');
x = soup.select('.book-subject');
y = soup.select('.book-detail');
z = soup.select('.book-image');
for a, b, c in zip(x, y, z):
print(a.text); # 도서명
temp = b.text.replace('\n','').replace(' ', '').replace('-', ' ');
print(temp); # 저자명
with open('img/{}.png'.format(a.text), "wb") as f:
url = 'https://wikidocs.net' + parse.quote(c.attrs['src'].replace('//', '/'), encoding='UTF-8');
f.write(urlopen(url).read());
2. 기상청 사이트에서 특정 요소만 찾아서 출력하기
(1) 필요한 라이브러리 임포트
from bs4 import BeautifulSoup
from urllib import request as req
(2) 데이터 가져오기
※ http://www.kma.go.kr > [생활과산업] > [서비스] > [인터넷] > RSS
rssUrl = 'http://www.kma.go.kr/weather/forecast/mid-term-rss3.jsp'
res = req.urlopen(rssUrl)
print(res)
# 실행결과
<http.client.HTTPResponse object at 0x000001C41977F708>
(3) 가져온 데이터 파싱하기
soup = BeautifulSoup(res, 'html.parser')
print(soup)
# 실행결과
파싱된 HTML 전체 출력
(4) 필요한 데이터 추출하기 --- title 출력
title = soup.select_one('header > title')
print(title.text)
# 실행결과
전국 육상중기예보
(5) 도시 및 시간대별 날씨 추출하기 --- city, tmef, wf 출력
for loc in soup.select('location'):
city = loc.select_one('city')
days = loc.select('data tmef')
wfs = loc.select('data wf')
print(city.text)
for days, wfs in zip(days, wfs):
print(days.text, wfs.text)
# 실행결과
서울
2021-01-29 12:00 / 맑음
2021-01-30 00:00 / 맑음
....
원주
2021-01-29 12:00 / 맑음
2021-01-30 00:00 / 구름많음
....
< 전체 스크립트 >
from bs4 import BeautifulSoup
from urllib import request as req
# 1. 데이타 가져오기
# http://www.kma.go.kr > [생활과산업] > [서비스] > [인터넷] > RSS
rssUrl = 'http://www.kma.go.kr/weather/forecast/mid-term-rss3.jsp'
res = req.urlopen(rssUrl)
# print(res) # [결과] http.client.HTTPResponse object
# 2. 필요한 데이터 추출하기
soup = BeautifulSoup(res, 'html.parser')
# print(soup) # 파싱 결과확인
# 제목 / 도시 / 시간대별 날씨상태등 추출하여 출력
# (1) 제목
title = soup.select_one('header > title')
# print(title.text) # [결과] 전국 육상중기예보
# (2) 도시
for loc in soup.select('location'):
city = loc.select_one('city')
days = loc.select('data tmef')
wfs = loc.select('data wf')
print(city.text)
for days, wfs in zip(days, wfs):
print(days.text, wfs.text)
3. 각 구청의 상세 정보를 찾아서 저장하고 출력하기
BeautifulSoup : 파서를 이용해서 html 의 요소를 추출하게 해주는 모듈 - 단점은 개행문자를 고려해야 한다 Tag : 태그 NavigableString : 원래는 tag 사이의 문자열 # xpath를 사용하면 개행문자를 고려하지 않아도 된단다 # https://developer.mozilla.org/ko/docs/XPath |
(1) 필요한 라이브러리 임포트
from urllib import request
from bs4 import BeautifulSoup
(2) 링크로부터 HTML 을 가져오는지 확인
html = 'http://www.seoul.go.kr/seoul/autonomy.do'
site = request.urlopen(html)
site1=site.read()
print(site1)
# 실행결과
b'\r\n\r\n\r\n\r\n\r\n<!DOCTYPE html>\r\n<html lang="ko">\r\n\r\n<head>\...........
(3) 읽어온 HTML 을 파싱
soup = BeautifulSoup(site1,"html.parser")
(4) 필요한 요소를 추출하여 리스트에 저장
구청이름 = [name.text for name in soup.select('div.call-list li strong')]
구청주소 = [addr.text for addr in soup.select('div.call-list li ul li:nth-of-type(1)')]
구청전화번호 = [tel.text for tel in soup.select('div.call-list li ul li:nth-of-type(2)')]
구청홈페이지 = [home.attrs['href'] for home in soup.select('div.call-list li ul li:nth-of-type(3) a')]
(5) 지역별로 출력
for a,b,c,d in zip(구청이름, 구청주소, 구청전화번호, 구청홈페이지):
print('{} {} {} {}'.format(a,b,c,d))
# 실행결과
종로구 (우) 03153 / 서울시 종로구 삼봉로43 (수송동) TEL. 02-2148-1114 http://www.jongno.go.kr
중구 (우) 04558 / 서울시 중구 창경궁로 17 (예관동) TEL. 02-3396-4114 http://www.junggu.seoul.kr/
용산구 (우) 04390 / 서울시 용산구 녹사평대로150 (이태원동) TEL. 02-2199-6114 http://www.yongsan.go.kr
....
< 전체 스크립트 >
from urllib import request
from bs4 import BeautifulSoup
html = 'http://www.seoul.go.kr/seoul/autonomy.do'
site = request.urlopen(html)
site1=site.read()
# print(site1)
soup = BeautifulSoup(site1,"html.parser")
# 아래 리스트에 각 구청의 상세 정보를 저장하고 출력하기
구청이름 = [name.text for name in soup.select('div.call-list li strong')]
구청주소 = [addr.text for addr in soup.select('div.call-list li ul li:nth-of-type(1)')]
구청전화번호 = [tel.text for tel in soup.select('div.call-list li ul li:nth-of-type(2)')]
구청홈페이지 = [home.attrs['href'] for home in soup.select('div.call-list li ul li:nth-of-type(3) a')]
for a,b,c,d in zip(구청이름, 구청주소, 구청전화번호, 구청홈페이지):
print('{} {} {} {}'.format(a,b,c,d))
4. HTML 내부에 있는 링크 URL 추출하기
: 페이지 내에서 <a> 의 속성으로 들어있는 URL 을 모두 가져오기
(0) 사용할 라이브러리 패키지를 불러온다.
from bs4 import BeautifulSoup
from urllib import parse
from urllib import request
(1) 웹 페이지 주소를 변수에 담아 urlopen 이 되는지 먼저 확인한다.
response 의 결과가 객체로 출력되고, read( ) 의 결과가 바이너리 문자열로 출력된다.
url = 'https://docs.python.org/3.7/library/'
response = request.urlopen(url) # urllib.request.urlopen() : BeautifulSoup을 통해 html 파서할(데이타를 가져올) 대상
print(response)
print(response.read())
# 실행결과
<http.client.HTTPResponse object at 0x00000214529334C8>
b'\n<!DOCTYPE html>\n\n<html xmlns="http://www.w3.org/1999/xhtml">\n .......
(2) enum_links( ) 라는 함수의 인자로 응답결과(HTML)와 웹페이지의 주소(url)를 넘긴다.
result = enum_links(response, url)
(3) enum_links( ) 함수를 사용하여 웹 페이지 내 파란 글씨로 표시되는 하이퍼링크를 리스트에 담아 리턴한다.
① enum_links( ) 는 urlopen( )결과인 HTML과 웹페이지 주소를 인자로 받는다.
def enum_links(html,base):
② HTML을 파싱한다.
def enum_links(html,base):
soup = BeautifulSoup(html, 'html.parser')
③ 하이퍼링크가 존재하는 <a>만 추출한다.
def enum_links(html,base):
soup = BeautifulSoup(html, 'html.parser')
links = soup.select('a')
④ 최종 결과를 담을 빈 리스트를 생성한다.
def enum_links(html,base):
soup = BeautifulSoup(html, 'html.parser')
links = soup.select('a')
result = []
⑤ <a> 태그가 들어있는 리스트 links 로부터 href 속성값만 추출한다.
result = []
for a in links:
href = a.attrs['href']
print(href)
# 실행결과
../genindex.html
../py-modindex.html
intro.html
....
⑥ href 는 링크가 완전하지 않기 때문에 URL 과 join 하여 완전한 링크로 만든다.
urljoin( ) 함수에서 href의 경로에 따라 링크를 만들어준다.
def enum_links(html,base):
....
href = a.attrs['href']
url = parse.urljoin(base,href)
print(url)
# 실행결과
https://docs.python.org/3.7/genindex.html
https://docs.python.org/3.7/py-modindex.html
https://docs.python.org/3.7/library/intro.html
....
⑦ 완성된 url 을 리스트에 담아 리턴한다.
def enum_links(html,base):
....
for a in links:
href = a.attrs['href']
url = parse.urljoin(base,href)
result.append(url)
return result
(4) 완성된 함수로부터 링크를 가져온다.
result = enum_links(response, url)
print(result)
# 실행결과
['https://docs.python.org/3.7/genindex.html', 'https://docs.python.org/3.7/py-modindex.html', .........
< 전체 스크립트 >
from bs4 import BeautifulSoup
from urllib import parse
from urllib import request
def enum_links(html,base):
soup = BeautifulSoup(html, 'html.parser')
links = soup.select('a')
result = []
for a in links:
href = a.attrs['href']
url = parse.urljoin(base,href)
result.append(url)
return result
#-------------------------------------------
if __name__ == '__main__':
url = 'https://docs.python.org/3.7/library/'
response = request.urlopen(url)
result = enum_links(response, url)
5. HTML 내부의 파일을 다운 받기
(1) 필요한 라이브러리 임포트
from bs4 import BeautifulSoup
from urllib import parse
from urllib import request
import os, time, re
(2) 다운받을 URL 을 download_file( ) 함수의 인자로 넣어 호출한다.
url = 'https://docs.python.org/3.6/library/'
result = download_file(url)
(3) download_file( ) 함수를 작성한다.
① 인자로 받아온 url 의 HTML 을 파싱한다.
def download_file(url):
p = parse.urlparse(url)
② urllib라이브러리의 parse 모듈로부터
netloc( ) 함수를 사용하여 hostname 을, path( ) 함수를 사용하여 경로를 가져온다.
hostname과 path 를 사용하여 현재 폴더에 생성할 새 저장 경로를 만든다.
def download_file(url):
p = parse.urlparse(url)
savepath = './' + p.netloc + p.path
print(savepath)
# 실행결과
./docs.python.org/3.6/library/
③ 저장경로가 / 로 끝날 경우 index.html 을 덧붙임으로써,
파일을 다운받을 경로가 완성되게끔 한다.
def download_file(url):
p = parse.urlparse(url)
savepath = './' + p.netloc + p.path
if re.search('/$',savepath):
savepath += 'index.html'
print(savepath)
# 실행결과
./docs.python.org/3.6/library/index.html
④ 다운받은 파일이 저장될 폴더가 없을 경우 생성한다.
def download_file(url):
....
savedir = os.path.dirname(savepath)
if not os.path.exists(savedir):
os.makedirs(savedir)
⑤ 다운받고자 하는 파일이 이미 존재할 경우, 함수를 더 이상 수행하지 않고 종료하도록 조건문을 작성한다.
def download_file(url):
....
if os.path.exists(savepath):
return savepath
⑥ urllib 라이브러리의 request 모듈로부터
urlretrieve( ) 함수를 사용하여 웹페이지로부터 경로명에 해당하는 파일 savepath(현재페이지)를 다운받는다.
다운받는 시간이 다소 소요될 수 있으므로 2초간 sleep 시킨다.
def download_file(url):
....
try:
print('download = ', url)
request.urlretrieve(url, savepath)
time.sleep(2)
return savepath
except:
print('fail download...', url)
return None
# 실행결과
download = https://docs.python.org/3.6/library/
< 전체 스크립트 >
from bs4 import BeautifulSoup
from urllib import parse
from urllib import request
import os, time, re # re : 정규식
def download_file(url):
p = parse.urlparse(url)
savepath = './' + p.netloc + p.path
if re.search('/$',savepath):
savepath += 'index.html'
# 다운받을 폴더 생성
savedir = os.path.dirname(savepath)
if not os.path.exists(savedir):
os.makedirs(savedir) # 하위경로까지 생기게 함
# 이미 파일을 다운받았을 경우 함수 종료
if os.path.exists(savepath):
return savepath
# 다운받기
try:
print('download = ', url)
request.urlretrieve(url, savepath)
time.sleep(2)
return savepath
except:
print('fail download...', url)
return None
if __name__ == '__main__':
url = 'https://docs.python.org/3.6/library/'
result = download_file(url)
print(result)
6. 링크를 추출하는 함수 enum_links 와 현재페이지를 다운받는 함수 download_file 을 사용하여
현재 페이지로부터 모든 하이퍼링크를 타고 들어가 페이지 다운받기 (재귀 호출)
(1) 필요한 라이브러리를 임포트
from bs4 import BeautifulSoup
from urllib.parse import *
from urllib.request import *
import os, time, re
(2) 함수가 작성된 py 파일 임포트
import Ex07_alldown1
import Ex07_alldown2
(3) 시작점이 될 페이지의 루트 경로를 analyze_html( ) 함수의 인자로 넣는다.
url = "https://docs.python.org/3.5/library/"
analyze_html(url, url)
(4) 이미 다운받은 파일인지 확인하기 위한 변수 proc_file 을 생성
proc_files = {}
(5) index.html 을 제외한 웹페이지 루트 경로를 인자로 받아,
HTML을 분석하고 다운받을 함수 analyze_html( ) 을 작성한다.
proc_files = {}
def analyze_html(url, root_url):
① download_file( ) 함수를 호출하여 url 에 해당하는 웹 페이지를 파일로 다운받고,
savepath 변수에는 index.html 이 포함된 경로가 지정된다.
웹 페이지 다운로드가 실패했을 경우에는 None 이 리턴되므로 함수를 종료하게끔 작성한다.
def analyze_html(url, root_url):
savepath = Ex07_alldown2.download_file(url)
if savepath is None: return
② 이미 처리한 파일인지 확인할 수 있는 조건문을 작성한다.
현재 페이지를 다운받았을 때에는 proc_files 에 해당 데이터가 들어있지 않기 때문에
리턴 없이 그대로 통과하여 함수를 계속 수행할 수 있다.
def analyze_html(url, root_url):
savepath = Ex07_alldown2.download_file(url)
if savepath is None: return
if savepath in proc_files: return
③ 현재 페이지가 key 이고, 값이 True 인 데이터를 proc_files 에 추가한다.
proc_files = {}
def analyze_html(url, root_url):
savepath = Ex07_alldown2.download_file(url)
if savepath is None: return
if savepath in proc_files: return
print(proc_files) # 비교1
proc_files[savepath] = True
print(proc_files) # 비교2
# 실행결과
{}
{'./docs.python.org/3.5/library/index.html': True}
④ 현재 페이지가 저장되는 파일을 UTF-8 로 인코딩하여 열고, 읽는다. (한글깨짐 방지)
def analyze_html(url, root_url):
savepath = Ex07_alldown2.download_file(url)
....
f = open(savepath, 'r', encoding='utf-8')
html = f.read()
⑤ 읽어온 파일로부터 루트 경로에 부합하는 페이지 내 모든 링크를 추출하여 리스트 links 에 담는다.
( 현재 루트 경로 url = "https://docs.python.org/3.5/library/" )
def analyze_html(url, root_url):
savepath = Ex07_alldown2.download_file(url)
....
f = open(savepath, 'r', encoding='utf-8')
html = f.read()
links = Ex07_alldown1.enum_links(html, url)
⑥ 리스트의 url 을 하나씩 추출했을 때 루트경로가 다를 경우에는
아무 일도 하지 않고 넘어가는 조건문을 작성한다.
def analyze_html(url, root_url):
savepath = Ex07_alldown2.download_file(url)
....
f = open(savepath, 'r', encoding='utf-8')
html = f.read()
links = Ex07_alldown1.enum_links(html, url)
for link_url in links:
if link_url.find(root_url) != 0 :
continue
⑦ 리스트에서 가져온 경로가 .html 로 끝날 경우
해당 경로를 인자로 하여 analyze_html( ) 함수를 호출하도록 작성한다.
→ 재귀 호출
def analyze_html(url, root_url):
savepath = Ex07_alldown2.download_file(url)
....
for link_url in links:
if link_url.find(root_url) != 0 :
continue
if re.search('.html', link_url):
analyze_html(link_url, root_url)
continue
# 실행결과
download = https://docs.python.org/3.5/library/lzma.html
download = https://docs.python.org/3.5/library/zipfile.html
download = https://docs.python.org/3.5/library/tarfile.html
....
< 전체 스크립트 >
from bs4 import BeautifulSoup
from urllib.parse import *
from urllib.request import *
import os, time, re
import Ex07_alldown1
import Ex07_alldown2
# 이미 처리한 파일인지 확인하기 위한 변수
proc_files = {} # 셋, 딕셔너리
# HTML을 분석하고 다운받는 함수
def analyze_html(url, root_url):
# ------------------------------------------------------
# 해당 웹 페이지 다운로드
savepath = Ex07_alldown2.download_file(url)
if savepath is None: return
# 이미 처리한 파일인지 확인하기
if savepath in proc_files: return
proc_files[savepath] = True
'''
{
'https://docs.python.org/3.5/library/shutil.html':True,
'https://docs.python.org/3.5/library/glob.html':True
}
'''
# 링크 추출
f = open(savepath, 'r', encoding='utf-8')
html = f.read()
links = Ex07_alldown1.enum_links(html, url)
# print(links)
for link_url in links:
# 링크의 루트 경로(https://docs.python.org) 외의 경로를 무시
if link_url.find(root_url) != 0 :
continue
# 경로가 .html 로 끝나면
if re.search('.html', link_url):
analyze_html(link_url, root_url)
continue
#-------------------------------------------------------------
if __name__ == "__main__":
# URL에 있는 모든 것 다운받기
url = "https://docs.python.org/3.5/library/"
analyze_html(url, url)
7. 네이버 영화에서 평점 및 감상평 스크래핑 하기
from urllib import request
from bs4 import BeautifulSoup
site = request.urlopen('https://movie.naver.com/movie/bi/mi/basic.nhn?code=134963')
soup = BeautifulSoup(site, 'html.parser')
# 영화제목
title = soup.select_one('h3 > a')
score = soup.select('div.score_result div.star_score > em')
name = soup.select('div.score_reple a > span')
reple = soup.select('div.score_reple > p')
print('영화제목 : {}\n'.format(title.text))
for s, n, r in zip(score, name, reple):
print('평점 : {} | 작성자 : {}\n감상평 : {}\n'.format(s.text, n.text, r.text.strip()))
# 실행결과
영화제목 : 라라랜드
평점 : 9 | 작성자 : wche****
감상평 : '그래도 우리 잘 해냈지?'라고 말하는 듯한 마지막 눈빛교환이 잊혀지질 않는다
....
8. 다음 영화에서 평점 및 감상평 스크래핑 하기
from urllib import request
from bs4 import BeautifulSoup
from selenium import webdriver
driver = webdriver.Chrome('../4_selenium_class/webdriver/chromedriver')
driver.implicitly_wait(3)
driver.get('https://movie.daum.net/moviedb/main?movieId=136715')
comments = driver.find_element_by_class_name('list_comment').get_attribute('innerHTML')
soup = BeautifulSoup(comments, 'html.parser')
review = soup.select('p')
score = soup.select('.ratings')
for s, r in zip(score, review):
print('{}점\n{}'.format(s.text, r.text.replace('\n',' ')))
# 실행결과
7점
당신의 삶이 목적이 아닌 과정에 놓여 있기를
8점
가보지도 않은 목적지에 도달하기 위한 집착과
....