Puppeteer: يؤدي إغلاق المتصفح عند إغلاق الصفحة الأخيرة أحيانًا إلى حدوث خطأ

تم إنشاؤها على ٢٧ مارس ٢٠١٨  ·  9تعليقات  ·  مصدر: puppeteer/puppeteer

أخبرنا عن بيئتك:

  • إصدار محرك العرائس: 1.2.0
  • إصدار النظام الأساسي / نظام التشغيل: CentOS 7.4.1708
  • عناوين URL (إن وجدت): لا ينطبق
  • إصدار Node.js: v8.10.0

ما هي الخطوات التي سوف تتكاثر هذه المشكلة؟

const puppeteer = require('puppeteer');

const run = async () => {
  const browser = await puppeteer.launch();

  // close any open pages
  let pages = await browser.pages();
  await Promise.all(pages.map(async page => await page.close()));

  // handle a page being closed
  browser.on('targetdestroyed', async target => {
    const openPages = await browser.pages();
    console.log('Open pages:', openPages.length);
    if (openPages.length == 0) {
      console.log('Closing empty browser');
      await browser.close();
      console.log('Browser closed');
    }
  });

  pages = await browser.pages();
  for (let i = 0; i < 5; i++) {
    await browser.newPage();
  }

  pages = await browser.pages();
  pages.forEach((page) => page.close());
};

run();

ما هي النتيجة المتوقعة؟

Count: 0
Open pages: 4
Open pages: 3
Open pages: 2
Open pages: 1
Open pages: 0
Closing empty browser
Browser closed

ماذا يحدث بدلا من ذلك؟

Open pages: 4
Open pages: 3
Open pages: 2
Open pages: 1
Open pages: 0
Closing empty browser
(node:29074) UnhandledPromiseRejectionWarning: Error: Protocol error (Target.closeTarget): Target closed.
    at Promise (node_modules/puppeteer/lib/Connection.js:86:56)
    at new Promise (<anonymous>)
    at Connection.send (node_modules/puppeteer/lib/Connection.js:85:12)
    at Page.close (node_modules/puppeteer/lib/Page.js:802:36)
    at pages.forEach (test.js:27:32)
    at Array.forEach (<anonymous>)
    at run (test.js:27:9)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
(node:29074) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:29074) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Browser closed

هذا لا يحدث في كل مرة ولكنه يحدث بين الحين والآخر. من الممكن تمامًا أن أفعل شيئًا سيئًا هنا!

bug chromium

ال 9 كومينتر

+1

بعد إجراء بعض التصحيح السريع للأخطاء ، تم تشغيل الحدث targetDestroyed قبل استلام رسالة page.close() (أو بالأحرى ، قام محرك الدمى بمعالجة الإقرار).

يمكنني التفكير في طرق لإصلاحها - على سبيل المثال ، انتظر الحصول على اعتراف Target.closeTarget قبل معالجة الحدث targetDestroyed . كيف يتم القيام بذلك بشكل صحيح رغم أنه سؤال آخر!

شكرا MJPA على التحقيق.

تأتي المشكلة من حقيقة أن browser يزيل الأهداف من القائمة قبل إغلاق الصفحات المرتبطة.

في المثال التالي ، أتوقع أن يقوم browser.pages() بالإبلاغ عن صفحة واحدة حتى تنتهي المكالمة page.close() :

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const [page] = await browser.pages();
  browser.on('targetdestroyed', async () => console.log('Target destroyed. Pages count: ' + (await browser.pages()).length));
  console.log('closing page...');
  await page.close();
  console.log('page closed.');
  await browser.close();
})();

الإخراج:

closing page...
Target destroyed. Pages count: 0
page closed.

JoelEinbinder ما رأيك؟

نفس المشكلة مثل MJPA

        await page.waitForSelector('aside.aside a[href="/checkout"]')
    .then(() => {
        logMessage('Checkout is available');
    })
    .catch(err => {
        log('Checkout not available');
        success = false;
        browser.close();
    });
    // click checkout button
    await Promise.all([
        page.click('aside.aside a[href="/checkout"]'),
        page.waitForNavigation()
    ]);

في هذه الحالة ، تحدث نفس الأخطاء دائمًا.

ربما هذا الخطأ محرك الدمى.

أحصل على نفس الخطأ مع تطبيق مشابه قليلاً ، ولكن من خلال الاستماع إلى حدث close على page . التوثيق ليس واضحًا بشكل مفرط بشأن معنى "الإغلاق" ، ولكن وفقًا لتطبيقه ، يتم إطلاق close _b قبل_ إغلاق الصفحة ، وبالتالي فإن المثال التالي لا يعمل ، على غرار targetdestroyed مثال.

const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();

// create 5 test pages
for(let i=0; i<5; i++){

  let page = await browser.newPage();

  // listen to page close event and close browser
  // when all get closed
  page.on('close', async () => {
    let pages = await browser.pages();
    if(!pages.length){
      // will not get here!
      browser.close();
    }
  });

}

let pages = await browser.pages();

pages.forEach(async (page) => await page.close());  

أعتقد أن هذا _sort of_ منطقي ، لأن الحدث يسمى close وليس closed . لقد تمكنت من التغلب على هذا بمفردي عن طريق إصدار حدث closed في رد الاتصال بالوعد close() والاستماع إلى ذلك بدلاً من ذلك ، والذي _ يفعل_.

page.close().then(() => {
  page.emit('closed');
});

يبدو أن المغزى من القصة هو أنه لا توجد طريقة مضمونة 100٪ لمعرفة متى يكون المتصفح آمنًا للإغلاق (دون الحاجة إلى اكتشاف الاستثناء). يرجى إعلامي إذا فاتني شيء ما.

يحتمل أن تكون مرتبطة بـ # 3423؟

أحصل على هذا بعد إغلاق جميع علامات التبويب في المتصفح ثم محاولة فتح صفحة جديدة

في المثال التالي ، أتوقع أن يقوم browser.pages() بالإبلاغ عن صفحة واحدة حتى تنتهي المكالمة page.close() :

هذا من شأنه كسر الكود في المثال أعلاه. الصفحة الأخيرة مغلقة ، brower.pages (). سيكون الطول 1 ، ولن يتم إغلاق المتصفح. ثم لن تأتي المزيد من الأحداث المستهدفة المدمرة وسيظل المتصفح يعيش إلى الأبد.

أود أن أقول أن هذا خطأ مستخدم ، على الرغم من كونه دقيقًا. يجب انتظار أي مكالمات إلى page.close قبل استدعاء browser.close() .

إذا لم تكن هناك صفحات يتحكم فيها المستخدم ، فما عليك سوى إغلاق Promise.all جميع الصفحات ، ثم إغلاق المتصفح. يجب ألا تكون هناك حاجة للاستماع إلى أحداث targetdestroyed . أو فقط اتصل ب browser.close واترك جميع الصفحات تغلق من تلقاء نفسها.

إذا كانت هناك صفحات يتحكم فيها المستخدم ، فاستمع إلى أحداث targetdestroyed ، ولكن لن تكون هناك حاجة لاستدعاء page.close .

+1.
بدأت أرى هذا الخطأ بعد إغلاق النوافذ المنبثقة بـ page.on("popup") . عندما تنطلق نافذة منبثقة داخل سياق إطار أحيانًا لا يمكنني إغلاقها بـ "popup.close ()" ويترك محرك الدمى علامة التبويب مفتوحة ، لذلك عندما أحاول إغلاق المتصفح بأكمله ، فإنه يلقي خطأ "الهدف مغلق" ، ويتوقف النص.

أستخدم محرك الدمى 1.14 مع Chrome 73 (لا أستخدم Chromium)
الكود الذي أستخدمه لإغلاق المتصفح

async close_browser() {
    for (let page of await this.browser.pages()) {
      await page.close({
        "runBeforeUnload": true
      });
    }
   await this.browser.close();
}
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات