اتوماسیون مرورگر با Selenium + مثال دیجی کالا

سلنیوم (Selenium) یک مجموعه ابزار و کتابخانه برای خودکارسازی اموری که با مرورگرها انجام می شوند است و برای زبان های برنامه نویسی مختلف کتابخانه دارد و برای مرورگرهای مختلف هم درایور دارد تا با استفاده از آن یک مرورگر تحت کنترل سلنیوم باز شود و شروع به انجام مواردی که برنامه ریزی شده اند بکند. بنقل از سایت خودش :Primarily it is for automating web applications for testing purposes, but is certainly not limited to just that.برای تست های مختلف نرم افزاری و زیرساخت و اتوماسیون کار با مرورگر مثل ورود اطلاعات یا ثبت نام و موارد مشابه کاربرد دارد و ابزار تخصصی و محبوب این کار است.یعنی شما برنامه ریزی می فرمایید که فلان url را باز کن، در کد html دنبال فلان چیز بگرد، فلان چیز را تایپ کن، روی فلان المنت کلیک کن و هر کار دیگری که شما روی مرورگر انجام می دهید.مثل بسیاری دیگری از ماژول های پایتون کارکردن با آن ساده تر از چیزی است که در ابتدا بنظر می رسد.روی پایتون با یک pip install selenium براحتی نصب می شود، نصب کتابخانه سلنیوم برای محیط های مختلف اینجا توضیح داده شده است. باید درایور مربوط به مرورگر را هم نصب بفرمایید. در این بخش از مستندات نصب درایور به روش های مختلف و در زبان های مختلف توضیح داده شده است.در مورد مثال جمع زدن مجموع خرید از دیجی کالا :توجه بفرمایید که براساس "شرایط و قوانین استفاده از سرویس‌ها و خدمات دیجی‌کالا" کاربران مجاز به استفاده از ربات و داده کاوی و موارد مشابه نیستند.ما هم درحقیقت چنین کاری نمی کنیم، اکانت و اطلاعات هر کاربر متعلق به وی است و ما فقط در حال اتوماسیون امور مجاز مربوط به یک شخص کاربر هستیم.کد زیر :۰- نام کاربری و پسورد و تعداد کل سفارشات تحویل شده را می گیرد(بالای کد).۱- یک مرورگر کروم را تحت مدیریت و کنترل سلنیوم باز می کند.۲- آدرس /=https://www.digikala.com/users/login/?backUrl را که صفحه لاگین است در آن باز می کند.۳- نام کاربری و سپس رمز عبور را وارد می کند.۴- وارد صفحه =https://www.digikala.com/profile/orders/?activeTab=sent&page که لیست سفارشات تحویل شده است می شود، مقدار page معادل شماره صفحه است که در یک حلقه صفحه به صفحه فراخوانی می شوند. مقدار page براساس تقسیم تعداد کل سفارشات به ۱۰ که تعداد در هر صفحه است و گرد کردن نتیجه آن به بالا تعیین می شود. مثلا ً برای ۱۰۴ سفارش می شود ۱۱ صفحه.۵- مبلغ سفارشات را در هر صفحه به یک لیست اضافه می کند و به سراغ صفحه بعد می رود. در اینجا اعداد فارسی مثل ۱ که درحقیقت کاراکتر هستند و عدد نیستند با تابع تعریف شده formatter به 1 که عدد است تبدیل می شوند که بتوان بسادگی جمع زد.۶- در پایان مجموع لیست را که حاوی مبالغ است جمع می زند و چاپ می کند.import time import math from selenium import webdriver from selenium.webdriver.common.by import By digiUser = &quotusername&quot # digikala mobile or username digiPassword = &quotpassword&quot # digikala password ordersCount = 104 # total number of orders ordersPages = math.ceil(ordersCount/10) # number of orders pages to loop purchaseAmounts=[] # container of all purchases prices driver = webdriver.Chrome() driver.maximize_window() driver.get(&quothttps://www.digikala.com/users/login/?backUrl=/&quot) # digkala login page time.sleep(5) driver.find_element(by=By.NAME, value='username').send_keys(digiUser) # enter username driver.find_element(by=By.TAG_NAME, value='form').submit() time.sleep(5) driver.find_element(by=By.NAME, value='password').send_keys(digiPassword) # enter password driver.find_element(by=By.TAG_NAME, value='form').submit() time.sleep(5) def formatter(x): x = x.replace(&quot,&quot,&quot&quot) x = x.replace(&quot۱&quot,&quot1&quot) x = x.replace(&quot۲&quot,&quot2&quot) x = x.replace(&quot۳&quot,&quot3&quot) x = x.replace(&quot۴&quot,&quot4&quot) x = x.replace(&quot۵&quot,&quot5&quot) x = x.replace(&quot۶&quot,&quot6&quot) x = x.replace(&quot۷&quot,&quot7&quot) x = x.replace(&quot۸&quot,&quot8&quot) x = x.replace(&quot۹&quot,&quot9&quot) x = x.replace(&quot۰&quot,&quot0&quot) return int(x) for i in range(1, ordersPages+1): url = &quothttps://www.digikala.com/profile/orders/?activeTab=sent&page=&quot+str(i) # go through orders pagination driver.get(url) time.sleep(10) p = driver.find_elements(by=By.CSS_SELECTOR, value='div.text-body1-strong div.color-800 div.text-body1-strong') for pp in p: purchaseAmounts.append(formatter(pp.get_attribute(&quotinnerHTML&quot))) # text inside = purchase amount print(&quot============================&quot) print(&quotTotal Purchases Count : &quot+ str(len(purchaseAmounts)) + &quot From Total &quot + str(ordersCount) + &quot orders&quot) print(&quotTotal Purchases Amount : &quot+f&quot{sum(purchaseAmounts):,}&quot+ &quot (Toman)&quot) print(&quot============================&quot)نکات و توضیح کد :در خطوط ۱ تا ۴ ماژول های مورد نیاز import شده اند.در خطوط ۶ تا ۱۰ متغییر های مورد نیاز تعریف و مقداردهی شده اند.خطوط ۱۲ تا ۱۵ ، یک مرورگر کروم را تحت کنترل سلنیوم باز می کند، آن را maximize می کند، صفحه لاگین دیجی کالا را باز می کند و ۵ ثانیه اجرای کند متوقف می شود.خطوط ۱۶ تا ۱۸ در صفحه لاگین باز شده براساس نام المنت داخل username نام کاربری را وارد کرده و form را submit می کند و ۵ ثانیه اجرای کند متوقف می شود.خطوط ۱۹ تا ۲۱ در صفحه جدید باز شده password را مثل همان نام کاربری پیدا کرده وارد می کند و فرم را submit می کند و ۵ ثانیه اجرای کند متوقف می شود.خطوط ۲۳ تا ۳۶ تابع formatter است که , را حذف کرده و اعداد فارسی را به اعداد integer تبدیل می کند.تابع for در خط ۳۸ باعث می شود صفحات لیست سفارشات تحویل شده صفحه به صفحه باز شده و دستورات داخل آن برای آن ها اجرا شود. دقت بفرمایید حلقه در تابع range پایتون از مقدار شروع تا یکی کمتر مقدار پایان اجرا می شود بنابراین مقدار ordersPages بعلاوه یک شده است.خطوط ۳۹ تا ۴۱ هر صفحه سفارشات تحویل شده را باز می کند و ۱۰ثانیه اجرای کد روی آن متوقف می شود تا اطمینان پیدا کنیم صفحه کامل باز شده است.خط ۴۲ براساس CSS_SELECTOR و بوسیله find_elements همه مبلغ ها را پیدا می کند. مبلغ در صفحه سفارشات تحویل شده همیشه در درون یک div با کلاس text-body1-strong که خودش درون یک div با کلاس color-800 است و خود آن درون یک div با کلاس text-body1-strong است قرار دارد و این ترتیب همیشه ثابت است. روش های مختلف پیدا کردن element را اینجا ببینید.در خطوط ۴۴ و ۴۵ مبالغ استخراج شده از هر صفحه پس از عبور از تابع formatter به لیست purchaseAmounts اضافه می شوند.خطوط ۴۷ تا ۵۰ هم مربوط به پرینت تعداد سفارشات و مجموع هستند. استفاده از f"{sum(purchaseAmounts):,} بخاطر این است که عدد مجموع را فرمت کند و سه تا سه تا بین اعداد ، بیفتد.این کد کامل و مطلوب نیست و برای سادگی خلاصه شده است. در امور اتوماسیون و خودکارسازی مدیریت خطاها و exception handling بسیار مهم است. مثلاً در همین سناریو گاهی صفحه ای از سفارشات دیجی کالا خطای ارتباط می دهد یا باز نمی شود و کد پس از اتمام حلقه و پیدا نکردن قیمت بدون خطا به صفحه بعد می رود، بنابراین با توجه به ترافیک و شلوغی دیجی کالا ممکن است از هر ۱۰ بار که اجرا می کنید ۳-۴ بار همه صفحات و قیمت ها را جمع نزده باشد. من یک مکانیزم بسیار ساده برای این مشکل در این سناریو پیاده سازی کرده ام، کل سفارشات تحویل شده من ۱۰۴ مورد است بنابراین در لیست purchaseAmounts باید ۱۰۴ آیتم وجود داشته باشد و بالای مجموع حساب شده تعداد قیمت های منظور شده درج می شود تا چک کنیم حتماً همه صفحات باز شده و همه قیمت ها منظور شده اند.گاهی اوقات که پشت vpnهستم درایور گیر می کند و ۲-۳ دقیقه طول می کشد تا مرورگر باز شود، در بعضی از روش های نصب باید آدرس مسیر درایور را هم در کد و در هنگام فراخوانی بدهید.آخر کد باید ()driver.quit می زدم که خارج شود و مرورگر بسته شود که در کد بالا نیست.مجموعه سلنیوم یک WebDriver دارد که برای اجرای برنامه نویسی اتوماسیون از آن استفاده می کنیم، مثل مثال بالا، یک IDE (Integrated Development Environment) دارد که یک ابزار برای توسعه test cases است و می تواند مثلاً اعمال شما را record کند که بعد همان را کد کنید و ... ، و یک Selenium Grid دارد که اجازه می دهد روی ماشین های مختلف و محیط های مختلف test case خودتان را اجرا کنید. اطلاعات بیشتر را اینجا مطالعه بفرمایید.