Skip to content

Instantly share code, notes, and snippets.

@jeznag
Created August 24, 2024 05:13
Show Gist options
  • Save jeznag/fbdec58a942e5c66a809b1b3eceb4689 to your computer and use it in GitHub Desktop.
Save jeznag/fbdec58a942e5c66a809b1b3eceb4689 to your computer and use it in GitHub Desktop.
Downloading airtable data if there's no export button
// Run this in chrome dev tools
// Only helpful for one page worth of data
// Look at an API integration if you need to capture more data than that
(async function() {
const headerMap = {};
const headerArray = ['Row Index']; // Add Row Index as the first column
const rowMap = {};
// Function to extract and quote text content from a cell
function getCellText(cell) {
let text = cell.innerText.trim();
// If the text contains a comma or a newline, wrap it in double quotes
if (text.includes(',') || text.includes('\n')) {
text = `"${text}"`;
}
return text;
}
// Function to process the currently visible rows
function processVisibleRows() {
const rows = document.querySelectorAll('.dataRow');
rows.forEach(row => {
const rowId = row.getAttribute('data-rowid');
const firstCell = row.querySelector('.cell[data-rowindex]');
const rowIndex = firstCell ? firstCell.getAttribute('data-rowindex') : '';
if (!rowMap[rowId]) {
rowMap[rowId] = { 'Row Index': rowIndex };
// Initialize the row with empty strings for each column
headerArray.forEach(header => {
if (header !== 'Row Index') { // Row Index already added
rowMap[rowId][header] = '';
}
});
}
// Fill rowMap with data from each cell
const cells = row.querySelectorAll('.cell[data-columnid]');
cells.forEach(cell => {
const cellText = getCellText(cell);
const columnName = headerMap[cell.getAttribute('data-columnid')];
rowMap[rowId][columnName] = cellText;
});
});
console.log(`Processed ${rows.length} rows in this iteration.`);
}
// Function to capture headers
function captureHeaders() {
const headers = document.querySelectorAll('.gridHeaderCellPhosphorIcons');
headers.forEach(header => {
const columnId = header.getAttribute('data-columnid');
const headerText = getCellText(header);
headerMap[columnId] = headerText;
if (!headerArray.includes(headerText)) {
headerArray.push(headerText);
}
});
}
// Function to prompt the user to scroll and continue processing
function promptToScroll() {
// Prompt the user to scroll
const shouldContinue = confirm("Scroll the page down and then click 'OK' to continue. Click 'Cancel' to stop.");
if (shouldContinue) {
// Capture visible rows
processVisibleRows();
// Prompt again after a delay
setTimeout(promptToScroll, 1000); // Wait for 1 second before prompting again
} else {
console.log('Scrolling and processing stopped.');
finalizeData();
}
}
// Function to finalize the data and create the CSV
function finalizeData() {
const data = [];
// Convert the rowMap object to an array of rows in the correct order, sorted by Row Index
const sortedRows = Object.values(rowMap).sort((a, b) => a['Row Index'] - b['Row Index']);
sortedRows.forEach(rowObject => {
const rowArray = headerArray.map(header => rowObject[header]);
data.push(rowArray);
});
// Add the headers as the first row
data.unshift(headerArray);
// Convert the data array to CSV format
const csvContent = data.map(e => e.join(",")).join("\n");
// Create a Blob from the CSV data
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
// Create a download link and click it programmatically
const link = document.createElement("a");
const url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", "airtable_data.csv");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
console.log('CSV file has been generated and downloaded.');
}
// Start by capturing the headers
captureHeaders();
// Start the interactive scroll prompts
promptToScroll();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment