4

我正在尝试在服务器端环境中在一秒钟内生成网页图像。这些请求可以同时来自网络。为此,我正在使用运行良好的 Puppeteer-Sharp库。在后端,它使用 Chromium 加载页面,然后对其进行截图。

问题是需要一段时间才能开始。例如,请注意 readme.md 示例代码中的时间(来自我的电脑):

var options = new new LaunchOptions {Headless = true, ExecutablePath = @"c:\foo\chrome.exe"};
var browser = await Puppeteer.LaunchAsync(options).Result;    //  ~500ms
var page = browser.NewPageAsync().Result;                     //  ~215ms
var webPage = page.GoToAsync("http://www.google.com").Result; //  ~500ms
var screenshot = page.ScreenshotAsync(outputFile);            
screenshot.wait();                                            //  ~300ms   

如您所见,它很容易超过一秒钟。我不知道 Chromium 在内部是如何工作的,所以我有几个与我正在考虑的解决方案有关的问题。

  1. 对象是PuppeteerSharp.Browser线程安全的和/或可重入的吗?我可以使用来自不同线程的相同浏览器对象吗?我不这么认为,因为它与内存中的特定 Chromium 实例相关联。
  2. 如果我从每个请求中删除.LaunchAsync.NetPageAsync这将显着加快操作。对象池会PuppeteerSharp.Browser起作用吗?例如,我可以预先分配其中的 5 个并.NetPageAsync在它们上执行。然后传入的请求将使用池中的对象。这是一个可行的方法吗?
4

1 回答 1

8

尽管仍有许多改进,Puppeteer-Sharp 是线程安全的。为了提高加载性能,您可以采取一些方法。

启动一个浏览器,然后连接到它

您可以启动一个(真正的)浏览器,然后使用该ConnectAsync方法连接到它。

await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
    Headless = false,
});

var theBrowser1 = await Puppeteer.ConnectAsync(new ConnectOptions { BrowserWSEndpoint = browser.WebSocketEndpoint });
var theBrowser2 = await Puppeteer.ConnectAsync(new ConnectOptions { BrowserWSEndpoint = browser.WebSocketEndpoint });
var page1 = await theBrowser1.NewPageAsync();
var page2 = await theBrowser2.NewPageAsync();

await Task.WhenAll(
    page1.GoToAsync("https://www.stackoverflow.com"),
    page2.GoToAsync("https://serverfault.com/")
);

我知道代码不是并行运行的,但是您会了解重用同一浏览器的想法。

在同一浏览器上创建新页面

如果您使用的是TPL,则使用同一浏览器从不同线程创建新页面应该没有任何问题。

await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
    Headless = false,
});

var urls = new string[]
{
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com"
};

await Task.WhenAll(
    urls.Select(url => Task.Factory.StartNew(async () =>
    {
        var page = await browser.NewPageAsync();
        return page.GoToAsync(url);
    })));

同样,此示例只是为了让您了解如何完成此操作。

页面队列

有一个用户创建了一个 X 页面队列(x 从 0 到 X => NewPage),然后他会从该队列中抓取页面。您可以在此处查看示例

于 2019-02-20T11:40:37.750 回答