본문 바로가기
Python

[Python]selenium-stealth를 이용한 Cloudflare 우회

by 태진아밴드 2022. 2. 22.

이더리움 ERC-20 토큰 관련해서 DB화를 시켜야할일이 생겼었다.

 

토큰 정보들을 어디서 가져오기 고민하다가 그냥 심플하게 이더스캔에서 크롤링을 해오기로 결정.

 

https://etherscan.io/tokens

 

Token Tracker | Etherscan

The list of ERC-20 Tokens and their Prices, Market Capitalizations and the Number of Holders in the Ethereum Blockchain on Etherscan.

etherscan.io

위 페이지 내에서 토큰명, 심볼, contract address, 현재가격을 가져와서 저장하는 코드를 짠 뒤

 

실서버에 파일만 올려두고 크론탭을 이용해 주기적으로 실행시켜줄 생각이였다.

 

로컬에서 테스트를 마치고 리눅스서버에 올려서 테스트를 해보는데 오잉...?

 

잘 되던 소스가 안돌아간다...?

 

왜 안대여...?

왜 element를 못찾나 싶어서 전체소스를 print해봤는데 서버에서 호출했더니

 

Cloudflare 방화벽에 막혀서 이더스캔이 아닌 Cloudflare 페이지가 떠서 크롤링을 못했던거였다.

 

Cloudflare recaptcha를 우회할 방법을 여기저기 찾아보고 일주일을 삽질하다가

 

역시 우리의 선생님 stackover flow에서 방법을 찾아서 여기 적어두려고 한다.

 

우선 pip로 selenium-stealth를 설치해주자.

pip install selenium-stealth

 

나머지는 그냥 코드로 보자..^^!

 

import requests
from selenium.webdriver.common.by import By
from selenium import webdriver
from selenium_stealth import stealth

# 이더스캔 토큰정보 크롤링 함수
def crawling_init():
    result = []

    # 환율정보
    EXCHANGE_API_URL = 'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD'
    # 이더스캔 URL
    EHTERSCAN_URL = 'https://etherscan.io'

    response = requests.get(url=EXCHANGE_API_URL)
    # 달러 -> 원화 환율기준
    usd_to_krw = response.json()[0].get('basePrice')
    options = webdriver.ChromeOptions()
    # 크롬드라이버 헤더 옵션추가 (리눅스에서 실행시 필수)
    options.add_argument("start-maximized")
    options.add_argument("--headless")
    options.add_argument("--no-sandbox")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option('useAutomationExtension', False)

    # 크롬드라이버 경로
    driver_path = '서버내의 크롬드라이버 경로'
    driver = webdriver.Chrome(executable_path=driver_path, chrome_options=options)

### 추가된 부분 ###
    # selenium stealth 옵션추가 ( cloudflare 우회용 )
    stealth(driver,
            languages=["en-US", "en"],
            vendor="Google Inc.",
            platform="Win32",
            webgl_vendor="Intel Inc.",
            renderer="Intel Iris OpenGL Engine",
            fix_hairline=True,
            )
    ### // 추가된 부분 ###

    driver.get(EHTERSCAN_URL + '/tokens?ps=50p=1')
    driver.implicitly_wait(10)

    # 전체 페이지 수
    total_page = int(driver.find_element(By.XPATH,
                                         '//*[@id="ContentPlaceHolder1_divpagingpanel"]/nav/ul/li[3]/span/strong[2]').text)

    page_cnt = 1
    while page_cnt < total_page + 1:
        driver.get(EHTERSCAN_URL + '/tokens?p=' + str(page_cnt))
        # 페이지 로딩 대기
        driver.implicitly_wait(10)
        # 토큰 리스트
        token_xpath = '//*[@id="tblResult"]/tbody/tr'
        token_td = driver.find_elements(By.XPATH, token_xpath)
        token_td_len = len(token_td)
        i = 1
        while i < token_td_len + 1:
            token_tr_xpath = token_xpath + '[' + str(i) + ']'
            token_txt = driver.find_element(By.XPATH, token_tr_xpath + '/td[2]/div/div/h3/a').text
            # 토큰명
            token_name = token_txt.split(' (')[0]
            # 토큰 심볼
            token_symbol = token_txt.split(' (')[1].replace(')', '')
            # 토큰 이미지 url
            token_img = driver.find_element(By.XPATH, '//*[@id="tblResult"]/tbody/tr['
                                            + str(i) + ']/td[2]/div/img').get_attribute('src')
            # 토큰 컨트랙트주소
            contract_addr = \
                driver.find_element(By.XPATH, token_tr_xpath + '/td[2]/div/div/h3/a').get_attribute('href').split(
                    'token/')[
                    1]

            token_usd_price_txt = driver.find_element(By.XPATH, token_tr_xpath + '/td[3]').text
            # 달러기준 가격
            token_usd_price = token_usd_price_txt.split('\n')[0].replace('$', '').replace(',', '')
            # 원화기준 가격
            token_krw_price = round(float(token_usd_price) * usd_to_krw, 2)
            obj = {'token_name': token_name, 'token_symbol': token_symbol, 'contract_addr': contract_addr,
                   'token_img': token_img, 'usd_price': token_usd_price, 'krw_price': token_krw_price}
            result.append(obj)
            i = i + 1
        page_cnt = page_cnt + 1
    driver.quit()
    return result

 

기존 소스에 stealth 옵션을 추가해주면 끝..!

 

저게 막히면 또 무슨방법으로 해야될지는 모르겠다😢

 

 

* 참조

https://stackoverflow.com/questions/68289474/selenium-headless-how-to-bypass-cloudflare-detection-using-selenium