데이터 분석/웹 스크래핑

[Web Scrapping 실습] Crawling Test

eunnys 2023. 11. 3. 11:38

▶ select 태그 선택 테스트
 

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
from bs4 import BeautifulSoup
from selenium.webdriver.support.ui import Select

url = 'https://www.selenium.dev/selenium/web/formPage.html'
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())
driver.get(url)

select = Select(driver.find_element(By.NAME, 'selectomatic'))

# 인덱스를 기준으로 선택하기
# select.select_by_index(1)

# 보여지는 선택값 텍스트로 선택하기
# select.select_by_visible_text('Four')

# option 요소의 값으로 선택하기
# select.select_by_value('four')

# select 태그에 onchange 옵션이 있어서 Select 클래스를 사용할 수 없는 경우
driver.find_element(By.CSS_SELECTOR, 'option[value="four"]').click() # 직접 click 함수를 호출해야만 작동함

 
 
[실습] yes24에서 파이썬 도서 검색하기
- yes24 사이트에서 파이썬 도서 검색 후 평점 9.6 이상인 도서 제목과 가격, 평점 가져오기

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
from bs4 import BeautifulSoup
from selenium.webdriver.support.ui import Select

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())
driver.get('https://www.yes24.com')

# 검색어 입력
ele = driver.find_element(By.CSS_SELECTOR, '#query')
ele.send_keys('파이썬') 
ele.send_keys(Keys.ENTER)

# 검색어 화면 노출설정 변경
select = Select(driver.find_element(By.ID, 'stat_gb'))
driver.find_element(By.CSS_SELECTOR, 'option[value="120"]').click()

page = 1
while True:
    time.sleep(5)
    # 도서 별 info 영역 불러오기
    bs = BeautifulSoup(driver.page_source, 'lxml')
    books = bs.find('ul', attrs={'id':'yesSchList'})
    books = books.find_all('div', attrs={'class':'itemUnit'})
    
    # 별점 필터 걸기 (9.6이상)
    filtered_book = {}
    for book in books:
        book_name = book.find('a', attrs={'class':'gd_name'})
        rating = book.find('span', attrs={'class':'rating_grade'})
        try:
            rating = rating.find('em', attrs={'class':'yes_b'})
        except:
            continue
        if float(rating.text) >= 9.6:
            filtered_book[book_name.text] = rating.text
            print(book_name.text, rating.text)
    print(len(books), len(filtered_book))
    print(f'페이지 번호: {page}')
    print('END')

    page += 1
    try:
        time.sleep(5)
        driver.find_element(By.CSS_SELECTOR, f'#goodsListWrap > div.sGoodsPagen > div > a:nth-child({page})').click()
    except: break

 

driver.find_element(By.CSS_SELECTOR, '#goodsListWrap > div.sGoodsSecArea > div > span.baseFilter > a:nth-child(6)').click()
soup = BeautifulSoup(driver.page_source, 'lxml')

book_list = soup.find('ul', attrs={'id':'yesSchList'})
books = book_list.find_all('li')
# print(len(books)) # 130개

page = 1

while True:
    for book in books:
        title = book.find('a', attrs={'class':'gd_name'})
        # <em class='yes_b'> 태그가 가격과 평점에서 동일하기 때문에 상위 태그를 이용
        price = book.find('strong', attrs={'class':'txt_num'})
        rating = book.find('span', attrs={'class':'rating_grade'})
        if not rating: continue # 평점이 없는 도서는 pass
    
        rating = rating.find('em', attrs={'class':'yes_b'}).text # 범위를 좁혀서 평점 값만 가져옴
        if float(rating) < 9.6: continue
        print(f'{title.text} | {price.text} | {rating}')

    page += 1
    # 다음 페이지 클릭
    time.sleep(3)
    driver.find_element(By.CSS_SELECTOR, f'#goodsListWrap > div.sGoodsPagen > div > a:nth-child({page})').click() # 페이지 번호의 CSS 값

 


 
[실습] 다음 영화 사이트에서 박스오피스 1위 영화의 감상평 및 평점 수집
- 감상평을 수집하여 텍스트 파일로 저장하기
- 평점을 수집하여 csv 파일로 저장하기
      import csv
      with open('rating.csv', 'w') as f:
          writer = csv.writer(f):
          writer.writerow(ratings) # ratings : 평점을 모아놓은 리스트 객체

      * 스크래핑 순서
      Selenium : 다음 영화 접속 (movie.daum.net) -> '랭킹' 링크 클릭 -> '박스오피스' 링크 클릭 -> 1위 영화를 클릭 -> '평점' 클릭 -> 마지막 평점까지 스크롤
      BeautifulSoup : 사용자별 감상평 및 평점 수집 -> 수집된 정보들은 리스트에 저장 -> 파일 저장!
 

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
from bs4 import BeautifulSoup


# 페이지가 로드될 때까지 기다리는 시간
SCROLL_PAUSE_TIME = 1


driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://movie.daum.net')

driver.find_element(by=By.LINK_TEXT, value='랭킹').click()

driver.find_element(by=By.LINK_TEXT, value='박스오피스').click()

# 박스오피스 1위 영화 클릭
driver.find_element(By.CSS_SELECTOR, '#mainContent > div > div.box_boxoffice > ol > li:nth-child(1) > div > div.thumb_cont > strong > a').click()

# 평점 탭 클릭
time.sleep(SCROLL_PAUSE_TIME)
driver.find_element(by=By.LINK_TEXT, value='평점').click()

last_height = driver.execute_script('return document.body.scrollHeight')

# 스크롤 횟수
scroll_cnt = 0

while True:
    driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    scroll_cnt += 1
    time.sleep(SCROLL_PAUSE_TIME)
    new_height = driver.execute_script('return document.body.scrollHeight')
    print(f'last height:{last_height}, new height:{new_height}, scroll count:{scroll_cnt}')

    if last_height != new_height:
        last_height = new_height
    else:
        try:
            ele = driver.find_element(By.CSS_SELECTOR, '#alex-area > div > div > div > div.cmt_box > div.alex_more > button')
            ele.click()
            print('평점 더보기 클릭')
        except:
            break

 

soup = BeautifulSoup(driver.page_source, 'lxml')

ratings = []
comments =[]

ele = soup.find('ul', attrs={'class':'list_comment'}) # 전체 영역 선택
ele = ele.find_all('li') # 모든 유저에 대한 감상평 가져오기
for e in ele:
    rating = e.select_one('div > div').text # li 밑에 div 밑에 div에 있는 평점 가져오기
    ratings.append(int(rating)) # 정수로 바꿔줌
    # 감상평이 이모티콘으로 되어 있는(텍스트가 없는) 경우에 예외 발생
    try:
        comment = e.select_one('div > p').text # li 밑에 div 밑에 p에 있는 감상평 가져오기
    except:
        continue
    comment = comment.replace('\n', ' ') # 한줄의 형태로 변환해줌
    comments.append(comment)

print(f'네티즌 평점: {sum(ratings) / len(ratings):.1f}점')

import csv
import os

with open('ratings.csv', 'w') as f:
    writer = csv.writer(f)
    writer.writerow(ratings)
print('평점 저장 완료')

with open('comments.txt', 'a', encoding='utf-8') as f:
    for comment in comments:
        f.write(comment+'/n')
print('감상평 저장 완료')

 
 
iframe을 사용한 웹 페이지 스크래핑 하기
- iframe의 id 값을 찾는다
  - frame = driver.find_element(By.ID, 'entryIFrame')
- 해당 iframe으로 switch
  - driver.switch_to.frame(frame)
- 이 후 iframe내의 element를 검색하여 데이터를 스크래핑 한다
- iframe내의 스크래핑 작업이 끝난 루 기본 frame의 내용을 스크래핑 하기 위해 기본 frame으로 전환한다
  - driver.switch_to.default_content()