goodthings4me.tistory.com
[출처] 파이썬 코딩 무료 강의 (활용편3) - 웹 크롤링? 웹 스크래핑! 제가 가진 모든 비법을 알려드리겠습니다. [나도코딩] https://youtu.be/yQ20jZwDjTE
"파이썬 기본편을 학습한 분들을 위한 파이썬 웹 크롤링 - 스크래핑 무료 강의"
본 포스팅은 상기 유튜브 영상을 보면서 연습한 코드임
다른 코드 연습 중 관련 함수 등이 필요할 경우 참고하기 위해 올려놓음
■ 웹 스크래핑 requests 응답
import requests
# 웹 스크래핑 requests 응답
def requests_():
response = requests.get('http://naver.com')
print('응답코드 :', response.status_code)
response = requests.get('http://nadocoding.tistory.com')
response.raise_for_status()
print('응답코드 :', response.status_code)
# response 에러 발생한다고 가정 시 response.raise_for_status() 에러 코드 보이고 프로그램 종료시킴
if response.status_code == requests.codes.ok: # 200
print('정상')
requests_()
[결과]
응답코드 : 200
응답코드 : 200
정상
■ 스크래핑 데이터 저장해보기
import requests
# 스크래핑 데이터 저장해보기
def mygoogle_html():
response = requests.get('http://gogle.com')
response.raise_for_status()
# 현재 디렉토리에 mygoogle.html로 저장
with open('./mygoogle.html', 'w', encoding='utf8') as f:
f.write(response.text)
mygoogle_html()
■ 정규표현식 연습
import re
# 정규표현식 연습
def re_(pattern, args):
# 어떤 (정규)식을 컴파일 할 지 정해주기 re.compile('원하는 형태')
# .은 하나의 문자, ^는 문자열의 시작, $는 문자열의 끝
p = re.compile(pattern)
# match()는 주어진 문자열의 처음부터 일치하는지 확인
m = p.match(args)
if m: # 매치 되었을 경우
print(f'args: {args}')
print(f'm.group(): {m.group()}') # 일치하는 문자열 반환
print(f'm.string: {m.string}') # 입력받은 문자열 그대로 출력
print(f'm.start: {m.start()}') # 일치하는 문자열의 시작 index
print(f'm.end: {m.end()}') # 일치하는 문자열의 끝 index
print(f'm.span: {m.span()}') # 일치하는 문자열의 시작/끝 index
else:
print(f'match: {args} 매치 안됨')
# search()는 주어진 문자열 중에 일치하는게 있는지 확인
s = p.search(args)
if s:
print(f'search: {s.group()}')
else:
print(f'search: {args} 매치X')
# findall()는 일치하는 모든 것을 리스트 형태로 반롼
lst = p.findall(args)
print(f'findall(): {lst}')
print()
lst = ['case', 'caffe', 'care', 'careless', 'icare', 'good care cafe']
for ls in lst:
re_('ca.e', ls)
[결과]
args: case
m.group(): case
m.string: case
m.start: 0
m.end: 4
m.span: (0, 4)
search: case
findall(): ['case']
match: caffe 매치 안됨
search: caffe 매치X
findall(): []
args: care
m.group(): care
m.string: care
m.start: 0
m.end: 4
m.span: (0, 4)
search: care
findall(): ['care']
args: careless
m.group(): care
m.string: careless
m.start: 0
m.end: 4
m.span: (0, 4)
search: care
findall(): ['care']
match: icare 매치 안됨
search: care
findall(): ['care']
match: good care cafe 매치 안됨
search: care
findall(): ['care', 'cafe']
■ requests 응답이 없을 때, 추가로 headers 정보 전송하여 응답 오류 해결하기
import requests
# requests 응답이 없을 때, 추가로 headers 정보 전송하여 응답 오류 해결하기
# User- Agent 확인 : https://www.whatismybrowser.com/detect/what-is-my-user-agent
def nadocoding():
url = 'http://nadocoding.tistory.com'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'
}
response = requests.get(url, headers=headers)
response.raise_for_status()
with open('./nadocoding.html', 'w', encoding='utf8') as f:
f.write(response.text)
print('파일 저장 완료')
nadocoding()
■ BeautifulSoup 활용, 스크래핑 함수들
import requests
from bs4 import BeautifulSoup
# BeautifulSoup 활용, 스크래핑 함수들
# find, next_sibling, previous_sibling, find_next_sibling, find_next_siblings
def bs4_use():
url = 'https://comic.naver.com/webtoon/weekday.nhn'
response = requests.get(url)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'lxml') # html.parser
print(soup.title)
print(soup.title.get_text())
print(soup.a) # soup 객체에서 첫번째 발견되는 a element 반환
print(soup.a.attrs) # dict, a element의 속성 정보 출력
print(soup.a['href'])
print(soup.find('a', attrs={'class': 'Nbtn_upload'}))
print(soup.find('a', {'class':'Nbtn_upload'}))
print(soup.find('a', class_ = 'Nbtn_upload'))
print(soup.find(attrs={'class':'Nbtn_upload'}))
# Nbtn_upload 속성이 하나이기 때문에 태그 지정 없이 가능
print(soup.find('li', class_='rank01')) # 인기급상승 1위
rank01 = soup.find('li', {'class':'rank01'})
print(rank01.a.get_text())
## li 태그가 여러 개, 형제 엘리먼트를 호출하기
print(rank01.next_sibling) # 아무것도 안나올 경우, 태그 간에 개행 정보가 있기 때문...
rank02 = rank01.next_sibling.next_sibling # next_sibling 한 번 더
print(rank02.get_text())
rank03 = rank02.next_sibling.next_sibling
print(rank03.get_text())
print(rank03.previous_sibling.previous_sibling)
## next_sibling 2번 안쓸 수 있게
rank2 = rank01.find_next_sibling('li') # find_next_sibling()
print(rank2.a.text)
## 부모 태그로 가기
print(rank01.parent)
## 형제들 가져오기
ranks = rank01.find_next_siblings('li') # find_next_siblings()
print(rank01.a.text)
for rank in ranks:
print(rank.a.text)
'''
싸움독학-85화 : 싸움보다 어렵냐
이번 생도 잘 부탁해-50화
투신전생기-1화
입학용병-34화
약한영웅-145화
맘마미안-100화
곱게 키웠더니, 짐승-32화
나만 보여!-26화
열렙전사-2부 101화 - 마지막 거짓말
소녀재판-64화
'''
## 'text=' 로 찾아 가져오기
webtoon = soup.find('a', text='노답소녀-33화')
bs4_use()
■ 네이버 웹툰 전체 제목 가져오기
import requests
from bs4 import BeautifulSoup
# 네이버 웹툰 전체 제목 가져옴 - fina_all()
def naver_webtoon():
url = 'https://comic.naver.com/webtoon/weekday.nhn'
response = requests.get(url)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'lxml') # html.parser
cartoons = soup.find_all('a', attrs={'class':'title'})
print(type(cartoons)) # <class 'bs4.element.ResultSet'>
for cartoon in cartoons:
print(cartoon.get_text())
naver_webtoon()
[결과]
<class 'bs4.element.ResultSet'>
신의 탑
참교육
뷰티풀 군바리
파이게임
윈드브레이커
신입일기
소녀의 세계
장씨세가 호위무사
삼매경
앵무살수
백수세끼
만렙돌파
요리GO
칼가는 소녀
...
- 이후 생략 -
■ 가우스전자 만화 제목, 링크 가져오기
import requests
from bs4 import BeautifulSoup
# 가우스전자 만화 제목, 링크 가져오기
def naver_webtoon():
url = 'https://comic.naver.com/webtoon/list.nhn?titleId=675554'
response = requests.get(url)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'lxml') # html.parser
cartoons = soup.find_all('td', class_='title')
# title = cartoons[0].a.get_text()
# print(title)
for cartoon in cartoons:
title = cartoon.a.get_text()
link = 'https://comic.naver.com' + cartoon.a['href']
print(title, link)
total_rate = 0
cartoon_ratings = soup.find_all('div', attrs={'class':'rating_type'})
for cartoon in cartoon_ratings:
rate = cartoon.find('strong').get_text()
print(rate)
total_rate += float(rate)
print(f'총점: {total_rate:.2f}\n평균: {(total_rate / len(cartoon_ratings)):.2f}') # f'{:.2f}'
naver_webtoon()
[결과]
후기 + 10년 후 가우스 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=911&weekday=mon
시즌4 430화 내일 봐요 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=910&weekday=mon
시즌4 429화 잠행 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=909&weekday=mon
시즌4 428화 추억 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=908&weekday=mon
시즌4 427화 섬세한사람 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=907&weekday=mon
시즌4 426화 적응 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=906&weekday=mon
시즌4 425화 대견 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=905&weekday=mon
시즌4 424화 초빙강사 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=904&weekday=mon
시즌4 423화 추억의 물건 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=903&weekday=mon
시즌4 422화 아니요 https://comic.naver.com/webtoon/detail.nhn?titleId=675554&no=902&weekday=mon
9.98
9.98
9.97
9.97
9.97
9.98
9.97
9.97
9.97
9.97
총점: 99.73
평균: 9.97
■ 쿠팡(Coupang)에서 노트북 검색결과
import requests
from bs4 import BeautifulSoup
import re
# 쿠팡에서 노트북 검색결과
# https://www.coupang.com/np/search?component=&q=노트북&channel=user
def coupang_search():
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'
}
for i in range(1, 3):
coupang = 'https://www.coupang.com'
url = 'https://www.coupang.com/np/search?q=%EB%85%B8%ED%8A%B8%EB%B6%81&channel=user&component=&eventCategory=SRP&trcid=&traid=&sorter=scoreDesc&minPrice=&maxPrice=&priceRange=&filterType=&listSize=36&filter=&isPriceRange=false&brand=&offerCondition=&rating=0&page={}&rocketAll=false&searchIndexingToken=&backgroundColor='.format(i)
response = requests.get(url, headers=headers)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser') # 'lxml' or 'html.parser'
# <li class='search-product search-product__ad-badge" 이지만, '광고' 글자 없으면 search-product 만 있음
# 이럴 경우 스크래핑이 안되는 경우가 있는데, 이를 해결하는 방법으로
# 정규식 re 모듈 사용하여 class 속성 전체를 지정하게 함. 시작을 search-product로...
items = soup.find_all('li', attrs={'class':re.compile('^search-product')})
print(items[0].find('div', {'class':'name'}))
for item in items:
# 광고(아이콘) 상품 제외하기
ad_badge = item.find('span', attrs={'class':'ad-badge-text'})
if ad_badge:
print('###<광고 상품> - 제외\n')
continue
name = item.find('div', {'class':'name'}).get_text()
# Apple 상품 제외하기
if [n for n in ('Apple', '애플', 'apple') if n in name]:
print('###<애플 상품> - 제외\n')
continue
link = item.find('a', class_='search-product-link')['href']
price = item.find('strong', {'class':'price-value'}).get_text()
rate = item.find('em', class_='rating')
if rate:
rate = rate.get_text()
else:
rete = '평점없음'
rate_cnt = item.find('span', attrs={'class':'rating-total-count'})
if rate_cnt:
rate_cnt = rate_cnt.get_text()[1:-1] # 숫자만 슬라이싱
else:
rate_cnt = '평점수없음'
print(f'{i}page > 상품명: {name}\n바로가기: {coupang + link}\n가격: {price}\n평점: {rate}\n평점수: {rate_cnt}\n')
coupang_search()
[결과]
<div class="name">에이수스 노트북 실버 S513EA-CP129 (i5-1135G7 Iris), SSD 256GB, 윈도우 미포함, 8GB</div>
###<광고 상품> - 제외
1page > 상품명: 델 게이밍 노트북 Phantom Grey with speckles G15 5515-DG5515-WH01DKR (라이젠7-5800H 39.6cm Win10 Home RTX3060), 윈도우 포함, NVMe 512GB, 16GB
바로가기: https://www.coupang.com/vp/products/5619336514?itemId=9104260632&vendorItemId=76390529350
가격: 1,599,000
평점: None
평점수: 평점수없음
1page > 상품명: 삼성전자 플러스2 퓨어화이트 노트북 NT550XDA-K14AW (샐러론 6305 39.6cm WIN10 Pro Edu), 윈도우 포함, NVMe 128GB, 8GB
바로가기: https://www.coupang.com/vp/products/5244543934?itemId=7416308410&vendorItemId=74707281741
가격: 549,000
평점: 5.0
평점수: 166
- 이후 생략 -
■ 다음(Daum) 연도별 영화 순위 웹페이지에서 이미지 다운로드하기
import requests
from bs4 import BeautifulSoup
import re
import os
# 다음(Daum) 연도별 영화 순위 웹페이지에서 이미지 다운로드하기
def daum_movie(): # 2011~2020년영화순위
for year in range(2011, 2021):
# {year}년영화순위
url = f'https://search.daum.net/search?w=tot&q={year}%EB%85%84%EC%98%81%ED%99%94%EC%88%9C%EC%9C%84&DA=MOR&rtmaxcoll=MOR'
response = requests.get(url)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'lxml') # html.parser
movies = soup.find('ol', class_=re.compile('^type_plural')) # re.compile()
images = movies.find_all('img', attrs={'class':'thumb_img'})
for idx, image in enumerate(images, 1):
image_url = image['src']
# 코드 상의 url이 '//'으로 시작하는 경우, startswith()
if image_url.startswith('//'):
image_url = 'https:' + image_url
# print(image_url)
image_res = requests.get(image_url)
image_res.raise_for_status()
path = './' + str(year)
if not os.path.exists(path): # 저장할 디렉토리(폴더) 생성
os.mkdir(path)
with open(f'{path}/movie_{year}_{idx}.jpg', 'wb') as f:
f.write(image_res.content)
print(idx)
daum_movie()
[결과]
'프로그램 실행 디렉토리에 연도별 디렉토리 생성후 이미지가 다운로드 되어 있음'
■ 네이버 금융 - 국내 증시 시가총액 가져와서 .csv 파일로 저장하기
csv 모듈로 csv 파일 쓰기
- .csv 파일을 만들고 그 파일을 쓰기 모드로 오픈한 후 csv 파일 객체(csv.writer())에 넣는다.
- 파일 객체는 writerow() 메서드로 list 데이터를 한 라인 추가 (for 문 사용 시 list 내 모든 요소 추가)
- Windows의 경우 csv모듈에서 데이터를 쓸 때 각 라인 뒤에 빈 라인이 추가됨 (open()에 newline= 옵션 지정하면 됨)
f = open('csv 파일명', 'w', encoding='utf-8', newline='')
writer = csv.writer(f)
writer.writerow(리스트)
f.close()
csv 모듈로 csv 파일 읽기
- .csv 파일을 읽기 모드로 오픈한 후 csv 파일 객체(csv.reader())에 넣는다.
- 파일 객체는 Iterator 타입의 reader 객체 리턴(리스트 타입) (for 문으로 한 라인씩 가져올 수 있음)
f = open('csv 파일명', 'r', encoding='utf-8')
reader = csv.reader(f)
for line in reader:
print(line)
f.close()
import requests
from bs4 import BeautifulSoup
import csv
# 네이버 금융 - 국내 증시 시가총액 가져와서 .csv 파일로 저장하기
def naver_stock():
# url = 'https://finance.naver.com/sise/sise_market_sum.nhn'
filename = 'naver_stock_sise.csv'
# csv write 시 줄바꿈 1줄이 생김 --> newline= 주면 자동으로 줄바꿈이 생기지 않음
# 엑셀 내 한글이 깨질 경우 'utf-8' 대신 'utf-8-sig' 사용
f = open(filename, 'w', encoding='utf-8-sig', newline='')
writer = csv.writer(f) # csv 파일 객체
# 엑셀의 각 column 제목 만들기
title = 'N 종목명 현재가 전일비 등락률 액면가 시가총액 상장주식수 외국인비율 거래량 PER ROE'.split('\t')
writer.writerow(title)
for page in range(1, 2):
url = 'https://finance.naver.com/sise/sise_market_sum.nhn?&page=' + str(page)
response = requests.get(url)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'lxml') # html.parser
# table의 tr에 정보 있는 구조, table thead에도 tr 있음. tbody 얻어와서 tr을 대상으로 작업
data_rows = soup.find('table', attrs={'class':'type_2'}).find('tbody').find_all('tr')
for row in data_rows:
columns = row.find_all('td')
# 빈 list (개행 tr)와 list 내 \n\t 등이 있음
if len(columns) <= 1: # 의미없는 빈 list (개행 tr) skip
continue
data = [column.get_text().strip() for column in columns]
print(data)
# csv에 쓸 때 리스트 형태로 넘겨줌
# csv 파일객체 메서드 wreterow()로 데이터 쓰기
writer.writerow(data)
naver_stock()
[결과]
['1', '삼성전자', '81,600', '400', '+0.49%', '100', '4,871,343', '5,969,783', '53.61', '12,966,342', '19.59', '9.99', '']
['2', 'SK하이닉스', '128,500', '2,500', '+1.98%', '5,000', '935,483', '728,002', '49.53', '2,516,045', '18.35', '9.53', '']
-이후 생략-
'디렉토리 내에 엑셀 파일도 저장되어 있음'
■ 셀레니움(Selenium) 다루어보기
# 셀레니움(Selenium) 웹드라이버 다운로드
https://sites.google.com/a/chromium.org/chromedriver/downloads
# 크롬 버전 확인
Chrome://version
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
# 셀레니움(Selenium) 다루어보기
def selenium_use():
# 현재 경로인 경우 './chromedriver.exe' 필요 없음
browser = webdriver.Chrome('./chromedriver.exe')
browser.get('http://naver.com')
time.sleep(1)
elem = browser.find_element_by_id('query')
elem.send_keys('나도코딩')
elem.send_keys(Keys.ENTER)
time.sleep(2)
print(elem, type(elem))
# send_keys() 사용 시 생각해야 할 것!! ----------------------------
# elem = browser.find_element_by_id('query').send_keys('나도코딩').send_keys(Keys.ENTER)
# 또는
# elem = browser.find_element_by_id('query').send_keys('나도코딩')
# elem.send_keys(Keys.ENTER)
# 이 경우, send_keys() 반환값을 받은 elem 은 NoneType이 되고, 오류 발생함
# AttributeError: 'NoneType' object has no attribute 'send_keys' 발생
# print(elem) # None
# print(type(elem)) # <class 'NoneType'>
# webdriver WebElement 객체 타입인 elem에서 send_keys() 수행토록 함
# --------------------------------------------------------------
browser.back() # 뒤로
browser.find_element_by_id('query').send_keys('파이썬')
browser.find_element_by_class_name('ico_search_submit').click()
time.sleep(1.5)
browser.back()
browser.find_element_by_class_name('link_login').click()
time.sleep(1.5)
browser.back()
time.sleep(0.5)
browser.forward()
time.sleep(0.5)
browser.back()
browser.refresh() # 새로고침
browser.get('http://daum.net')
elem = browser.find_element_by_name('q')
elem.send_keys('나도코딩')
elem.send_keys(Keys.ENTER) # daum에서는 이상없이 수행됨
time.sleep(1)
browser.back()
elem = browser.find_element_by_name('q')
elem.send_keys('파이썬')
elem_btn = browser.find_element_by_xpath('//*[@id="daumSearch"]/fieldset/div/div/button[2]').click()
print(elem_btn, type(elem_btn))
browser.quit()
selenium_use()
■ 셀레니움(Selenium) 이용 네이버 자동 로그인
from selenium import webdriver
import time
# 셀레니움(Selenium) 이용 네이버 자동 로그인
def naver_login():
# 현재 경로인 경우 './chromedriver.exe' 필요 없음
browser = webdriver.Chrome('./chromedriver.exe')
browser.get('http://naver.com')
time.sleep(1)
browser.find_element_by_class_name('link_login').click()
time.sleep(0.5)
script = 'document.getElementById("id").value="{id}"; document.getElementById("pw").value="{pw}"'\
.format(id='ID', pw='PW')
time.sleep(0.5)
browser.execute_script(script)
time.sleep(0.5)
browser.find_element_by_id('log.login').click()
print(browser.page_source)
time.sleep(1)
browser.quit()
naver_login()
■ 셀레니움(Selenium) 네이버 항공권 예매 및 로딩 지연에 대한 처리
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 셀레니움(Selenium) 네이버 항공권 예매 및 로딩 지연에 대한 처리
# WebDriverWait( , ).until(EC.presence_of_element_located((By.XPATH, '')))
def naver_flight():
# 현재 경로인 경우 './chromedriver.exe' 필요 없음
browser = webdriver.Chrome('./chromedriver.exe')
browser.maximize_window()
browser.get('https://flight.naver.com/flights/')
time.sleep(1.5)
# 날짜 선택
browser.find_element_by_link_text('가는날 선택').click() # 텍스트로 선택
# # 캘린더 월이 2개 - 날짜도 2개
# browser.find_elements_by_link_text('29')[0].click() # 가는 날
# browser.find_elements_by_link_text('30')[0].click() # 오는 날
# # 다음 달이면,
# browser.find_elements_by_link_text('29')[1].click() # 가는 날
# browser.find_elements_by_link_text('30')[1].click() # 오는 날
# 달을 달리하면,
browser.find_elements_by_link_text('29')[0].click() # 가는 날
browser.find_elements_by_link_text('3')[1].click() # 오는 날
# 여행지 선택
browser.find_element_by_xpath('//*[@id="content"]/div/div[1]/ul/li[2]').click() # 국내 클릭
browser.find_element_by_xpath('//*[@id="recommendationList"]/ul/li[1]/div/dl').click() # 제주 클릭
# 항공권 검색 클릭
browser.find_element_by_link_text('항공권 검색').click()
# 어떤 엘리먼트(예로, XPATH 지정)가 나올 때까지 지정 시간만큼 기다리기 처리(인수 괄호에 주의!)
# 지정 시간(10초) 전이면 이후 진행, 시간 초과 시 에러 발생
# 이 부분은 예외처리하고, 오류 시 브라우저 종료하는 것이 좋음
try:
elem = WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.XPATH, \
'//*[@id="content"]/div[2]/div/div[4]/ul/li[1]')))
print(elem.text.split('\n'))
finally:
time.sleep(1.5)
browser.quit()
naver_flight()
[결과]
['제주항공', '출발지', 'GMP', '06:00', '도착지', 'CJU', '07:10', '총 소요시간', '01시간 10분', '할인석', '편도 37,100원', '편도 36,740원 (KB국민카드 결제시 1% 청구할인)', '성인이벤트혜택']
■ 동적 페이지 웹 스크래핑(예로, 사용자가 스크롤링 할 때 등) - 구글 무비
※ 아래 코드(requests 사용 결과)는 원하는 데이터를 가져올 수 없음
import requests
from bs4 import BeautifulSoup
# 구글 무비 인기 차트 영화에서 할인중인 영화 목록 추출해보기
# 구글 무비는 User-Agent(국가별)에 따라 서비스 페이지가 다르고, 스크롤 시 무비 데이터가 업데이트됨(동적페이지)
# headers 정보에 User-Agent와 Accept-Language 내용도 필요
def google_movie():
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
'Accept-Language': 'ko-KR,ko'
}
url = 'https://play.google.com/store/movies/top'
response = requests.get(url, headers=headers)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'lxml')
movies = soup.find_all('div', attrs={'class':'ImZGtf mpg5gc'})
print(len(movies)) # 0, headers 추가 후 10, 전체 영화 목록 안보임
# # 소스 검증 위해 파일 저장
# with open('movie.html', 'w', encoding='utf8') as f:
# # f.write(response.text) # 판독 불가
# f.write(soup.prettify()) # html 문서를 예쁘게 출력
for movie in movies:
title = movie.find('div', class_='WsMG1c nnK0zc').get_text()
print(title)
google_movie()
[결과]
10
노바디
콰이어트 플레이스 (자막판)
고질라 VS. 콩
라야와 마지막 드래곤
테넷
소울
킬러의 보디가드
킬러의 보디가드 무삭제 특별판 (자막판)
어벤져스 : 엔드게임 (자막판)
너의 이름은. (자막판)
■ google movie 에 대해 selenium 사용 (동적 페이지 웹 스크래핑)
from bs4 import BeautifulSoup
from selenium import webdriver
import time
# google movie(동적 페이지)에 대해 셀레니움으로 처리하기
def google_movie_selenium():
# 현재 경로인 경우 './chromedriver.exe' 필요 없음
browser = webdriver.Chrome('./chromedriver.exe')
browser.maximize_window()
url = 'https://play.google.com/store/movies/top'
browser.get(url)
time.sleep(1.5)
# 스크롤링 하기
# 모니터(해상도) 높이인 1080 위치로 스크롤 내리기 - 위치 숫자(해상도) 증가 시 그 위치만큼 이동
# browser.execute_script('widow.scrollTo(0, 1080)')
# browser.execute_script('widow.scrollTo(0, 2080)')
# 현재 보이는 화면 스크롤 끝(가장 아래)까지 스크롤 내리기
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
time.sleep(2)
# 구글 무비 사이트는 스크롤 시마다 영화 목록이 추가(업데이트)되며, 영화 목록 끝까지 스크롤을 내릴려면
# 현재 화면 높이를 가져와서 높이가 변하지 않을 때까지 스크롤을 내린다
# 현재 화면(문서) 높이를 가져와서 저장
prev_height = browser.execute_script('return document.body.scrollHeight')
print(prev_height)
while True:
# 페이지에서 스크롤을 가장 아래로 내림
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
time.sleep(2)
# 현재 페이지 높이 저장
curr_height = browser.execute_script('return document.body.scrollHeight')
if curr_height == prev_height:
break
prev_height = curr_height
print('스크롤 완료')
# selenium과 같이 사용할 경우, BeautifulSoup 객체에 전달할 소스코드는 webdrive의 page_source
soup = BeautifulSoup(browser.page_source, 'lxml')
# Tip! 스크래핑할 컨텐츠가 있는 태그와 속성이 하나가 아닌 경우, 리스트로 속성을 감싸준다. (여기서는 아님)
movies = soup.find_all('div', attrs={'class':'Vpfmgd'})
print(len(movies))
for movie in movies:
title = movie.find('div', class_='WsMG1c nnK0zc').get_text()
print(title)
# 할인된 영화만 추출해보기
print('#' * 50)
for movie in movies:
title = movie.find('div', class_='WsMG1c nnK0zc').get_text()
original_price = movie.find('span', class_='SUZt4c djCuy')
if original_price:
original_price = original_price.get_text()
else:
print(title, ' -- 할인되지 않은 영화 제외')
continue
# 할인된 가격
price = movie.find('span', attrs={'class':'VfPpfd ZdBevf i5DZme'}).get_text()
# 영화 링크
link = movie.find('a', attrs={'class':'JC71ub'})['href']
print(f'제목 : {title}')
print(f'할인 전 금액 : {original_price}')
print(f'할인 후 금액 : {price}')
print('링크 :', 'https://play.google.com' + link)
print('-' *50)
print('구글 인기차트 영화 리스트 추출 완료!')
browser.quit()
google_movie_selenium()
[결과]
노바디 -- 할인되지 않은 영화 제외
콰이어트 플레이스 (자막판) -- 할인되지 않은 영화 제외
고질라 VS. 콩 -- 할인되지 않은 영화 제외
라야와 마지막 드래곤 -- 할인되지 않은 영화 제외
테넷 -- 할인되지 않은 영화 제외
소울 -- 할인되지 않은 영화 제외
킬러의 보디가드 -- 할인되지 않은 영화 제외
제목 : 킬러의 보디가드 무삭제 특별판 (자막판)
할인 전 금액 : ₩2,500
할인 후 금액 : ₩1,200
링크 : https://play.google.com/store/movies/details/%ED%82%AC%EB%9F%AC%EC%9D%98_%EB%B3%B4%EB%94%94%EA%B0%80%EB%93%9C_%EB%AC%B4%EC%82%AD%EC%A0%9C_%ED%8A%B9%EB%B3%84%ED%8C%90_%EC%9E%90%EB%A7%89%ED%8C%90?id=ea3io4U1-qs
--------------------------------------------------
어벤져스 : 엔드게임 (자막판) -- 할인되지 않은 영화 제외
너의 이름은. (자막판) -- 할인되지 않은 영화 제외
몬스터 헌터 Monster Hunter -- 할인되지 않은 영화 제외
날씨의 아이 (자막) -- 할인되지 않은 영화 제외
잭 스나이더의 저스티스 리그 -- 할인되지 않은 영화 제외
베놈 Venom -- 할인되지 않은 영화 제외
어벤져스 : 인피니티 워 (자막판) -- 할인되지 않은 영화 제외
바람에 젖은 여자 -- 할인되지 않은 영화 제외
건마의 신:어린 아내의 알바 -- 할인되지 않은 영화 제외
모탈 컴뱃 -- 할인되지 않은 영화 제외
제목 : 미나리
할인 전 금액 : ₩7,700
할인 후 금액 : ₩5,500
링크 : https://play.google.com/store/movies/details/%EB%AF%B8%EB%82%98%EB%A6%AC?id=kOWoZydbl3o.P
-이하 생략-
■ 크롬 안띄우고 처리해보기(headless chrome : Chrome without Chrome)
from bs4 import BeautifulSoup
from selenium import webdriver
import time
# 동적 페이지에 대해 셀레니움으로 처리하기
# - 크롬 안띄우고 처리해보기(headless chrome : Chrome without Chrome)
def google_movie_headless():
## 크롬 띄우고 처리할 경우 아래를 주석 처리 -----------------------
options = webdriver.ChromeOptions()
options.headless = True # 크롬 안띄우기
options.add_argument('window-size=1920x1080') # 윈도우 창 크기 지정
browser = webdriver.Chrome('./chromedriver.exe', options=options)
# 여기까지 주석 처리 -------------------------------------------
# browser = webdriver.Chrome('./chromedriver.exe') # 크롬 띄울 경우 주석 처리 풀기
browser.maximize_window()
url = 'https://play.google.com/store/movies/top'
browser.get(url)
time.sleep(1.5)
prev_height = browser.execute_script('return document.body.scrollHeight')
while True:
# 페이지에서 스크롤을 가장 아래로 내림
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
time.sleep(2)
# 현재 페이지 높이 저장
curr_height = browser.execute_script('return document.body.scrollHeight')
if curr_height == prev_height:
break
prev_height = curr_height
print('스크롤 완료')
# selenium과 같이 사용할 경우, BeautifulSoup 객체에 전달할 소스코드는 webdrive의 page_source
soup = BeautifulSoup(browser.page_source, 'lxml')
movies = soup.find_all('div', attrs={'class':'Vpfmgd'})
print(len(movies))
# 할인된 영화만 추출해보기
for movie in movies:
title = movie.find('div', class_='WsMG1c nnK0zc').get_text()
original_price = movie.find('span', class_='SUZt4c djCuy')
if original_price:
original_price = original_price.get_text()
else:
print(title, ' -- 할인되지 않은 영화 제외')
continue
# 할인된 가격
price = movie.find('span', attrs={'class':'VfPpfd ZdBevf i5DZme'}).get_text()
# 영화 링크
link = movie.find('a', attrs={'class':'JC71ub'})['href']
print(f'제목 : {title}')
print(f'할인 전 금액 : {original_price}')
print(f'할인 후 금액 : {price}')
print('링크 :', 'https://play.google.com' + link)
print('-' *50)
print('구글 인기차트 영화 리스트 추출 완료!')
browser.quit()
google_movie_headless()
■ headlees Chrome 사용 시 주의할 점 - User-Agent 값 변경
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/91.0.4472.114 Safari/537.36
아래 User-Agent의 HeadlessChrome 부분이 바뀌었음
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
HeadlessChrome/91.0.4472.114 Safari/537.36
- 'User-Agent'가 'HeadlessChrome'으로 표시 --> 스크래핑 거부될 수 있음
이를 방지하기 위한 조치 - options.add_argument('user-agent=올바른 값 넣어주기')
from selenium import webdriver
import time
# options.add_argument('user-agent=올바른 값 넣어주기')
def chrome_headless():
options = webdriver.ChromeOptions()
options.headless = True
options.add_argument('window-size=1920x1080')
options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36')
browser = webdriver.Chrome('./chromedriver.exe', options=options)
browser.maximize_window()
url = 'https://www.whatismybrowser.com/detect/what-is-my-user-agent'
browser.get(url)
time.sleep(1)
detected_value = browser.find_element_by_id('detected_value')
print(detected_value.text)
browser.quit()
chrome_headless()
* 나도코딩 유튜브 채널 - https://www.youtube.com/channel/UC7iAOLiALt2rtMVAWWl4pnw/videos
'코딩 연습 > 코딩배우기' 카테고리의 다른 글
[Python] 파이썬을 활용한 업무자동화 - 엑셀 자동화(with openpyxl) 연습 코드 정리 (0) | 2021.07.01 |
---|---|
[Python] 파이썬 웹 크롤링 - 스크래핑 관련 연습 코드 [네이버 날씨 & 뉴스, 오늘의 영어지문 등 가져오기] (0) | 2021.06.29 |
[Python] 문자열 내 특수문자 제거 - replace(), isalnum(), join() 등 사용 (0) | 2021.06.23 |
[Python] 폴더(디렉토리) 만들기 - mkdir() or makedirs() (0) | 2021.06.22 |
파이썬 실천 기술 #06 - 내장함수와 특수메서드(스페셜 메서드) (0) | 2021.06.12 |
댓글