pythonselenium-webdriverpowerbi

How to render Power BI slicer items with Python Selenium


I'd like to scrape the second page of this Power BI dashboard. In order to get the data from a specific month, I must set the date in a slicer:

Date slicer

However, when I expand a year element to display the month elements, some are out of sight and thus unrendered. Therefore, I must scroll down the slicer. However, I have yet to find a method that works.

Before I give further details, this is how I get to the desired page and expand the slicer:

# Selenium  resources
from selenium import webdriver
from selenium.webdriver.edge.options import Options
from selenium.webdriver.common.by import By

# Driver service
driver_file = r"d:\dev\selenium\msedgedriver.exe" # or whatever driver is available

service = Service(driver_file)

# Browser options
options = Options()
options.add_experimental_option("detach", True)
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("--inprivate")

# Open browser
driver = webdriver.Edge(service = service, options = options)

# Get URL
url = "https://app.powerbi.com/view?r=eyJrIjoiZWIzNDg3YzUtMGFlMC00MzdmLTgzOWQtZThkOWExNTU2NjBlIiwidCI6IjQ0OTlmNGZmLTI0YTYtNGI0Mi1iN2VmLTEyNGFmY2FkYzkxMyJ9"

driver.get(url)

# Proceed to next page
driver.find_element(By.XPATH, '//button[@aria-label="Próxima Página"]/i').click()

# Open date slicer
driver.find_element(By.XPATH, '//div[@class="slicer-dropdown-menu"]/i')

# Expand month options for a year, e.g. 2024
(driver \
.find_element(By.XPATH, '//div[@class="slicerItemContainer" and @title="2024"]/div[@class="expandButton"]') \
.click())

And so:

Expanded slicer

But as it stands, I cannot select any month beyond March.

The slicer doesn't budge when I execute a JavaScript snippet:

slicer_container = driver.find_element(By.XPATH, '//div[@class="slicerContainer"]')
driver.execute_script("arguments[0].scrollTop = arguments[0].scrollHeight", slicer_container)

Trying to scroll down with keys throws an ElementNotInteractableException:

from selenium.webdriver.common.keys import Keys
scroll_container = driver.find_element(By.CLASS_NAME, "scroll-bar")
scroll_container.send_keys(Keys.DOWN)

ElementNotInteractableException: Message: element not interactable (Session info: MicrosoftEdge=135.0.3179.85)

ActionChains yield the same error:

from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys

actions = ActionChains(driver)
actions.move_to_element(scroll_bar).click().send_keys(Keys.PAGE_DOWN).perform()

ElementNotInteractableException: Message: element not interactable (Session info: MicrosoftEdge=135.0.3179.85)

And finally, I've tried to use a style transform – but it only manages to scroll the element visually and doesn't trigger the rendering of the next slicer items.

visible_group = driver.find_element(By.CLASS_NAME, 'visibleGroup')
driver.execute_script(f'arguments[0].style.transform = "translateY(-60px)";', visible_group)

enter image description here

All told, I have no idea what to do. Any ideas? Please feel free to ask for further details.


Solution

  • ActionChains with scrolling via move_to_element, click_and_hold, and move_by_offset, It is key when interacting with complex UIs like Power BI embedded dashboards or custom dropdowns.

    move_to_element(): Ensures you're hovering over the correct scrollable area.

    click_and_hold(): Simulates a mouse drag. Without this, the scroll may not happen.

    move_by_offset(0, 100): Moves the view vertically (down). Can be adjusted for a larger scroll.

    release(): Finishes the simulated mouse action.

    Here's a working code that I've tested:

    # ===== IMPORTS =====
    from time import sleep
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver import ActionChains
    
    # ===== SETUP OPTIONS =====
    options = Options()
    options.add_argument("--start-maximized")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_argument("force-device-scale-factor=0.95")
    
    driver = webdriver.Chrome(options=options)
    wait = WebDriverWait(driver, 10)
    
    
    def report_analyser(year: str, month: int) -> None:
        url = "https://app.powerbi.com/view?r=eyJrIjoiZWIzNDg3YzUtMGFlMC00MzdmLTgzOWQtZThkOWExNTU2NjBlIiwidCI6IjQ0OTlmNGZmLTI0YTYtNGI0Mi1iN2VmLTEyNGFmY2FkYzkxMyJ9"
        driver.get(url)
    
        # Wait for the page navigation element
        wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, 'div[aria-label="Mercado Page navigation . Mercado"]')))
    
        # Click to go to second page
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#embedWrapperID>div.logoBarWrapper>logo-bar>div>div>div>logo-bar-navigation>span>button:nth-child(3)'))).click()
    
        # Click to open the date drop-down
        wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#pvExplorationHost > div > div > exploration > div > explore-canvas > div > div.canvasFlexBox > div > div.displayArea.disableAnimations.fitToPage > div.visualContainerHost.visualContainerOutOfFocus > visual-container-repeat > visual-container:nth-child(6) > transform > div > div.visualContent > div > div > visual-modern > div > div > div.slicer-content-wrapper > div>i'))).click()
    
        # Expand month options for 2023
        wait.until(EC.presence_of_element_located((By.XPATH, f'//div[@class="slicerItemContainer" and @title="{year}"]/div[@class="expandButton"]'))).click()
    
        sleep(3)  # Let dropdown finish animating
        sc = driver.find_element(By.CSS_SELECTOR, 'div[id^="slicer-dropdown-popup-"]>div>div>div:nth-child(2)>div>div:nth-child(3)')
    
        # Scroll the slicer container
        action_chains = ActionChains(driver)
        action_chains.move_to_element(sc).click_and_hold().move_by_offset(0, 100).release().perform()
        sleep(2)
    
        driver.find_element(By.XPATH, f'//div[@class="slicerItemContainer" and @aria-posinset="{month}"]').click()
        sleep(2)
    
    
    report_analyser('2022', 1)
    

    Visual output:

    https://github.com/Help-the-community/Web_Scraping_with_Selenium/blob/main/app_powerbi_com_anp.gif

    you can check here for a cleaner version of the code: https://github.com/Help-the-community/Web_Scraping_with_Selenium/blob/main/app_powerbi_com_anp.py