def waitFor(self, selectorOrFunctionOrTimeout: Union[str, int, float], options: dict = None, *args: Any, **kwargs: Any) -> Awaitable: """Wait for function, timeout, or element which matches on page. This method behaves differently with respect to the first argument: * If ``selectorOrFunctionOrTimeout`` is number (int or float), then it is treated as a timeout in milliseconds and this returns future which will be done after the timeout. * If ``selectorOrFunctionOrTimeout`` is a string of JavaScript function, this method is a shortcut to :meth:`waitForFunction`. * If ``selectorOrFunctionOrTimeout`` is a selector string, this method is a shortcut to :meth:`waitForSelector`. Pyppeteer tries to automatically detect function or selector, but sometimes miss-detects. If not work as you expected, use :meth:`waitForFunction` or :meth:`waitForSelector` dilectly. """ frame = self.mainFrame if not frame: raise PageError('no main frame.') return frame.waitFor(selectorOrFunctionOrTimeout, options, *args, **kwargs)
async def start(self, options: Dict = None, **kwargs: Any) -> None: 'Start coverage measurement.' options = merge_dict(options, kwargs) if self._enabled: raise PageError('JSCoverage is always enabled.') self._resetOnNavigation = (True if ('resetOnNavigation' not in options) else bool(options['resetOnNavigation'])) self._enabled = True self._scriptURLs.clear() self._scriptSources.clear() self._eventListeners = [ helper.addEventListener( self._client, 'Debugger.scriptParsed', (lambda e: asyncio.ensure_future(self._onScriptParsed(e)))), helper.addEventListener(self._client, 'Runtime.executionContextsCleared', self._onExecutionContextsCleared) ] (await self._client.send('Profiler.enable')) (await self._client.send('Profiler.startPreciseCoverage', { 'callCount': False, 'detailed': True, })) (await self._client.send('Debugger.enable')) (await self._client.send('Debugger.setSkipAllPauses', { 'skip': True, }))
async def stop(self) -> List: 'Stop coverage measurement and return results.' if (not self._enabled): raise PageError('CSSCoverage is not enabled.') self._enabled = False result = (await self._client.send('CSS.stopRuleUsageTracking')) (await self._client.send('CSS.disable')) (await self._client.send('DOM.disable')) helper.removeEventListeners(self._eventListeners) styleSheetIdToCoverage = {} for entry in result['ruleUsage']: ranges = styleSheetIdToCoverage.get(entry['styleSheetId']) if (not ranges): ranges = [] styleSheetIdToCoverage[entry['styleSheetId']] = ranges ranges.append({ 'startOffset': entry['startOffset'], 'endOffset': entry['endOffset'], 'count': (1 if entry['used'] else 0), }) coverage = [] for styleSheetId in self._stylesheetURLs: url = self._stylesheetURLs.get(styleSheetId) text = self._stylesheetSources.get(styleSheetId) ranges = convertToDisjointRanges( styleSheetIdToCoverage.get(styleSheetId, [])) coverage.append({ 'url': url, 'ranges': ranges, 'text': text, }) return coverage
async def addStyleTag(self, options: Dict) -> ElementHandle: 'Add style tag to this frame.\n\n Details see :meth:`pyppeteer.page.Page.addStyleTag`.\n ' context = (await self.executionContext()) if (context is None): raise ElementHandleError('ExecutionContext is None.') addStyleUrl = "\n async function (url) {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = url;\n document.head.appendChild(link);\n await new Promise((res, rej) => {\n link.onload = res;\n link.onerror = rej;\n });\n return link;\n }" addStyleContent = "\n function (content) {\n const style = document.createElement('style');\n style.type = 'text/css';\n style.appendChild(document.createTextNode(content));\n document.head.appendChild(style);\n return style;\n }" if isinstance(options.get('url'), str): url = options['url'] try: return (await context.evaluateHandle(addStyleUrl, url)).asElement() except ElementHandleError as e: raise PageError(''.join( ['Loading style from ', '{}'.format(url), ' failed'])) from e if isinstance(options.get('path'), str): with open(options['path']) as f: contents = f.read() contents = ( contents + '/*# sourceURL={}*/'.format(options['path'].replace('\n', ''))) return (await context.evaluateHandle(addStyleContent, contents)).asElement() if isinstance(options.get('content'), str): return (await context.evaluateHandle(addStyleContent, options['content'])).asElement() raise ValueError( 'Provide an object with a `url`, `path` or `content` property')
async def stop(self) -> List: 'Stop coverage measurement and return results.' if (not self._enabled): raise PageError('JSCoverage is not enabled.') self._enabled = False result = (await self._client.send('Profiler.takePreciseCoverage')) (await self._client.send('Profiler.stopPreciseCoverage')) (await self._client.send('Profiler.disable')) (await self._client.send('Debugger.disable')) helper.removeEventListeners(self._eventListeners) coverage = [] for entry in result.get('result', []): url = self._scriptURLs.get(entry.get('scriptId')) text = self._scriptSources.get(entry.get('scriptId')) if ((text is None) or (url is None)): continue flattenRanges = [] for func in entry.get('functions', []): flattenRanges.extend(func.get('ranges', [])) ranges = convertToDisjointRanges(flattenRanges) coverage.append({ 'url': url, 'ranges': ranges, 'text': text, }) return coverage
async def start(self, options: Dict = None, **kwargs: Any) -> None: """Start coverage measurement.""" options = merge_dict(options, kwargs) if self._enabled: raise PageError('JSCoverage is always enabled.') self._resetOnNavigation = (True if 'resetOnNavigation' not in options else bool(options['resetOnNavigation'])) self._reportAnonymousScript = bool( options.get('reportAnonymousScript')) # noqa: E501 self._enabled = True self._scriptURLs.clear() self._scriptSources.clear() self._eventListeners = [ helper.addEventListener( self._client, 'Debugger.scriptParsed', lambda e: self._client._loop.create_task( self._onScriptParsed(e))), helper.addEventListener(self._client, 'Runtime.executionContextsCleared', self._onExecutionContextsCleared), ] await asyncio.gather( self._client.send('Profiler.enable'), self._client.send('Profiler.startPreciseCoverage', { 'callCount': False, 'detailed': True }), self._client.send('Debugger.enable'), self._client.send('Debugger.setSkipAllPauses', {'skip': True}))
async def addScriptTag(self, options: Dict) -> ElementHandle: 'Add script tag to this frame.\n\n Details see :meth:`pyppeteer.page.Page.addScriptTag`.\n ' context = (await self.executionContext()) if (context is None): raise ElementHandleError('ExecutionContext is None.') addScriptUrl = "\n async function addScriptUrl(url) {\n const script = document.createElement('script');\n script.src = url;\n document.head.appendChild(script);\n await new Promise((res, rej) => {\n script.onload = res;\n script.onerror = rej;\n });\n return script;\n }" addScriptContent = "\n function addScriptContent(content) {\n const script = document.createElement('script');\n script.type = 'text/javascript';\n script.text = content;\n document.head.appendChild(script);\n return script;\n }" if isinstance(options.get('url'), str): url = options['url'] try: return (await context.evaluateHandle(addScriptUrl, url)).asElement() except ElementHandleError as e: raise PageError(''.join( ['Loading script from ', '{}'.format(url), ' failed'])) from e if isinstance(options.get('path'), str): with open(options['path']) as f: contents = f.read() contents = ( contents + '//# sourceURL={}'.format(options['path'].replace('\n', ''))) return (await context.evaluateHandle(addScriptContent, contents)).asElement() if isinstance(options.get('content'), str): return (await context.evaluateHandle(addScriptContent, options['content'])).asElement() raise ValueError( 'Provide an object with a `url`, `path` or `content` property')
async def waitForNavigation(self, options: dict = None, **kwargs: Any) -> Optional[Response]: """Wait for navigation. Available options are same as :meth:`goto` method. """ options = merge_dict(options, kwargs) mainFrame = self._frameManager.mainFrame if mainFrame is None: raise PageError('No main frame.') timeout = options.get('timeout', self._defaultNavigationTimeout) watcher = NavigatorWatcher(self._frameManager, mainFrame, timeout, options) responses: Dict[str, Response] = dict() listener = helper.addEventListener( self._networkManager, NetworkManager.Events.Response, lambda response: responses.__setitem__(response.url, response)) result = await watcher.navigationPromise() helper.removeEventListeners([listener]) error = result[0].pop().exception() if error: raise error response = responses.get(self.url, None) return response
async def evaluateHandle(self, pageFunction: str, *args: Any ) -> JSHandle: """Execute function on this page. Difference between :meth:`~pyppeteer.page.Page.evaluate` and :meth:`~pyppeteer.page.Page.evaluateHandle` is that ``evaluateHandle`` returns JSHandle object (not value). :arg str pageFunction: JavaScript function to be executed. """ if not self.mainFrame: raise PageError('no main frame.') if not self.mainFrame.executionContext: raise PageError('No context.') return await self.mainFrame.executionContext.evaluateHandle( pageFunction, *args)
async def stop(self) -> List: """Stop coverage measurement and return results.""" if not self._enabled: raise PageError('JSCoverage is not enabled.') self._enabled = False result = await self._client.send('Profiler.takePreciseCoverage') await self._client.send('Profiler.stopPreciseCoverage') await self._client.send('Profiler.disable') await self._client.send('Debugger.disable') helper.removeEventListeners(self._eventListeners) coverage: List = [] for entry in result.get('result', []): url = self._scriptURLs.get(entry.get('scriptId')) if not url and self._reportAnonymousScript: url = f'debugger://VM{entry.get("scriptId")}' text = self._scriptSources.get(entry.get('scriptId')) if text is None or url is None: continue flattenRanges: List = [] for func in entry.get('functions', []): flattenRanges.extend(func.get('ranges', [])) ranges = convertToDisjointRanges(flattenRanges) coverage.append({'url': url, 'ranges': ranges, 'text': text}) return coverage
def waitForFunction(self, pageFunction: str, options: dict = None, *args: str, **kwargs: Any) -> Awaitable: """Wait until the function completes and returns a truethy value. :arg Any args: Arguments to pass to ``pageFunction``. :return: Return awaitable object which resolves when the ``pageFunction`` returns a truethy value. It resolves to a :class:`~pyppeteer.execution_context.JSHandle` of the truethy value. This method accepts the following options: * ``polling`` (str|number): An interval at which the ``pageFunction`` is executed, defaults to ``raf``. If ``polling`` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If ``polling`` is a string, then it can be one of the following values: * ``raf``: to constantly execute ``pageFunction`` in ``requestAnimationFrame`` callback. This is the tightest polling mode which is suitable to observe styling changes. * ``mutation``: to execute ``pageFunction`` on every DOM mutation. * ``timeout`` (int|float): maximum time to wait for in milliseconds. """ frame = self.mainFrame if not frame: raise PageError('no main frame.') return frame.waitForFunction(pageFunction, options, *args, **kwargs)
def waitForSelector(self, selector: str, options: dict = None, **kwargs: Any) -> Awaitable: """Wait until element which matches ``selector`` appears on page. Wait for the ``selector`` to appear in page. If at the moment of callingthe method the ``selector`` already exists, the method will return immediately. If the selector doesn't appear after the ``timeout`` milliseconds of waiting, the function will raise error. :arg str selector: A selector of an element to wait for. :return: Return awaitable object which resolves when element specified by selector string is added to DOM. This method accepts the following options: * ``visible`` (bool): Wait for element to be present in DOM and to be visible; i.e. to not have ``display: none`` or ``visibility: hidden`` CSS properties. Defaults to ``False``. * ``hidden`` (bool): Wait for eleemnt to not be found in the DOM or to be hidden, i.e. have ``display: none`` or ``visibility: hidden`` CSS properties. Defaults to ``False``. * ``timeout`` (int|float): Maximum time to wait for in milliseconds. Defaults to 30000 (30 seconds). """ frame = self.mainFrame if not frame: raise PageError('no main frame.') return frame.waitForSelector(selector, options, **kwargs)
def _onFrameNavigated(self, framePayload: dict) -> None: isMainFrame = not framePayload.get('parentId') if isMainFrame: frame = self._mainFrame else: frame = self._frames.get(framePayload.get('id', '')) if not (isMainFrame or frame): raise PageError('We either navigate top level or have old version ' 'of the navigated frame') # Detach all child frames first. if frame: for child in frame.childFrames: self._removeFramesRecursively(child) # Update or create main frame. _id = framePayload.get('id', '') if isMainFrame: if frame: # Update frame id to retain frame identity on cross-process navigation. # noqa: E501 self._frames.pop(frame._id, None) frame._id = _id else: # Initial main frame navigation. frame = Frame(self._client, self._page, None, _id) self._frames[_id] = frame self._mainFrame = frame # Update frame payload. frame._navigated(framePayload) # type: ignore self.emit(FrameManager.Events.FrameNavigated, frame)
async def addStyleTag(self, options: Dict) -> ElementHandle: """Add style tag to this frame. Details see :meth:`pyppeteer.page.Page.addStyleTag`. """ context = await self.executionContext() if context is None: raise ElementHandleError('ExecutionContext is None.') addStyleUrl = ''' async function (url) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = url; const promise = new Promise((res, rej) => { link.onload = res; link.onerror = rej; }); document.head.appendChild(link); await promise; return link; }''' addStyleContent = ''' async function (content) { const style = document.createElement('style'); style.type = 'text/css'; style.appendChild(document.createTextNode(content)); const promise = new Promise((res, rej) => { style.onload = res; style.onerror = rej; }); document.head.appendChild(style); await promise; return style; }''' if isinstance(options.get('url'), str): url = options['url'] try: return (await context.evaluateHandle( # type: ignore addStyleUrl, url)).asElement() except ElementHandleError as e: raise PageError(f'Loading style from {url} failed') from e if isinstance(options.get('path'), str): with open(options['path']) as f: contents = f.read() contents = contents + '/*# sourceURL={}*/'.format( options['path'].replace('\n', '')) return (await context.evaluateHandle( # type: ignore addStyleContent, contents)).asElement() if isinstance(options.get('content'), str): return (await context.evaluateHandle( # type: ignore addStyleContent, options['content'])).asElement() raise ValueError( 'Provide an object with a `url`, `path` or `content` property')
async def close(self) -> None: """Close connection.""" conn = self._client._connection if conn is None: raise PageError('Protocol Error: Connectoin Closed. ' 'Most likely the page has been closed.') await conn.send('Target.closeTarget', {'targetId': self._target._targetId})
def _detach(self) -> None: for waitTask in self._waitTasks: waitTask.terminate( PageError('waitForFunction failed: frame got detached.')) self._detached = True if self._parentFrame: self._parentFrame._childFrames.remove(self) self._parentFrame = None
async def _document(self) -> ElementHandle: if not self._documentPromise: context = await self.executionContext() if context is None: raise PageError('No context exists.') self._documentPromise = ( await context.evaluateHandle('document')).asElement() return self._documentPromise
async def evaluateHandle(self, pageFunction: str, *args: Any) -> JSHandle: """Execute fucntion on this frame. Details see :meth:`pyppeteer.page.Page.evaluateHandle`. """ context = await self.executionContext() if context is None: raise PageError('this frame has no context.') return await context.evaluateHandle(pageFunction, *args)
def waitForSelector(self, selector: str, options: dict = None, **kwargs: Any) -> Awaitable: """Wait until element which matches selector appears on page.""" frame = self.mainFrame if not frame: raise PageError('no main frame.') return frame.waitForSelector(selector, options, **kwargs)
async def injectFile(self, filePath: str) -> str: """[Deprecated] Inject file to this page. This method is deprecated. Use :meth:`addScriptTag` instead. """ frame = self.mainFrame if not frame: raise PageError('no main frame.') return await frame.injectFile(filePath)
async def addStyleTag(self, options: Dict = None, **kwargs: str) -> str: """Add script tag to this page.""" frame = self.mainFrame if not frame: raise PageError('no main frame.') if options is None: options = {} options.update(kwargs) return await frame.addStyleTag(options)
async def click(self, selector: str, options: dict = None, **kwargs: Any ) -> None: """Click element which matches `selector`.""" options = merge_dict(options, kwargs) handle = await self.J(selector) if not handle: raise PageError('No node found for selector: ' + selector) await handle.click(options) await handle.dispose()
async def select(self, selector: str, *values: str) -> List[str]: """Select options and return selected values. If no element matched the ``selector``, raise ``ElementHandleError``. """ frame = self.mainFrame if not frame: raise PageError('no main frame.') return await frame.select(selector, *values)
async def rerun(self) -> None: # noqa: C901 """Start polling.""" runCount = self._runCount = self._runCount + 1 success: Optional[JSHandle] = None error = None try: context = await self._frame.executionContext() if context is None: raise PageError('No execution context.') success = await context.evaluateHandle( waitForPredicatePageFunction, self._predicateBody, self._polling, self._timeout, *self._args, ) except Exception as e: error = e if self.promise.done(): return if self._terminated or runCount != self._runCount: if success: await success.dispose() return # Add try/except referring to puppeteer. try: if not error and success and (await self._frame.evaluate( 's => !s', success)): await success.dispose() return except NetworkError: if success is not None: await success.dispose() return # page is navigated and context is destroyed. # Try again in the new execution context. if (isinstance(error, NetworkError) and 'Execution context was destroyed' in error.args[0]): return # Try again in the new execution context. if (isinstance(error, NetworkError) and 'Cannot find context with specified id' in error.args[0]): return if error: self.promise.set_exception(error) else: self.promise.set_result(success) self._cleanup()
async def tap(self, selector: str) -> None: """Tap the element which matches the ``selector``. Details see :meth:`pyppeteer.page.Page.tap`. """ handle = await self.J(selector) if not handle: raise PageError('No node found for selector: ' + selector) await handle.tap() await handle.dispose()
async def hover(self, selector: str) -> None: """Mouse hover the element which matches ``selector``. Details see :meth:`pyppeteer.page.Page.hover`. """ handle = await self.J(selector) if not handle: raise PageError('No node found for selector: ' + selector) await handle.hover() await handle.dispose()
async def focus(self, selector: str) -> None: """Fucus element which matches ``selector``. Details see :meth:`pyppeteer.page.Page.focus`. """ handle = await self.J(selector) if not handle: raise PageError('No node found for selector: ' + selector) await self.evaluate('element => element.focus()', handle) await handle.dispose()
def waitForFunction(self, pageFunction: str, options: dict = None, *args: str, **kwargs: Any) -> Awaitable: """Wait for function.""" frame = self.mainFrame if not frame: raise PageError('no main frame.') return frame.waitForFunction(pageFunction, options, *args, **kwargs)
async def focus(self, selector: str) -> None: """Focus the element which matches ``selector``. If no element matched the ``selector``, raise ``PageError``. """ handle = await self.J(selector) if not handle: raise PageError('No node found for selector: ' + selector) await self.evaluate('element => element.focus()', handle) await handle.dispose()
async def tap(self, selector: str) -> None: """Tap the element which matches the ``selector``. :arg str selector: A selector to search element to touch. """ handle = await self.J(selector) if not handle: raise PageError('No node found for selector: ' + selector) await handle.tap() await handle.dispose()