آموزش 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

برچسب‌ها:

خدمات رایگان لیارا

۲.۵ گیگابایت فضای ذخیره‌سازی ابری رایگان

۲.۵ گیگابایت Object Storage سازگار با پروتکل S3 با دیسک‌های SSD به‌صورت رایگان دریافت کنید.

هاست رایگان برای دیتابیس‌

دیتابیس‌های MariaDB، PostgreSQL و Redis را فقط با یک کلیک و به‌صورت رایگان تهیه کنید.

سرویس DNS رایگان

به سادگی دامنه‌تان را اضافه کنید و به صورت رایگان رکورد‌های آن را مدیریت کنید.

۱۰۰ هزار تومان اعتبار اولیه

بعد از ثبت نام در لیارا مبلغ ۱۰۰ هزار تومان اعتبار هدیه دریافت می‌کنید که با توجه به ساعتی بودن هزینه سرویس‌ها، می‌توانید تمامی خدمات پولی را برای چندین هفته رایگان استفاده کنید.

ارسال ۱۰۰ ایمیل تراکنشی رایگان در هر ماه

در سرویس ایمیل لیارا شما می‌توانید تا ۱۰۰ ایمیل رایگان در هر ماه ارسال کنید و فقط برای بیش از آن هزینه پرداخت کنید. (به‌همراه دسترسی SMTP)

هاست رایگان برای انواع وبسایت

تفاوتی ندارد برای وبسایت خود از Node استفاده می‌کنید یا Laravel و Django، در لیارا می‌توانید به صورت کاملا رایگان آن را میزبانی کنید.

توسعه‌دهندگان درباره‌ی ما چه می‌گویند

تجربه کار باliara_cloud@امروز خیلی خوب بود. یکی از سرویس هام رو منتقل کردم روش و راضیم. انقد سریع و جذاب کارم راه افتادم اصن باورم نمیشد! برعکس سرویس های PaaS دیگه با اون همه پیچیدگیشون. دمتون گرم
...

MohammadReza
liara testimonial
keikaavousi

بعد از بسته شدن @fandoghpaas و ناراحتی همه‌مون از اینکه یه سرویس خوب و صادق نمی‌تونه از پس هزینه‌ها بر بیاد، سرویسم رو منتقل کردم به پاس لیارا (https://liara.ir @liara_cloud) . تجربه راحت و خوب. تفاوت‌هایی داشت که کمی کار می‌خواست ولی تا الان کاملا راضی.

jadi
liara testimonial
jadi

یه خسته نباشید باید به تصمیمliara_cloud@بگم،
بعد از چندین روز سرکله زدن با سرویس های مشابه بالاخره تصمیم گرفتم لیارا رو امتحان کنم و باور نمیشه ۱۰ دقیقه بیشتر وقت نبرد،
دمتون گرم.

Arch
liara testimonial
EbadiDev

واسه سرویس PaaS با اختلاف لیارا بهترین رابط کاربری داره و یکی از مزیت‌های سرویس دیتابیس‌شون اینه که خودشون به صورت دوره‌ای بکآپ میگیرن.
...

Ali Najafi
liara testimonial
me_ali_najafi

یکی از کارهای خوبی که جدیداً میکنم اینه که یه دیتابیس روی لیارا میسازم و به پروژه وصل میکنم اینطوری هم خونه و هم محل کار دیتابیس بروز رو دارم و راحت میتونم ادامه بدم کار روliara_cloud@

Navid
liara testimonial
1navid

عاشقliara_cloud@شدم درسته در حد AWS نیست ولی خب تجربه خوبی واسه پروژه های داخل ایران ارائه میده، میتونم رو CD هم اجراش کنم

Amir H Shekari
liara testimonial
vanenshi

همراه شما هستیم

در خصوص سفارش یا استفاده از سرویس‌ها سوالی دارید؟
تلفن واحد فروش:
۰۲۵-۳۳۵۵۷۶۱۹ (روزهای کاری ۹ الی ۱۷)
تلفن واحد فروش: ۳۳۵۵۷۶۱۹-۰۲۵ (روزهای کاری ۹ الی ۱۷)