pythonseleniummathjaxexecute-script

How can I use selenium to record mathjax loading time


I am trying to use selenium get asynchronous elements(MathJax equation) loading time.

I try to write a python-selenium script to record the loading time of my website, but my website contains a lot of equation which converted by Mathjax asynchronous, so that I cannot record it correctly.

I try to use "performance.timing" to record the loading time first, but it only can provide me 'load time'.

from selenium import webdriver
source = "url"
driver = webdriver.Chrome()
driver.get(source)
navigationStart = driver.execute_script("return window.performance.timing.navigationStart")
loadEventEnd = driver.execute_script("return window.performance.timing.loadEventEnd")
load_time = loadEventEnd - navigationStart

Then, I try to locate the ID of "MathJax" and wait until one mathjax element (e.g "MathJax-Element-1-Frame") is loaded

from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
source = "url"
driver = webdriver.Chrome()
begin = time.time()
driver.get(source)
locator = (By.ID, 'MathJax-Element-1-Frame')
WebDriverWait(driver, 20, 0.5).until(EC.presence_of_element_located(locator))
end = time.time()
finish_time = end - begin

But the time is not absolutely correct.


Solution

  • Try using datetime.utcnow() with timedelta that is:

    A duration expressing the difference between two datetime instances to microsecond resolution.

    from datetime import datetime, timedelta
    from selenium import webdriver
    import time
    from selenium.webdriver.support.wait import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.common.by import By
    source = "url"
    driver = webdriver.Chrome()
    begin = datetime.utcnow() + timedelta(1)
    driver.get(source)
    locator = (By.ID, 'MathJax-Element-1-Frame')
    WebDriverWait(driver, 20, 0.5).until(EC.presence_of_element_located(locator))
    end = datetime.utcnow() + timedelta(1)
    finish_time = end - begin
    

    Update

    This is a function that waits for all pendingRequests to load. It might help in your case too.

    def wait_until_loaded(driver, seconds: int = 30) -> None:
        java_script_to_load = "var injector = window.angular.element('body').injector();"\
                                      " var $http = injector.get('$http');" \
                                      "return ($http.pendingRequests.length === 0);"
        end_time = datetime.utcnow() + timedelta(seconds=seconds)
        print("wait for All Elements....")
        while datetime.utcnow() <= end_time:
            try:
                if driver.execute_script(java_script_to_load):
                    print(f"loaded in"
                          f" {datetime.utcnow() + timedelta(seconds=seconds) - end_time}seconds")
                    sleep(1)
                    return
            except WebDriverException:
                continue
            sleep(0.1)
        raise TimeoutError("waiting for elements for too long")
    

    Hope this helps you!