wordpressselenium-webdriverwordpress-gutenberg

How to use Python to Publish a New Postings on Wordpress?


I'm trying to use Python to PUBLISH and EDIT a new posting on wordpress.com?

I'm able to login with my username and password and also get to the new wordpress.com posting page loaded using the following SELENIUM PYTHON code.

However for some reason i'm not able to select the title field... is there an easier way to get the CSS SELECTOR? or element id? it maybe that its being created dynamically and wont show up "normally?" Here is the bug in the code?

title_field = WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'h1.wp-block')))
        enter_text(title_field, title)

here is the full python code i'm using...

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

def list_my_sites(driver):
    try:
        # Navigate to "My Sites" after successful login
        my_sites_link = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "a.masterbar__item:nth-child(2)"))
        )
        my_sites_link.click()

        # Wait for the table to be present (adjust timeout as needed)
        table_body = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, ".dataviews-view-table > tbody"))  # Use a more general selector for the table body
        )

        # Get all rows in the table body
        rows = table_body.find_elements(By.TAG_NAME, "tr")


        first_column_data = []
        for row in rows:
            try:  # Handle potential errors if a row doesn't have enough columns
                first_cell = row.find_element(By.CSS_SELECTOR, "td:nth-child(1)")  # Get the first cell (column) in each row
                first_column_data.append(first_cell.text) # extract the text from that cell
            except: #NoSuchElementException:
                print("Skipping a row, its missing a first cell")

        print (first_column_data)

    except Exception as e:
        print(f"Error listing sites: {e}")


def create_and_publish_post(driver, title, content, tags=None, category=None, featured_image_path=None):
    def enter_text(element, text):  # Helper function for reliable text entry
        element.click()
        action = webdriver.ActionChains(driver)
        action.send_keys(text).perform()

    try:
        # 1. Navigate to "Add New Post"
        driver.get(website + "/wp-admin/post-new.php")  # Or your "Add New" URL

        # 2. Enter title
        title_field = WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'h1.wp-block')))
        enter_text(title_field, title)

        # 3. Enter content (Gutenberg)
        content_field = WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".block-editor-rich-text__editable")))
        enter_text(content_field, content)


        # 4. Add tags (if provided)
        if tags:
            tag_field = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "tags-input")))
            for tag in tags:
                enter_text(tag_field, tag)
                tag_field.send_keys(Keys.ENTER)

        # 5. Add category (if provided)  # New feature!
        if category:
            category_field = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.ID, "category-add-toggle")) # This will vary a lot across sites, INSPECT YOURS.
            )
            category_field.click() # click on the button first

            category_input_field = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.ID, "new-category-name"))  # sometimes there is an input for adding new categories
            )

            enter_text(category_input_field, category) # type in the category you want
            category_input_field.send_keys(Keys.ENTER) # select the newly added category


        # 6. Add featured image (if provided) # New feature!
        if featured_image_path:
            featured_image_button = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, ".editor-post-featured-image__toggle")) #  Click to open the Featured Image settings
            ).click()


            upload_button = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, ".components-button.editor-post-featured-image__upload-button"))  #  This one is tricky, it tends to have a dynamic ID, better to use a more broad selector
            )
            upload_button.send_keys(featured_image_path)  # Send the path directly to the upload button



        # 7. Publish
        WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".editor-post-publish-panel__toggle"))).click()  # Open publish panel
        WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".editor-post-publish-button"))).click()  # Publish

        print("Post published successfully!")

    except Exception as e:
        print(f"Error publishing post: {e}")

def wordpress_login(username, password):
    try:
        # Initialize the WebDriver (replace with your browser's driver)
        driver = webdriver.Firefox()  # Or webdriver.Chrome(), etc.

        # Go to the WordPress.com login page
        driver.get("https://wordpress.com/log-in")

        # Wait for the username field to be visible and enter the username
        username_field = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.ID, "usernameOrEmail"))
        )
        username_field.send_keys(username)

        # Continue button
        continue_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
        continue_button.click()

        # Wait for password field after continue button
        password_field = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.ID, "password"))
        )
        password_field.send_keys(password)


        # Log in button
        log_in_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
        log_in_button.click()

        # Add a wait to ensure the login is complete (adjust as needed)
        time.sleep(5)  # You might need a more robust check here

        # Check for successful login (example, adjust based on your site's structure)
        # You might check for a specific element on the logged-in page
        try:
          check_button = driver.find_element(By.CSS_SELECTOR, "a.masterbar__item:nth-child(2) > svg:nth-child(1) > g:nth-child(1) > path:nth-child(1)")
          print("Login successful!")
        except:
            print("Login failed. Check credentials or website structure.")

        #list_my_sites(driver)

        create_and_publish_post(driver, "hello!", "world!!!")

    except Exception as e:
        print(f"An error occurred: {e}")

    finally:
        # Close the browser window
        # driver.quit()
        print("all done?!")

if __name__ == "__main__":
    username = "username"  # Replace with your username
    password = "password"  # Replace with your password
    website = "https://ashercmartin.wordpress.com"  # Replace with your wordpress website
    wordpress_login(username, password)

Solution

  • I wasn't able to find the precise CSS SELECTOR however i was able to publish a posting with this code. One possible reason is a WORDPRESS shadow DOM???

    def create_and_publish_post(driver, title, content, tags=None, category=None, featured_image_path=None):
        
        def enter_text(element, text):  # Helper function for reliable text entry
            element.click()
            action = webdriver.ActionChains(driver)
            action.send_keys(text).perform()
    
        try:
            # 1. Navigate to "Add New Post"
            driver.get(website + "/wp-admin/post-new.php")  # Or your "Add New" URL
            time.sleep(2)
    
            #works! add a new block!
            #new_block = WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.components-button > svg:nth-child(1)')))
            #new_block.click()
            action = webdriver.ActionChains(driver)
            #action.send_keys(title + " " + content).perform()
            #action.send_keys(Keys.ENTER).perform()
            #action.send_keys("whats up!!!").perform()
    
            #change format style to html / code
            select = WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div.components-dropdown:nth-child(6) > button:nth-child(1) > svg:nth-child(1)"))).click()  # Open publish panel
    
            #make sure code edit is selected button.components-menu-items-choice:nth-child(2) > span:nth-child(1)
            select_code = WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.components-menu-items-choice:nth-child(2) > span:nth-child(1)"))).click()  # Open publish panel
    
            #update title #inspector-textarea-control-0
            select_title = WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.ID, "inspector-textarea-control-0"))).click()  # Open publish panel
            action.send_keys("awesome title!").perform()
    
            #select text / html #post-content-2
            select_content = WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.ID, "post-content-0"))).click()  # Open publish panel
            action.send_keys("whats up!!!").perform()
    
            # 7. Publish button.components-button:nth-child(5)
            WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".editor-post-publish-panel__toggle"))).click()  # Open publish panel
            WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".editor-post-publish-button"))).click()  # Publish
    
            print("Post published successfully!")
    
        except Exception as e:
            print(f"Error publishing post: {e}")
    

    thanks for the help! what i needed to do was change to HTML/CODE view and then select the details from there ... for some reason the default view i couldn't use a valid CSS SELECTOR.

    ;)