본문 바로가기
  • 모바일 정보
파이썬

파이선 셀러리움 웹크롤링 총정리

by 리뷰하는 김과장 2022. 12. 23.

 

 

 

파이썬으로 셀러리움 웹페이지  창 뛰우기 

 

options.add_argument('headless') 

이 옵션으로 창이 보이지 않게 된다.

 

options.add_argument("lang=ko_KR") 

이 옵션으로 언어를 설정할수가 있다.

기본언어를 다른언어로 설정하고싶을때 수정해서 사용하면 유용하다.

 

options.add_argument('--start-maximized')

이 옵션으로 창을 최대화 해서 뛰울수가 있다.

동적 웹페이지에서 보다 많은 데이터를 읽을수 있는 장점이 있다.

 

chrome_ver = chromedriver_autoinstaller.get_chrome_version().split('.')[0]
try:
    browser = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe',options=options)
except:
    chromedriver_autoinstaller.install(True)
    browser = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe',options=options)
browser.implicitly_wait(10)    

 

크롬드라이버가 자주 업데이트 되는데 그때마다 크롬드라이버를 새로 까는 것이 매우 귀찮을것이다. 이 코드로 깔끔하게 해결된다. 자동으로 크롬드라이버를 깔아서 실행해준다.

 

 

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
import chromedriver_autoinstaller

options = Options()
#headless모드 브라우저가 뜨지 않고 실행됩니다.
user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"  #크롬
options.add_argument('user-agent=' + user_agent)
options.add_argument('headless') 
options.add_argument("no-sandbox")
options.add_argument("lang=ko_KR")  
#창 최대화
options.add_argument('--start-maximized') #브라우저가 최대화된 상태로 실행됩니다.
options.add_argument("disable-gpu") ## 그래픽카드 기능을 제거함으로써 셀레니움 작동 속도를 높여줍니다.


chrome_ver = chromedriver_autoinstaller.get_chrome_version().split('.')[0]
try:
    browser = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe',options=options)
except:
    chromedriver_autoinstaller.install(True)
    browser = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe',options=options)
browser.implicitly_wait(10)                  

browser.get(url)

 

 

 

 

 

셀레니움 wait  두가지 개념익히기

 

from selenium import webdriver

driver = webdriver.Chrome('chromedriver.exe')
driver.implicitly_wait(10)

implicitly_wait 는 쉽게 설명하자면 페이지 이동등의 명령어를 줬을 때 다음 웹페이지가 넘어올때까지 기다리라는 뜻이다. 드라이버를 열고 나서 한번만 설정해주면 된다. 위 예제에서 괄호 안에 숫자의 의미는 10초동안 웹페이지가 로딩될때까지 기다리고 10초가 넘어가면 웹페이지가 로딩이 됐던 안됐던 다음 명령어를 실행하겠다는 것이다. 자신의 컴퓨터가 너무 느리다면 60초 즉 1분을 설정해도 상관없다.

 

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

driver = webdriver.Chrome('chromedriver.exe')
driver.get("https://pythondocs.net")

element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "idname")))
element = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'someid')))

WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "idname")))

 

WebDriverWait(driver, 10)  10초까지 해당 element 가 나올때 까지 기다려보겠다는 의미이다. 10초 전에 나오면 해당 element 값이 변수로 저장될것이다.

 

element = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'someid')))

이것도 위와 비슷하다. 해당 요소가 클릭할수 있을때 까지 10초간 기다린다.

 

위 두가지를 병행해서 사용해도 부족할때가 있을것이다. 그럴땐 적절히 time.sleep  를 사용해야 할것이다.

 

 

 

 

 

 

마우스 스크롤 하기

 

동적 페이지일경우 마우스를 스크롤하면 데이터가 더 나오는 경우가 많다. 이럴때 마우스를 스크롤하는 코드이다.

 

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
    print(prev_height)
    print(curr_height)
    prev_height=curr_height
time.sleep(2) 
print("스크롤 완료")

 

 

위 방법으로 마우스 스크롤이 안될때가 있다. 아이프레임 안에 스크롤 막대가 있다던지 아니면 새로운 창이 하나 뜬 상태일때 사용하면 좋다.

def scroll(modal):
    try:        
        # 스크롤 높이 받아오기
        last_height = driver.execute_script("return arguments[0].scrollHeight", modal)
        while True:
            pause_time = random.uniform(0.5, 0.8)
            # 최하단까지 스크롤
            driver.execute_script("arguments[0].scrollTo(0, arguments[0].scrollHeight);", modal)
            # 페이지 로딩 대기
            time.sleep(pause_time)
            # 무한 스크롤 동작을 위해 살짝 위로 스크롤
            driver.execute_script("arguments[0].scrollTo(0, arguments[0].scrollHeight-50);", modal)
            time.sleep(pause_time)
            # 스크롤 높이 새롭게 받아오기
            new_height = driver.execute_script("return arguments[0].scrollHeight", modal)
            try:
                # '더보기' 버튼 있을 경우 클릭
                all_review_button = driver.find_element_by_xpath('/html/body/div[1]/div[4]/c-wiz/div/div[2]/div/div/main/div/div[1]/div[2]/div[2]/div/span/span').click()
            except:
                # 스크롤 완료 경우
                if new_height == last_height:
                    print("스크롤 완료")
                    break
                last_height = new_height
                
    except Exception as e:
        print("에러 발생: ", e)
        
modal = WebDriverWait(driver, 2).until(EC.element_to_be_clickable((By.XPATH, "")))
scroll(modal)

 

 

 

 

 

특정 요소  찾기 클릭하기 내용입력하기 텍스트 가져오기

특정요소를 찾는 방법은 아래를 참고하자.

from selenium.webdriver.common.by import By

driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_element(By.XPATH, '//button')
driver.find_element(By.ID, 'loginForm')
driver.find_element(By.LINK_TEXT, 'Continue')
driver.find_element(By.PARTIAL_LINK_TEXT, 'Conti')
driver.find_element(By.NAME, 'username')
driver.find_element(By.TAG_NAME, 'h1')
driver.find_element(By.CLASS_NAME, 'content')
driver.find_element(By.CSS_SELECTOR, 'p.content')

 

특정요소 클릭하는 코드는 아래를 참고하자.

 

driver.find_element_by_xpath("//form[@class='ui form']/button").click()

 

위 방법으로 클릭이 되지 않는다면 아래 방법을 시도해보면 된다.

 

from selenium.webdriver.common.keys import Keys

driver.find_element_by_xpath("//form[@class='ui form']/button").send_keys(Keys.ENTER)

 

 

인풋박스에 내용입력하는 방법은 아래를 참고하자. 네이버 아이디와 비번을 입력하고 로그인 버튼을 누르는 예시다.

 

from selenium.webdriver.common.keys import Keys

browser.find_element_by_id('id').send_keys('naver_id')
browser.find_element_by_id('pw').send_keys('naver_password')
browser.find_element_by_id('log.login').click()

 

find_element 를 이용해서 간다히 텍스트를 추출할수도 있다. 간단히 특정한 한개의 텍스트만 추출할때 사용하면 유용하다.

 

textdata=driver.find_element(By.XPATH, '//button[text()="Some text"]').text
print(textdata)

 

 

 

 

 

크롤링된 데이터 가공하기

 

 

find 를 사용해서 데이터를 추출하는 예제1

 

browser.get(call_url1)
soup=BeautifulSoup(browser.page_source,"lxml")

datas=soup.find("div",attrs={"class":"cm_info_box"}).find_all(["dt","dd"])   
# 위 처럼 하면 두가지를 동시에 찾을수가 있다.
for data in datas:         
    info=data.text

datas=soup.find("ul",attrs={"class":"grid_box _exterior _list"}).find_all("li",attrs={"class":"item _item"})  
#아래는 이미지 주소와 url 주소를 찾는 예제다.
for data in datas:
    if data.find("img"):
        img_src=data.find("img")["src"]
    if data.find("a"):
        url=data.find("a")["href"]

 

find 를 사용해서 데이터를 추출하는 예제2

 

아래는 requests 를 이용해서 데이터를 크롤링했을경우의 예제다.

특히 주목해야 할부분은  

items=soup.find_all("li", attrs={"class":re.compile("^_itemSection")})

요기 인데

"^_itemSection" 는 클래스 이름이 _itemSection 로 시작하는것을 모두 찾으라는 뜻이다. 정규식을 사용하는 방법이다.

크롤링에서 유일하게 많이 사용되는 정규식이니 알아두면 종종 사용하게 될것이다.

 

 

import requests
import re
from bs4 import BeautifulSoup
import csv
url = "https://search.shopping.naver.com/best100v2/detail.nhn?catId=50004603"
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36"} 
res = requests.get(url, headers=headers)
res.raise_for_status()
soup = BeautifulSoup(res.text,'html.parser')
items=soup.find_all("li", attrs={"class":re.compile("^_itemSection")})
powerbanklist = [] # 라스트 생성
for item in items:
    temp = []
    name = item.find("a")["title"]#제품명
    price = item.find("span", attrs = {"class":"num"}).get_text() #가격 
    link = item.find("div", attrs={"class":"thumb_area"}).find("a")["href"] #링크 
    review_number = item.find("span",attrs = {"class":"mall"}).find("em").text #리뷰수
    review_number = review_number[1:-1]
    temp.append(name)
    temp.append(link)
    temp.append(price)
    temp.append(review_number)
    powerbanklist.append(temp)
# print(notebooklist[0])
with open('powerbanklist.csv',"w", encoding="utf-8-sig", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(['품명','링크','가격','리뷰수'])
    writer.writerows(powerbanklist)
f.close

 

 

select 를 사용해서 데이터를 추출하는 예제

 

 
soup = BeautifulSoup(res.text,'html.parser')
items=soup.select("#productListArea > ul > li")
powerbanklist = [] # 라스트 생성
#print(items)
for item in items:
    temp=[]
    name = item.select_one("#productListArea > ul > li > p > a")["title"]
    price = item.select_one("#productListArea > ul > li > div.price > strong > span.num").text
    link = item.select_one("#productListArea > ul > li > p > a")["href"]
    review_number = item.select_one("#productListArea > ul > li > div.info > span > a.txt > em").text
    review_number = review_number[1:-1]
    # print (review_number)
    temp.append(name)
    temp.append(link)
    temp.append(price)
    temp.append(review_number)
    powerbanklist.append(temp)

 

select 를 사용하면 좀더 편리하게 코딩이 가능하다.

개발자모드에 들어간후 원하는 요소에다 마우스 우클릭후 copy selector 를 클릭하면 select 주소가 복사가된다.

 

select_one 은 첫번째 요소만 반환되고

select 를 사용하면 여러개가 리스트로 반환된다.

 

 

 

find_element 를 이용해서  데이터를 추출하는 방법

 

from selenium.webdriver.common.by import By

driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_element(By.XPATH, '//button')
driver.find_element(By.ID, 'loginForm')
driver.find_element(By.LINK_TEXT, 'Continue')
driver.find_element(By.PARTIAL_LINK_TEXT, 'Conti')
driver.find_element(By.NAME, 'username')
driver.find_element(By.TAG_NAME, 'h1')
driver.find_element(By.CLASS_NAME, 'content')
driver.find_element(By.CSS_SELECTOR, 'p.content')

# DOM의 하위집합 평가
fruits = driver.find_element(By.ID, "fruits")
fruit = fruits.find_element(By.CLASS_NAME,"tomatoes")

# 최적화 요소
fruit = driver.find_element(By.CSS_SELECTOR,"#fruits .tomatoes")

# 일치하는 모든 요소
plants = driver.find_elements(By.TAG_NAME, "li")

# 모든 요소 리스트에서 각 요소 가져오기
elements = driver.find_elements(By.TAG_NAME, 'p')
for e in elements:
    print(e.text)

# 요소에서 요소찾기
element = driver.find_element(By.TAG_NAME, 'div')
elements = element.find_elements(By.TAG_NAME, 'p')
for e in elements:
    print(e.text)

# 요소에서 속성값을 얻기
elements = browser.find_elements(By.TAG_NAME, 'a')
for element in elements:
    print(element.get_attribute('href'))

elements = browser.find_elements(By.TAG_NAME, 'img')
for element in elements:
    print(element.get_attribute('src'))
    

# 활성 요소 (현재 포커스가 있는)
attr = driver.switch_to.active_element.get_attribute("title")

 

 

 

 

알아두면 좋은 정보

 

텍스트 추출했을때 줄바꿈이 안되어 있을때 

 

requst_lyrics = soup.select('div.show_lyrics')

for i in requst_lyrics:
    s=i.get_text()
    print(s)

 

위와 같이 텍스트만 추출했다고 가정해보자.

근데 텍스트가 줄바꿈이 적용이 안되어서 보기가 너무 어려운 경우가 생기기도 한다.

왜냐하면 중간중간 <br/> 태그가 있는데 이게 무시되기 때문인것 같다.

이럴때 해결방법이다.

 

requst_lyrics = soup.select('div.show_lyrics')

for i in requst_lyrics:
    s=str(i)   
    s = s[40:]
    s = s[:-6]
    s = s.replace('<br/>', '\n')
    print(s)

 

웹엘리먼트 요소를 문자로 타입변경한후 필요없는 부분 잘라내고 <br/> 태그를 줄바꿈으로 변경했다.

이것도 알아두면 유용할것이다.

 

 

STRING 으로 문자 추출하기

 

텍스트가 포함된 태그를 찾을때 유용하다. 이것은 정규식을 이용해서 특정단어를 포함하는  문자 찾기나 시작하는 문자 찾기를 이용하면 좋은 결과를 얻을수있을듯하다.

 

 

soup.title.find_all(string=True)
soup.title(string=True)
# 스트링이 있는 title 태그 모두 검색

soup.find_all(string="Elsie")
# [u'Elsie']

soup.find_all(string=["Tillie", "Elsie", "Lacie"])
# [u'Elsie', u'Lacie', u'Tillie']

soup.find_all(string=re.compile("Dormouse"))
[u"The Dormouse's story", u"The Dormouse's story2"]
#Dormouse 를 포함하는 스트링 찾기


soup.find_all(string=is_the_only_string_within_a_tag)
# [u"The Dormouse's story", u"The Dormouse's story", u'Elsie', u'Lacie', u'Tillie', u'...']

soup.find_all("a", string="Elsie")
# [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]

 

 

인풋박스에 내용입력이 안될때 아래 방법으로 해결 (ActionChains)

 

from selenium.webdriver.common.action_chains import ActionChains

html = "내용"
browser.find_element(By.CSS_SELECTOR, 'textarea.KHxj8b.tL9Q4c').click()
time.sleep(0.5)
action = ActionChains(browser)
action.send_keys(html).perform()

 

ActionChains 을 사용해서 아이디와 비번을 입력하고 로그인버튼을 누르는 예제

 

send_keys 를 이용해서 내용 입력하는것이 안될때 ActionChains 을 이용하면 가능한 경우가 많았다.

 

from selenium.webdriver.common.action_chains import ActionChains

# 사용자 동작에 필요한 웹 요소들 찾기

actions = ActionChains(driver)
actions.send_keys_to_element(id_box, 'str1')
actions.send_keys_to_element(pw_box, 'str2')
actions.click(login_button)
actions.perform()

 

 

문자열에서 특수문자를 제거하는 방법 , 숫제만 제거, 숫자만 추출 방법

 

정규식을 사용해서  특수문자를 없애는 방법입니다. 크롤링한 제목을 이용해서 폴더이름을  생성할때 특수문자가 있어서 폴더를 만들수 없다는 에러가 나올때가 있습니다. 이럴때 사용하면 좋습니다.

 

import re


#특수문자 제거
str = "AA**BB#@$CC 가나다-123"
new_str = re.sub(r"[^\uAC00-\uD7A30-9a-zA-Z\s]", "", str)
print(new_str)
#Output:AABBCC 가나다123


#숫자만 남기기
new_str = re.sub(r"[^0-9]", "", string)
print(new_str)
#Output:123

#숫자만 제거
new_str = re.sub(r"[0-9]", "", string)
print(new_str)
#Output:AA**BB#@$CC 가나다-

 

아래는 한글만 추출하는 예제입니다.

 

import re

my_str = "안녕하세요 ㅎㅎ. Hello World! 12345?"

kor_str = re.sub(r"[^ㄱ-ㅣ가-힣\s]", "", my_str) # 한글 + 공백만 남기기
not_kor_str = re.sub(r"[ㄱ-ㅣ가-힣]", "", my_str) # 한글만 제거하기
not_zamo_str = re.sub(r"[^가-힣]", "", my_str) # 자모가 아닌 한글만 남기기(공백 제거)

print(kor_str) # 안녕하세요 ㅎㅎ   
print(not_kor_str) #  . hello world! 12345?
print(not_zamo_str) # 안녕하세요

 

아래는 영문만 추출하는 예제입니다.

 

import re

my_str = "안녕하세요 ㅎㅎ. Hello World! 12345?"

eng_str = re.sub(r"[^a-zA-Z\s]", "", my_str) # 영문자 + 공백만 남기기
not_eng_str = re.sub(r"[a-zA-Z]", "", my_str) # 영문자만 제거하기
lower_eng_str = re.sub(r"[^a-z]", "", my_str) # 소문자만 남기기

print(eng_str) #   Hello World 
print(not_eng_str) # 안녕하세요 ㅎㅎ.  ! 12345?
print(lower_eng_str) # elloorld

'파이썬' 카테고리의 다른 글

썸네일 이미지 자동으로 만들기 / 파이썬 / webp  (0) 2022.11.29

댓글