برنامه‌نویسی

آموزش Web Scraping با Selenium

آموزش web scraping با selenium

Selenium ابزاری است که به شما در اجرای تست‌‌های خودکار برنامه‌‌های وب کمک می‌کند. اگرچه هدف اصلی این مقاله، استفاده از Selenium برای Web Scraping با زبان Python است اما شما می‌توانید در زمینه‌های مختلفی از مزیت‌های این ابزار بهره‌مند شوید.

تفاوت Selenium با دیگر ابزارهای Web Scraping مانند BeautifulSoup در این است که می‌تواند به محتوای رندر شده با JavaScript دسترسی داشته باشد. علاوه‌براین ابزار Selenium در زمانی مفید است که شما می‌خواهید علاوه بر جمع‌آوری داده‌ها از صفحه‌های وب، به‌نوعی با صفحه ارتباط برقرار کنید. مثلا روی دکمه‌ها کلیک کنید یا از داده‌های مشخص شده‌ای در فیلد‌ها استفاده کنید.

هدف برنامه‌ی نهایی ما، استخراج نوسان‌های نرخ دلار از وبسایت investing.com است.

تجزیه و تحلیل URL

در این بخش، URL وبسایت هدف را تجزیه و تحلیل می‌کنیم. در این آدرس از وبسایت investing.com نوسان‌های نرخ دلار به‌نسبت یورو نمایش داده می‌شود. همچنین در این صفحه می‌توانید تاریخ معینی را تعیین کرده و نوسان‌های این دو ارز را در بازه‌ی خاصی از زمان مشاهده کنید.

برای مشاهده‌ی سایر ارز‌ها به‌نسبت نوسان‌های دلار می‌توانید مقدار eur را با ارز دلخواه خود در URL فوق جایگزین کنید.

Web Scraping با Selenium

بهتر است قبل از شروع توسعه‌ی پروژه یک محیط مجازی (Virtual environment) ایجاد کنیم که در مقاله‌ی آموزش نصب و استفاده از Virtual environment در Python عمیق‌تر به این موضوع پرداخته‌ایم.

پس از ایجاد محیط مجازی و active کردن آن، وابستگی‌های پروژه را با اجرای دستور‌های زیر نصب می‌کنیم:

pip install selenium
pip install pandas
pip install lxml

حال یک فایل با نام main.py در محیط مجازی ایجاد کرده و به توسعه‌ی برنامه می‌پردازیم:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas

در این مرحله بایستی یک فانکشن که داده‌های زیر را به‌عنوان ورودی دریافت می‌کند، توسعه دهیم:

  • لیستی از کدهای ارزها
  • تاریخ شروع بازه‌ی زمانی معین
  • تاریخ پایان بازه‌ی زمانی معین
  • یک مقدار Boolean برای زمانی که بخواهید خروجی برنامه را در یک فایل .csv دریافت کنید

ایده به این شکل است که داده‌های مختلفی از هر ارز را جمع‌آوری کنیم بنابراین یک لیست خالی را برای ذخیره‌ی داده‌ها آماده خواهیم کرد:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []

با فانکشن فوق می‌توانیم لیستی از ارز‌ها را دریافت کرده و با پیمایش آن‌ها، داده‌های مربوط به هر ارز را استخراج کنیم. برای هر ارز موجود در این لیست باید یک URL داینامیک داشته باشیم، یک DriverObject را نمونه‌سازی کرده و سپس از آن برای دریافت داده‌های صفحه استفاده کنیم.

سپس بایستی پنجره‌ی باز شده‌ی مرورگر را Maximize کنیم. البته نتیجه‌ی این کار فقط با فعال بودن گزینه‌ی option.headless = False قابل مشاهده است:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:

        # Opening the connection and grabbing the page
        chrome_driver_path = ''
        my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
        option = Options()
        option.headless = False
        option.executable_path = chrome_driver_path
        driver = webdriver.Chrome(options=option)
        driver.get(my_url)
        driver.maximize_window()


در کدهای بالا بایستی مقدار متغیر chrome_driver_path را تعیین کنید اما قبل از بایستی ChromeDriver دانلود کرده باشید. برای مثال ChromeDriver ما در مسیر زیر قرار دارد:

C:/liara/selenium-scrapper/pys/chromedriver.exe

بنابراین مقدار متغیر chrome_driver_path را به شکل زیر تعیین می‌کنیم:

chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'

برای دریافت نوسان‌های ارز در یک بازه‌ی زمانی معین از برخی قابلیت‌های جالب Selenium برای تعامل با وبسایت استفاده می‌کنیم. باید برای پیاده‌سازی این بخش بر روی گزینه‌ی تاریخ کلیک کرده، قسمت‌های مورد نیاز را با داده‌های تعریف شده پر کنیم و در نهایت این فیلتر اعمال شود.

حال برای تعیین کردن بازه‌ی زمانی معین بایستی فیلتر تاریخ را به شکل زیر انتخاب کرده و بر روی آن کلیک کنیم:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:

        # Opening the connection and grabbing the page
        chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'
        my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
        option = Options()
        option.headless = False
        option.executable_path = chrome_driver_path
        driver = webdriver.Chrome(options=option)
        driver.get(my_url)
        driver.maximize_window()
        # Clicking on the date button
        date_button = driver.find_element_by_id('flatDatePickerCanvasHol')
        date_button.click()

اکنون باید فیلدهای مربوط به Start Date را پر کنیم:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:

        # Opening the connection and grabbing the page
        chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'
        my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
        option = Options()
        option.headless = False
        option.executable_path = chrome_driver_path
        driver = webdriver.Chrome(options=option)
        driver.get(my_url)
        driver.maximize_window()
        # Clicking on the date button
        date_button = driver.find_element_by_id('flatDatePickerCanvasHol')
        date_button.click()
        # Sending the start date
        start_bar = driver.find_element_by_id('startDate')
        start_bar.clear()
        start_bar.send_keys(start)

با استفاده از clear تمام داده‌های پیش‌فرض این فیلد حذف شده و با استفاده از send_keys، داده‌های جدید را در این فیلد قرار می‌دهیم. همچنین این روند را نیز بایستی برای End Date تکرار کنیم:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:

        # Opening the connection and grabbing the page
        chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'
        my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
        option = Options()
        option.headless = False
        option.executable_path = chrome_driver_path
        driver = webdriver.Chrome(options=option)
        driver.get(my_url)
        driver.maximize_window()
        # Clicking on the date button
        date_button = driver.find_element_by_id('flatDatePickerCanvasHol')
        date_button.click()
        # Sending the start date
        start_bar = driver.find_element_by_id('startDate')
        start_bar.clear()
        start_bar.send_keys(start)
        # Sending the end date
        end_bar = driver.find_element_by_id('endDate')
        end_bar.clear()
        end_bar.send_keys(end)

سپس بایستی بر روی دکمه‌ی Apply کلیک کنید تا فیلترها اعمال شوند. همچنین از sleep استفاده می‌کنیم تا برنامه‌ی ما برای چند ثانیه متوقف شده و صفحه‌ی جدید کاملا بارگیری شود:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:

        # Opening the connection and grabbing the page
        chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'
        my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
        option = Options()
        option.headless = False
        option.executable_path = chrome_driver_path
        driver = webdriver.Chrome(options=option)
        driver.get(my_url)
        driver.maximize_window()
        # Clicking on the date button
        date_button = driver.find_element_by_id('flatDatePickerCanvasHol')
        date_button.click()
        # Sending the start date
        start_bar = driver.find_element_by_id('startDate')
        start_bar.clear()
        start_bar.send_keys(start)
        # Sending the end date
        end_bar = driver.find_element_by_id('endDate')
        end_bar.clear()
        end_bar.send_keys(end)
        # Clicking on the apply button
        apply_button = driver.find_element_by_id('applyBtn')
        apply_button.click()
        sleep(5)

اگر option.headless را برابر با False قرار داده باشید می‌توانید تمام فرایند اجرای برنامه را مشاهده کنید. حال زمان آن است که از فانکشن pandas.read_html برای انتخاب کردن جدول مورد نظرمان در صفحه‌ی وب استفاده کنیم. این فانکشن source code صفحه را دریافت می‌کند و پس از آن می‌توانید از ChromeDriver فعلی خارج شوید:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:
        # Opening the connection and grabbing the page
        chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'
        my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
        option = Options()
        option.headless = False
        option.executable_path = chrome_driver_path
        driver = webdriver.Chrome(options=option)
        driver.get(my_url)
        driver.maximize_window()
        # Clicking on the date button
        date_button = driver.find_element_by_id('flatDatePickerCanvasHol')
        date_button.click()
        # Sending the start date
        start_bar = driver.find_element_by_id('startDate')
        start_bar.clear()
        start_bar.send_keys(start)
        # Sending the end date
        end_bar = driver.find_element_by_id('endDate')
        end_bar.clear()
        end_bar.send_keys(end)
        # Clicking on the apply button
        apply_button = driver.find_element_by_id('applyBtn')
        apply_button.click()
        sleep(5)
        # Getting the tables on the page and quiting
        dataframes = pandas.read_html(driver.page_source)

        driver.quit()
        print(f'{currency} scraped.')

مدیریت Exceptionها در Selenium

فرایند جمع‌آوری داده‌ها در بخش قبل تمام شد اما بایستی Exceptionهای احتمالی را درنظر بگیریم. به‌همین منظور از try استفاده خواهیم کرد تا اگر برنامه‌ی ما با مشکلی روبرو شد قادر باشیم در بخش except آن را مدیریت کنیم. سناریو ما به‌صورت زیر است:

  • ChromeDriver به‌منظور استفاده‌ی بهینه از memory، بسته شود.
  • در زمان ایجاد خطا یک پیام در Console چاپ شود.
  • پس از آن برنامه برای ده ثانیه متوقف شود.
  • برنامه مجددا اجرا شود.

بایستی این روند را مرتبا تا زمانی که داده‌های مربوط به هر ارز دریافت شود، تکرار کنیم:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:
        try:
            # Opening the connection and grabbing the page
            chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'
            my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
            option = Options()
            option.headless = False
            option.executable_path = chrome_driver_path
            driver = webdriver.Chrome(options=option)
            driver.get(my_url)
            driver.maximize_window()
            # Clicking on the date button
            date_button = driver.find_element_by_id('flatDatePickerCanvasHol')
            date_button.click()
            # Sending the start date
            start_bar = driver.find_element_by_id('startDate')
            start_bar.clear()
            start_bar.send_keys(start)
            # Sending the end date
            end_bar = driver.find_element_by_id('endDate')
            end_bar.clear()
            end_bar.send_keys(end)
            # Clicking on the apply button
            apply_button = driver.find_element_by_id('applyBtn')
            apply_button.click()
            sleep(5)
            # Getting the tables on the page and quiting
            dataframes = pandas.read_html(driver.page_source)

            driver.quit()
            print(f'{currency} scraped.')

        except:
            driver.quit()
            print(f'Failed to scrape {currency}. Trying again in 10 seconds.')
            sleep(10)
            continue

تا به اینجا تمام صفحه‌ی وب را در متغیری با نام dataframes ذخیره کرده‌ایم. حال بایستی جدول مورد نظر را از بقیه‌ی کدهای موجود در صفحه، جدا کنیم:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:
        try:
            # Opening the connection and grabbing the page
            chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'
            my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
            option = Options()
            option.headless = False
            option.executable_path = chrome_driver_path
            driver = webdriver.Chrome(options=option)
            driver.get(my_url)
            driver.maximize_window()
            # Clicking on the date button
            date_button = driver.find_element_by_id('flatDatePickerCanvasHol')
            date_button.click()
            # Sending the start date
            start_bar = driver.find_element_by_id('startDate')
            start_bar.clear()
            start_bar.send_keys(start)
            # Sending the end date
            end_bar = driver.find_element_by_id('endDate')
            end_bar.clear()
            end_bar.send_keys(end)
            # Clicking on the apply button
            apply_button = driver.find_element_by_id('applyBtn')
            apply_button.click()
            sleep(5)
            # Getting the tables on the page and quiting
            dataframes = pandas.read_html(driver.page_source)

            driver.quit()
            print(f'{currency} scraped.')

        except:
            driver.quit()
            print(f'Failed to scrape {currency}. Trying again in 10 seconds.')
            sleep(10)
            continue

    # Selecting the correct table
    for dataframe in dataframes:
        if dataframe.columns.tolist() == ['Date', 'Price', 'Open', 'High', 'Low', 'Change %']:
            frames.append(dataframe)

اکنون اگر مقدار export_csv برابر با True باشد، بایستی داده‌های خروجی را با استفاده از dataframe.to_csv در یک فایل .csv ذخیره کنیم:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:
        try:
            # Opening the connection and grabbing the page
            chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'
            my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
            option = Options()
            option.headless = False
            option.executable_path = chrome_driver_path
            driver = webdriver.Chrome(options=option)
            driver.get(my_url)
            driver.maximize_window()
            # Clicking on the date button
            date_button = driver.find_element_by_id('flatDatePickerCanvasHol')
            date_button.click()
            # Sending the start date
            start_bar = driver.find_element_by_id('startDate')
            start_bar.clear()
            start_bar.send_keys(start)
            # Sending the end date
            end_bar = driver.find_element_by_id('endDate')
            end_bar.clear()
            end_bar.send_keys(end)
            # Clicking on the apply button
            apply_button = driver.find_element_by_id('applyBtn')
            apply_button.click()
            sleep(5)
            # Getting the tables on the page and quiting
            dataframes = pandas.read_html(driver.page_source)

            driver.quit()
            print(f'{currency} scraped.')

        except:
            driver.quit()
            print(f'Failed to scrape {currency}. Trying again in 10 seconds.')
            sleep(10)
            continue

    # Selecting the correct table
    for dataframe in dataframes:
        if dataframe.columns.tolist() == ['Date', 'Price', 'Open', 'High', 'Low', 'Change %']:
            frames.append(dataframe)
            # Exporting the .csv file
            if export_csv:
                dataframe.to_csv('currency.csv', index=False)
                print(f'{currency}.csv exported.')

    return frames

حال برای اجرای برنامه می‌توانیم این فانکشن را فراخوانی کرده و فایل main.py را با دستور python main.py اجرا کنیم:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
import pandas


def get_currencies(currencies, start, end, export_csv=True):
    frames = []
    for currency in currencies:
        try:
            # Opening the connection and grabbing the page
            chrome_driver_path = 'C:/liara/selenium-scrapper/pys/chromedriver.exe'
            my_url = f'https://investing.com/currencies/usd-{currency.lower()}-historical-data'
            option = Options()
            option.headless = False
            option.executable_path = chrome_driver_path
            driver = webdriver.Chrome(options=option)
            driver.get(my_url)
            driver.maximize_window()
            # Clicking on the date button
            date_button = driver.find_element_by_id('flatDatePickerCanvasHol')
            date_button.click()
            # Sending the start date
            start_bar = driver.find_element_by_id('startDate')
            start_bar.clear()
            start_bar.send_keys(start)
            # Sending the end date
            end_bar = driver.find_element_by_id('endDate')
            end_bar.clear()
            end_bar.send_keys(end)
            # Clicking on the apply button
            apply_button = driver.find_element_by_id('applyBtn')
            apply_button.click()
            sleep(5)
            # Getting the tables on the page and quiting
            dataframes = pandas.read_html(driver.page_source)

            driver.quit()
            print(f'{currency} scraped.')

        except:
            driver.quit()
            print(f'Failed to scrape {currency}. Trying again in 10 seconds.')
            sleep(10)
            continue

    # Selecting the correct table
    for dataframe in dataframes:
        if dataframe.columns.tolist() == ['Date', 'Price', 'Open', 'High', 'Low', 'Change %']:
            frames.append(dataframe)
            # Exporting the .csv file
            if export_csv:
                dataframe.to_csv('currency.csv', index=False)
                print(f'{currency}.csv exported.')

    return frames


print(get_currencies(['eur'], '01/13/2021', '02/13/2021'))

منبع: https://www.freecodecamp.org/news/how-to-code-a-scraping-bot-with-selenium-and-python