| const { v4 } = require("uuid"); |
| const { |
| PuppeteerWebBaseLoader, |
| } = require("langchain/document_loaders/web/puppeteer"); |
| const { writeToServerDocuments } = require("../../utils/files"); |
| const { tokenizeString } = require("../../utils/tokenizer"); |
| const { default: slugify } = require("slugify"); |
| const RuntimeSettings = require("../../utils/runtimeSettings"); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| async function scrapeGenericUrl({ |
| link, |
| captureAs = "text", |
| processAsDocument = true, |
| scraperHeaders = {}, |
| metadata = {}, |
| }) { |
| console.log(`-- Working URL ${link} => (${captureAs}) --`); |
| const content = await getPageContent({ |
| link, |
| captureAs, |
| headers: scraperHeaders, |
| }); |
|
|
| if (!content.length) { |
| console.error(`Resulting URL content was empty at ${link}.`); |
| return { |
| success: false, |
| reason: `No URL content found at ${link}.`, |
| documents: [], |
| }; |
| } |
|
|
| if (!processAsDocument) { |
| return { |
| success: true, |
| content, |
| }; |
| } |
|
|
| const url = new URL(link); |
| const decodedPathname = decodeURIComponent(url.pathname); |
| const filename = `${url.hostname}${decodedPathname.replace(/\//g, "_")}`; |
|
|
| const data = { |
| id: v4(), |
| url: "file://" + slugify(filename) + ".html", |
| title: metadata.title || slugify(filename) + ".html", |
| docAuthor: metadata.docAuthor || "no author found", |
| description: metadata.description || "No description found.", |
| docSource: metadata.docSource || "URL link uploaded by the user.", |
| chunkSource: `link://${link}`, |
| published: new Date().toLocaleString(), |
| wordCount: content.split(" ").length, |
| pageContent: content, |
| token_count_estimate: tokenizeString(content), |
| }; |
|
|
| const document = writeToServerDocuments({ |
| data, |
| filename: `url-${slugify(filename)}-${data.id}`, |
| }); |
| console.log(`[SUCCESS]: URL ${link} converted & ready for embedding.\n`); |
| return { success: true, reason: null, documents: [document] }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| function validatedHeaders(headers = {}) { |
| try { |
| if (Object.keys(headers).length === 0) return {}; |
| let validHeaders = {}; |
| for (const key of Object.keys(headers)) { |
| if (!key?.trim()) continue; |
| if (typeof headers[key] !== "string" || !headers[key]?.trim()) continue; |
| validHeaders[key] = headers[key].trim(); |
| } |
| return validHeaders; |
| } catch (error) { |
| console.error("Error validating headers", error); |
| return {}; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| async function getPageContent({ link, captureAs = "text", headers = {} }) { |
| try { |
| let pageContents = []; |
| const runtimeSettings = new RuntimeSettings(); |
| const loader = new PuppeteerWebBaseLoader(link, { |
| launchOptions: { |
| headless: "new", |
| ignoreHTTPSErrors: true, |
| args: runtimeSettings.get("browserLaunchArgs"), |
| }, |
| gotoOptions: { |
| waitUntil: "networkidle2", |
| }, |
| async evaluate(page, browser) { |
| const result = await page.evaluate((captureAs) => { |
| if (captureAs === "text") return document.body.innerText; |
| if (captureAs === "html") return document.documentElement.innerHTML; |
| return document.body.innerText; |
| }, captureAs); |
| await browser.close(); |
| return result; |
| }, |
| }); |
|
|
| |
| let overrideHeaders = validatedHeaders(headers); |
| if (Object.keys(overrideHeaders).length > 0) { |
| loader.scrape = async function () { |
| const { launch } = await PuppeteerWebBaseLoader.imports(); |
| const browser = await launch({ |
| headless: "new", |
| defaultViewport: null, |
| ignoreDefaultArgs: ["--disable-extensions"], |
| ...this.options?.launchOptions, |
| }); |
| const page = await browser.newPage(); |
| await page.setExtraHTTPHeaders(overrideHeaders); |
|
|
| await page.goto(this.webPath, { |
| timeout: 180000, |
| waitUntil: "networkidle2", |
| ...this.options?.gotoOptions, |
| }); |
|
|
| const bodyHTML = this.options?.evaluate |
| ? await this.options.evaluate(page, browser) |
| : await page.evaluate(() => document.body.innerHTML); |
|
|
| await browser.close(); |
| return bodyHTML; |
| }; |
| } |
|
|
| const docs = await loader.load(); |
| for (const doc of docs) pageContents.push(doc.pageContent); |
| return pageContents.join(" "); |
| } catch (error) { |
| console.error( |
| "getPageContent failed to be fetched by puppeteer - falling back to fetch!", |
| error |
| ); |
| } |
|
|
| try { |
| const pageText = await fetch(link, { |
| method: "GET", |
| headers: { |
| "Content-Type": "text/plain", |
| "User-Agent": |
| "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36,gzip(gfe)", |
| ...validatedHeaders(headers), |
| }, |
| }).then((res) => res.text()); |
| return pageText; |
| } catch (error) { |
| console.error("getPageContent failed to be fetched by any method.", error); |
| } |
|
|
| return null; |
| } |
|
|
| module.exports = { |
| scrapeGenericUrl, |
| }; |
|
|