我正在尝试创建一个 Node.js API,用于抓取网站(我一开始只使用 Goodreads 作为要抓取的网站,并且当 我第一次优化该方法时会进一步扩展) 并将抓取的数据提供给最终用户,将使用我的 API。
我最初的方法是规划API结构,决定使用puppeteer,然后开始创建。成功创建第一个端点时,我注意到一些事情 - 在 Postman 中大约需要 2-3 秒才能完成请求,这非常慢。
这是我的代码:
scraper-handler.ts
import { NextFunction, Request, Response } from "express";
import { MOST_POPULAR_LISTS } from "../utils/api/urls-endpoints.js";
import { listScraper } from "./spec-scrapers/list-scraper.js";
import { lists } from "../utils/api/full-urls.js";
import puppeteer from "puppeteer";
import { GOODREADS_POPULAR_LISTS_URL } from "../utils/goodreads/urls.js";
export const scraperHandler = async (
req: Request,
res: Response,
next: NextFunction
) => {
const browser = await puppeteer.launch({
// headless: false,
// defaultViewport: null,
});
const pages = await browser.pages();
await pages[0].setUserAgent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"
);
switch (req.url) {
case `/${MOST_POPULAR_LISTS}`: {
const result = await listScraper(
browser,
pages[0],
GOODREADS_POPULAR_LISTS_URL,
1,
".cell",
".listTitle",
".listTitle"
);
res.status(200).json({
status: "success",
data: result,
});
break;
}
default: {
next();
break;
}
}
};
这是
case /${MOST_POPULAR_LISTS}:
list-scraper.ts
import puppeteer, { Page } from "puppeteer";
import { Browser } from "puppeteer";
export const listScraper = async (
browser: Browser,
page: Page,
url: string,
pageI = 1,
main: string,
title = "",
ref = ""
) => {
// const page = await browser.newPage();
await page.goto(url, {
waitUntil: "domcontentloaded",
});
const books = await page.evaluate(
(mainSelector, titleSelector, refSelector) => {
// const nextLink = document.querySelector('a[rel="next"]');
// console.log(nextLink);
const elements = document.querySelectorAll(mainSelector);
return Array.from(elements)
.slice(0, 3)
.map((element) => {
const title =
titleSelector.length > 0 &&
(element.querySelector(titleSelector) as HTMLElement | null)
?.innerText;
const ref =
refSelector.length > 0 &&
(element.querySelector(refSelector) as HTMLAnchorElement | null)
?.href;
return { title, ref };
});
},
main,
title,
ref
);
// await page.click(".pagination > a");
await browser.close();
return books;
};
导入的变量值并不那么重要。
所以我的问题是如何优化我的方法以及可以使用哪些技术,以使抓取速度更快,从而大幅提高 API 的性能?
我搜索了各种帖子,许多帖子建议对 CPU 进行某种操作,但我不明白如何在我的案例中使用它。 Node.js 中的子进程也被建议了好几次。
提前谢谢您!
可以考虑一些事情来提高速度。
puppeteer
实例。我们可以按照单例模式创建一个模块。// browser_instance.ts
import puppeteer from 'puppeteer';
let browserInstance = null;
export async function getBrowser() {
if (!browserInstance) {
browserInstance = await puppeteer.launch({ ... });
}
return browserInstance;
}
然后在您的代码中,尝试获取实例
import { getBrowser } from './browser-instance.js';
....
export const scraperHandler = async (req: Request, res: Response, next: NextFunction) => {
const browser = await getBrowser();
// ... use the browser instance for scraping
};
Promise.all
等待所有请求以并发方式完成。const results = await Promise.all(listUrls.map(async (url) => {
const page = await browser.newPage();
// ... scraping logic for each page ...
}));
您还可以进行更多优化。考虑研究如何找到应用程序中的慢点,并研究如何优化流程。