Day73

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점
가보지도 않은 목적지에 도달하기 위한 집착과 
....

 

반응형

'교육과정 > KOSMO' 카테고리의 다른 글

Day75  (0) 2021.01.28
Day74  (0) 2021.01.27
Day72  (0) 2021.01.25
Day71  (0) 2021.01.22
Day70  (0) 2021.01.21