From d3c251c1ab3a935a14f1d3333a3643505cc933c4 Mon Sep 17 00:00:00 2001 From: haosenwang1018 <1293965075@qq.com> Date: Sun, 15 Mar 2026 17:53:23 +0800 Subject: [PATCH] fix: add max retry limit to _get_page_content to prevent infinite loop Both _get_page_content and _get_async_page_content use a while-True loop that retries page.content() on PlaywrightError with no upper bound. If the page is in a permanently broken state (crashed tab, closed context), this loops forever and hangs the process. Replace with a bounded for-loop (default 10 retries = 5s), returning an empty string if all attempts fail. This preserves the existing retry behavior for the transient Windows issue (playwright#16108) while preventing hangs. Co-Authored-By: Claude Opus 4.6 (1M context) --- scrapling/engines/toolbelt/convertor.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scrapling/engines/toolbelt/convertor.py b/scrapling/engines/toolbelt/convertor.py index 1a8d799..1860153 100644 --- a/scrapling/engines/toolbelt/convertor.py +++ b/scrapling/engines/toolbelt/convertor.py @@ -187,34 +187,34 @@ class ResponseFactory: return history @classmethod - def _get_page_content(cls, page: SyncPage) -> str: + def _get_page_content(cls, page: SyncPage, max_retries: int = 10) -> str: """ A workaround for the Playwright issue with `page.content()` on Windows. Ref.: https://github.com/microsoft/playwright/issues/16108 :param page: The page to extract content from. + :param max_retries: Maximum number of retry attempts before returning empty string. :return: """ - while True: + for _ in range(max_retries): try: return page.content() or "" except PlaywrightError: page.wait_for_timeout(500) - continue - return "" # pyright: ignore + return "" @classmethod - async def _get_async_page_content(cls, page: AsyncPage) -> str: + async def _get_async_page_content(cls, page: AsyncPage, max_retries: int = 10) -> str: """ A workaround for the Playwright issue with `page.content()` on Windows. Ref.: https://github.com/microsoft/playwright/issues/16108 :param page: The page to extract content from. + :param max_retries: Maximum number of retry attempts before returning empty string. :return: """ - while True: + for _ in range(max_retries): try: return (await page.content()) or "" except PlaywrightError: await page.wait_for_timeout(500) - continue - return "" # pyright: ignore + return "" @classmethod async def from_async_playwright_response(