RPA bot fails to extract data from dynamic web tables during order processing

Our RPA bot extracts order data from a supplier’s web portal three times daily. The bot worked reliably for 6 months, but now fails to extract data from their order table about 40% of the time, resulting in incomplete records in Creatio.

The supplier’s portal uses dynamic tables where column positions change based on available data. Sometimes the ‘Order Status’ column is in position 3, other times position 5. Our bot was using fixed column indexes:


table.row[i].cell[3].text  // Expected Order Status
table.row[i].cell[4].text  // Expected Delivery Date

When columns shift, we extract the wrong data or the bot crashes with index out of bounds errors. The supplier mentioned they’re continuously updating their UI for better mobile responsiveness, so column positions will keep changing.

How do you handle dynamic table parsing in RPA bots when the UI structure isn’t stable? We need header-based mapping that’s resilient to UI changes.

Yes, use the ‘Execute JavaScript’ action in your RPA bot workflow. It executes in the context of the current web page, so you have full DOM access. However, make sure you’re using explicit waits before executing the script - dynamic tables often load asynchronously, and if you try to parse before the data is fully loaded, you’ll get incomplete results. Add a wait condition that checks for a specific element or table row count before running your extraction script. This is especially important if the supplier’s portal uses AJAX to populate the table.

Fixed column indexes are fragile for exactly this reason. You need to extract the table headers first, then map column names to indexes dynamically. Read the header row to build a mapping dictionary like {‘Order Status’: 3, ‘Delivery Date’: 4}, then use those mappings to extract data from subsequent rows. This way when columns shift, your mapping updates automatically.

Here’s a comprehensive solution that addresses dynamic table parsing, header-based mapping, and UI change resilience for your RPA bot.

Dynamic Table Parsing with Header-Based Mapping:

Replace your fixed column index approach with dynamic header mapping. Add an ‘Execute JavaScript’ action in your RPA bot workflow after navigating to the order table:

// Build dynamic column mapping from headers
var headers = document.querySelectorAll('table.orders thead th');
var columnMap = {};
headers.forEach((header, index) => {
  var headerText = header.textContent.trim().toLowerCase();
  columnMap[headerText] = index;
});

// Extract data using mapped indexes
var rows = document.querySelectorAll('table.orders tbody tr');
var orders = [];
rows.forEach(row => {
  var cells = row.querySelectorAll('td');
  orders.push({
    status: cells[columnMap['order status']]?.textContent.trim(),
    deliveryDate: cells[columnMap['delivery date']]?.textContent.trim(),
    orderNumber: cells[columnMap['order #']]?.textContent.trim()
  });
});

return JSON.stringify(orders);

This script builds a mapping from header text to column index, then uses that mapping to extract data regardless of column position changes.

Header Text Variations and Resilience:

To handle header text changes (“Order Status” vs “Status” vs “Current Status”), implement flexible matching:

function findColumnIndex(headers, searchTerms) {
  for (let i = 0; i < headers.length; i++) {
    var headerText = headers[i].textContent.trim().toLowerCase();
    for (let term of searchTerms) {
      if (headerText.includes(term.toLowerCase())) {
        return i;
      }
    }
  }
  return -1;
}

var statusIndex = findColumnIndex(headers, ['order status', 'status', 'current status']);
var dateIndex = findColumnIndex(headers, ['delivery date', 'delivery', 'ship date']);

This searches for multiple possible header variations, making your bot resilient to minor UI text changes.

Handling Asynchronous Table Loading:

Before executing the extraction script, add a ‘Wait for Element’ action that ensures the table is fully loaded:

  1. Add ‘Wait for Element’ action with condition: table.orders tbody tr exists and count > 0
  2. Set timeout to 30 seconds
  3. Only then execute the JavaScript extraction

This prevents incomplete data extraction when the table loads asynchronously via AJAX.

UI Change Resilience Strategy:

Since the supplier is continuously updating for mobile responsiveness, implement these defensive patterns:

1. Multiple Table Selectors: Use a fallback chain for table selection:

var table = document.querySelector('table.orders') ||
            document.querySelector('table#order-list') ||
            document.querySelector('div.order-data table');

2. Validation and Error Handling: After extraction, validate that critical fields were found:

if (statusIndex === -1 || dateIndex === -1) {
  throw new Error('Critical columns not found. Table structure may have changed.');
}

This triggers the bot’s error handling workflow to notify your team immediately when table structure changes significantly.

3. Structure Fingerprinting: Log the detected column structure each run:

var structure = Array.from(headers).map(h => h.textContent.trim()).join('|');
console.log('Table structure: ' + structure);

Store this in Creatio logs. When extraction starts failing, compare recent structure logs to identify what changed.

Bot Workflow Configuration:

Update your RPA bot workflow sequence:

  1. Navigate to Portal (existing web recorder action)
  2. Wait for Table Load (new: explicit wait for table element)
  3. Extract Column Structure (new: JavaScript to build header mapping)
  4. Validate Structure (new: check critical columns exist)
  5. Extract Order Data (modified: use dynamic column mapping)
  6. Transform to Creatio Format (existing: map to Creatio order objects)
  7. Import to Creatio (existing: bulk insert action)

Add error handling at step 4: if validation fails, send notification to RPA team and log the current table structure for analysis.

Testing and Monitoring:

Implement continuous monitoring:

  1. Dry Run Mode: Add a bot parameter for dry-run mode that extracts and logs data without importing. Run this hourly to detect structure changes early.

  2. Structure Change Detection: Compare the column structure fingerprint from each run. If it differs from the previous run, send an alert even if extraction succeeds.

  3. Extraction Quality Metrics: Track the percentage of rows where all critical fields were successfully extracted. Alert if this drops below 95%.

Why This Approach Works:

  • Header-based mapping eliminates dependency on fixed column positions
  • Flexible string matching handles minor header text variations
  • Explicit waits ensure data is fully loaded before extraction
  • Structure validation catches breaking changes immediately
  • Monitoring provides early warning of UI changes

This solution transforms your bot from brittle fixed-index extraction to a resilient system that adapts to UI changes. The 40% failure rate should drop to near-zero, and when the supplier makes breaking changes, you’ll be notified immediately with enough diagnostic data to adapt quickly.

One more consideration: even with header-based mapping, watch out for header text variations. Suppliers sometimes change ‘Order Status’ to ‘Status’ or ‘Current Status’. Use flexible string matching (contains, trim, lowercase) when building your header mapping. Also, the supplier mentioned ongoing UI updates for mobile responsiveness - this could mean they’re using responsive tables that completely restructure on different viewport sizes. Consider whether your bot needs to handle multiple table layouts.

The web recorder in Creatio 7.18 has limitations for complex table parsing. I’d recommend switching to custom JavaScript execution for the table extraction part. You can use XPath to locate the table, extract headers, build your mapping, then iterate through rows. This gives you full control over the parsing logic and makes it easy to handle dynamic structures. The recorder is fine for navigation and clicking, but use JavaScript for data extraction from complex tables.

That makes sense. So I’d parse the header row once at the start, build the column mapping, then iterate through data rows using the mapped indexes. Do you have an example of how to implement this in Creatio’s RPA module? I’m using the web recorder actions to interact with the table.