Skip to content

Instantly share code, notes, and snippets.

@DungGramer
Last active April 28, 2025 04:02
Show Gist options
  • Save DungGramer/03b8820ea2b2b5430c8b1c8b3b23959d to your computer and use it in GitHub Desktop.
Save DungGramer/03b8820ea2b2b5430c8b1c8b3b23959d to your computer and use it in GitHub Desktop.
Get React content return to API
const response = await fetch('/api/extract-react-content', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
url:
iframe.url,
key: conversation.text.substring(key + 1, keyEnd) || '',
loginInfo: {
loginUrl: 'https://example.com'
'usernameSelector': '#username',
'passwordSelector': '#password',
'submitSelector': '#kc-login',
'username': 'username',
'password': 'password',
'waitForNavigation': true,
},
}),
});
// app/api/extract-react-content/route.ts
import { NextResponse } from 'next/server';
import * as cheerio from 'cheerio';
import puppeteer from 'puppeteer';
async function launchBrowser() {
// return await puppeteer.launch({ headless: true });
return await puppeteer.launch({
headless: false, // Show the browser
slowMo: 50, // Slow down each operation by 50ms (optional, easier to see typing)
devtools: true, // Open Chrome DevTools automatically (optional)
args: ['--start-maximized'] // Start maximized window
});
}
async function loginIfNeeded(page, loginInfo) {
if (!loginInfo) return;
const {
loginUrl,
usernameSelector,
passwordSelector,
submitSelector,
username,
password,
waitForNavigation = true,
} = loginInfo;
if (
!loginUrl ||
!usernameSelector ||
!passwordSelector ||
!submitSelector ||
!username ||
!password
) {
throw new Error('Incomplete login information provided');
}
// Go to the login page
await page.goto(loginUrl, { waitUntil: 'networkidle2', timeout: 30000 });
// Type username and password
await page.type(usernameSelector, username, { delay: 100 });
await page.type(passwordSelector, password, { delay: 100 });
// Click login button
await Promise.all([
page.click(submitSelector),
waitForNavigation
? page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 30000 })
: Promise.resolve(),
]);
}
export async function GET(request) {
try {
const { searchParams } = new URL(request.url);
const url = searchParams.get('url');
const key = searchParams.get('key');
if (!url) {
return NextResponse.json({ error: 'URL parameter is required' }, { status: 400 });
}
if (!key) {
return NextResponse.json({ error: 'Key parameter is required' }, { status: 400 });
}
const browser = await launchBrowser();
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
await new Promise(resolve => setTimeout(resolve, 2000));
const renderedHtml = await page.content();
await browser.close();
const $ = cheerio.load(renderedHtml);
let value = null;
$('.label-item').each((_, element) => {
const keyElement = $(element).find('.lb-key');
if (keyElement.text() === key) {
value = $(element).find('.lb-value').text();
return false;
}
});
if (value === null) {
return NextResponse.json({ error: `No label found with key "${key}"` }, { status: 404 });
}
return NextResponse.json({ key, value });
} catch (error) {
console.error('Error processing React content:', error);
return NextResponse.json({ error: 'Failed to process the React content' }, { status: 500 });
}
}
export async function POST(request) {
try {
const { url, key, selector, loginInfo } = await request.json();
if (!url) {
return NextResponse.json({ error: 'URL parameter is required' }, { status: 400 });
}
const browser = await launchBrowser();
const page = await browser.newPage();
// If login info provided, login first
await loginIfNeeded(page, loginInfo);
// Navigate to target page after login
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
await new Promise(resolve => setTimeout(resolve, 2000));
let result;
if (selector) {
await page.waitForSelector(selector, { timeout: 5000 }).catch(() => {
console.warn(`Selector ${selector} not found`);
});
result = await page.evaluate((sel) => {
const element = document.querySelector(sel);
return element ? element.textContent : null;
}, selector);
} else if (key) {
result = await page.evaluate((labelKey) => {
const labelItems = document.querySelectorAll('.label-item');
for (const item of labelItems) {
const keyElement = item.querySelector('.lb-key');
if (keyElement && keyElement.textContent === labelKey) {
const valueElement = item.querySelector('.lb-value');
return valueElement ? valueElement.textContent : null;
}
}
return null;
}, key);
} else {
result = await page.content();
}
await browser.close();
if (result === null) {
return NextResponse.json(
{
error: selector
? `No content found for selector "${selector}"`
: `No label found with key "${key}"`,
},
{ status: 404 }
);
}
return NextResponse.json({ content: result });
} catch (error) {
console.error('Error extracting React content:', error);
return NextResponse.json(
{ error: 'Failed to extract content from React application' },
{ status: 500 }
);
}
}
// app/api/extract-label/route.ts
import { NextResponse, type NextRequest } from 'next/server';
import * as cheerio from 'cheerio';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const url = searchParams.get('url');
if (!url) {
return NextResponse.json({ error: 'URL parameter is required' }, { status: 400 });
}
try {
// Fetch the external page
const response = await fetch(url);
const html = await response.text();
console.log(`📕 html - 17:route.ts \n`, html);
const $ = cheerio.load(html);
const title = $('title').text();
const paragraphs = $('p')
.map((i, el) => $(el).text())
.get();
const headings = $('h1, h2, h3')
.map((i, el) => $(el).text())
.get();
// Return the HTML content
return NextResponse.json({ content: html, title, paragraphs, headings });
} catch (error) {
console.error('Error fetching iframe content:', error);
return NextResponse.json({ error: 'Failed to fetch content' }, { status: 500 });
}
}
export async function POST(request) {
try {
const { url, html, key } = await request.json();
if (!key) {
return NextResponse.json({ error: 'The "key" parameter is required' }, { status: 400 });
}
let htmlContent = html;
// If URL is provided, fetch the HTML content
if (url && !html) {
const response = await fetch(url);
htmlContent = await response.text();
}
if (!htmlContent) {
return NextResponse.json(
{ error: 'Either "url" or "html" must be provided' },
{ status: 400 }
);
}
// Parse HTML with Cheerio
const $ = cheerio.load(htmlContent);
// Find the label-item div that contains the specified key
let value = null;
$('.label-item').each((_, element) => {
const keyElement = $(element).find('.lb-key');
if (keyElement.text() === key) {
value = $(element).find('.lb-value').text();
return false; // Break the loop if found
}
});
if (value === null) {
return NextResponse.json({ error: `No label found with key "${key}"` }, { status: 404 });
}
return NextResponse.json({ key, value });
} catch (error) {
console.error('Error processing HTML:', error);
return NextResponse.json({ error: 'Failed to process the content' }, { status: 500 });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment