Nick: improvements
This commit is contained in:
parent
2e264a4c75
commit
a5e718b084
@ -33,4 +33,6 @@ STRIPE_PRICE_ID_STANDARD=
|
|||||||
STRIPE_PRICE_ID_SCALE=
|
STRIPE_PRICE_ID_SCALE=
|
||||||
|
|
||||||
HYPERDX_API_KEY=
|
HYPERDX_API_KEY=
|
||||||
HDX_NODE_BETA_MODE=1
|
HDX_NODE_BETA_MODE=1
|
||||||
|
|
||||||
|
FIRE_ENGINE_BETA_URL= # set if you'd like to use the fire engine closed beta
|
42
apps/api/src/lib/load-testing-example.ts
Normal file
42
apps/api/src/lib/load-testing-example.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { scrapWithFireEngine } from "../../src/scraper/WebScraper/single_url";
|
||||||
|
|
||||||
|
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
const scrapInBatches = async (
|
||||||
|
urls: string[],
|
||||||
|
batchSize: number,
|
||||||
|
delayMs: number
|
||||||
|
) => {
|
||||||
|
let successCount = 0;
|
||||||
|
let errorCount = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < urls.length; i += batchSize) {
|
||||||
|
const batch = urls
|
||||||
|
.slice(i, i + batchSize)
|
||||||
|
.map((url) => scrapWithFireEngine(url));
|
||||||
|
try {
|
||||||
|
const results = await Promise.all(batch);
|
||||||
|
results.forEach((data, index) => {
|
||||||
|
if (data.trim() === "") {
|
||||||
|
errorCount++;
|
||||||
|
} else {
|
||||||
|
successCount++;
|
||||||
|
console.log(
|
||||||
|
`Scraping result ${i + index + 1}:`,
|
||||||
|
data.trim().substring(0, 20) + "..."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error during scraping:", error);
|
||||||
|
}
|
||||||
|
await delay(delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Total successful scrapes: ${successCount}`);
|
||||||
|
console.log(`Total errored scrapes: ${errorCount}`);
|
||||||
|
};
|
||||||
|
function run() {
|
||||||
|
const urls = Array.from({ length: 200 }, () => "https://scrapethissite.com");
|
||||||
|
scrapInBatches(urls, 10, 1000);
|
||||||
|
}
|
@ -10,6 +10,15 @@ import { fetchAndProcessPdf } from "./utils/pdfProcessor";
|
|||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
|
const baseScrapers = [
|
||||||
|
"fire-engine",
|
||||||
|
"scrapingBee",
|
||||||
|
"playwright",
|
||||||
|
"scrapingBeeLoad",
|
||||||
|
"fetch",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
|
||||||
export async function generateRequestParams(
|
export async function generateRequestParams(
|
||||||
url: string,
|
url: string,
|
||||||
wait_browser: string = "domcontentloaded",
|
wait_browser: string = "domcontentloaded",
|
||||||
@ -33,15 +42,39 @@ export async function generateRequestParams(
|
|||||||
return defaultParams;
|
return defaultParams;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function scrapWithCustomFirecrawl(
|
export async function scrapWithFireEngine(
|
||||||
url: string,
|
url: string,
|
||||||
options?: any
|
options?: any
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
// TODO: merge the custom firecrawl scraper into mono-repo when ready
|
const reqParams = await generateRequestParams(url);
|
||||||
return null;
|
const wait_playwright = reqParams["params"]?.wait ?? 0;
|
||||||
|
|
||||||
|
const response = await fetch(process.env.FIRE_ENGINE_BETA_URL+ "/scrape", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ url: url, wait: wait_playwright }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(
|
||||||
|
`[Fire-Engine] Error fetching url: ${url} with status: ${response.status}`
|
||||||
|
);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType = response.headers['content-type'];
|
||||||
|
if (contentType && contentType.includes('application/pdf')) {
|
||||||
|
return fetchAndProcessPdf(url);
|
||||||
|
} else {
|
||||||
|
const data = await response.json();
|
||||||
|
const html = data.content;
|
||||||
|
return html ?? "";
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error scraping with custom firecrawl-scraper: ${error}`);
|
console.error(`Error scraping with Fire Engine: ${error}`);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +96,7 @@ export async function scrapWithScrapingBee(
|
|||||||
|
|
||||||
if (response.status !== 200 && response.status !== 404) {
|
if (response.status !== 200 && response.status !== 404) {
|
||||||
console.error(
|
console.error(
|
||||||
`Scraping bee error in ${url} with status code ${response.status}`
|
`[ScrapingBee] Error fetching url: ${url} with status code ${response.status}`
|
||||||
);
|
);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -77,7 +110,7 @@ export async function scrapWithScrapingBee(
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error scraping with Scraping Bee: ${error}`);
|
console.error(`[ScrapingBee] Error fetching url: ${url} -> ${error}`);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,7 +130,7 @@ export async function scrapWithPlaywright(url: string): Promise<string> {
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.error(
|
console.error(
|
||||||
`Error fetching w/ playwright server -> URL: ${url} with status: ${response.status}`
|
`[Playwright] Error fetching url: ${url} with status: ${response.status}`
|
||||||
);
|
);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -111,11 +144,18 @@ export async function scrapWithPlaywright(url: string): Promise<string> {
|
|||||||
return html ?? "";
|
return html ?? "";
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error scraping with Puppeteer: ${error}`);
|
console.error(`Error scraping with Playwright: ${error}`);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getScrapingFallbackOrder(defaultScraper?: string) {
|
||||||
|
const fireEngineScraper = process.env.FIRE_ENGINE_BETA_URL ? ["fire-engine"] : [];
|
||||||
|
const uniqueScrapers = new Set(defaultScraper ? [defaultScraper, ...fireEngineScraper, ...baseScrapers] : [...fireEngineScraper, ...baseScrapers]);
|
||||||
|
const scrapersInOrder = Array.from(uniqueScrapers);
|
||||||
|
return scrapersInOrder as typeof baseScrapers[number][];
|
||||||
|
}
|
||||||
|
|
||||||
export async function scrapSingleUrl(
|
export async function scrapSingleUrl(
|
||||||
urlToScrap: string,
|
urlToScrap: string,
|
||||||
pageOptions: PageOptions = { onlyMainContent: true, includeHtml: false },
|
pageOptions: PageOptions = { onlyMainContent: true, includeHtml: false },
|
||||||
@ -137,17 +177,12 @@ export async function scrapSingleUrl(
|
|||||||
|
|
||||||
const attemptScraping = async (
|
const attemptScraping = async (
|
||||||
url: string,
|
url: string,
|
||||||
method:
|
method: typeof baseScrapers[number]
|
||||||
| "firecrawl-scraper"
|
|
||||||
| "scrapingBee"
|
|
||||||
| "playwright"
|
|
||||||
| "scrapingBeeLoad"
|
|
||||||
| "fetch"
|
|
||||||
) => {
|
) => {
|
||||||
let text = "";
|
let text = "";
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case "firecrawl-scraper":
|
case "fire-engine":
|
||||||
text = await scrapWithCustomFirecrawl(url);
|
text = await scrapWithFireEngine(url);
|
||||||
break;
|
break;
|
||||||
case "scrapingBee":
|
case "scrapingBee":
|
||||||
if (process.env.SCRAPING_BEE_API_KEY) {
|
if (process.env.SCRAPING_BEE_API_KEY) {
|
||||||
@ -205,15 +240,7 @@ export async function scrapSingleUrl(
|
|||||||
console.error(`Invalid URL key, trying: ${urlToScrap}`);
|
console.error(`Invalid URL key, trying: ${urlToScrap}`);
|
||||||
}
|
}
|
||||||
const defaultScraper = urlSpecificParams[urlKey]?.defaultScraper ?? "";
|
const defaultScraper = urlSpecificParams[urlKey]?.defaultScraper ?? "";
|
||||||
const scrapersInOrder = defaultScraper
|
const scrapersInOrder = getScrapingFallbackOrder(defaultScraper)
|
||||||
? [
|
|
||||||
defaultScraper,
|
|
||||||
"scrapingBee",
|
|
||||||
"playwright",
|
|
||||||
"scrapingBeeLoad",
|
|
||||||
"fetch",
|
|
||||||
]
|
|
||||||
: ["scrapingBee", "playwright", "scrapingBeeLoad", "fetch"];
|
|
||||||
|
|
||||||
for (const scraper of scrapersInOrder) {
|
for (const scraper of scrapersInOrder) {
|
||||||
// If exists text coming from crawler, use it
|
// If exists text coming from crawler, use it
|
||||||
@ -225,7 +252,10 @@ export async function scrapSingleUrl(
|
|||||||
}
|
}
|
||||||
[text, html] = await attemptScraping(urlToScrap, scraper);
|
[text, html] = await attemptScraping(urlToScrap, scraper);
|
||||||
if (text && text.trim().length >= 100) break;
|
if (text && text.trim().length >= 100) break;
|
||||||
console.log(`Falling back to ${scraper}`);
|
const nextScraperIndex = scrapersInOrder.indexOf(scraper) + 1;
|
||||||
|
if (nextScraperIndex < scrapersInOrder.length) {
|
||||||
|
console.info(`Falling back to ${scrapersInOrder[nextScraperIndex]}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
|
@ -63,7 +63,7 @@ export const urlSpecificParams = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ycombinator.com":{
|
"ycombinator.com":{
|
||||||
defaultScraper: "playwright",
|
defaultScraper: "fire-engine",
|
||||||
params: {
|
params: {
|
||||||
wait_browser: "networkidle2",
|
wait_browser: "networkidle2",
|
||||||
block_resources: false,
|
block_resources: false,
|
||||||
|
Loading…
Reference in New Issue
Block a user