-
[Python] 파이썬 웹 크롤링 - 스크래핑 관련 유튜브 강의[나도코딩] 연습 코드 정리코딩 연습/코딩배우기 2021. 6. 27. 14:42반응형
[출처] 파이썬 코딩 무료 강의 (활용편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://versionfrom 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