def __init__(self, connection: Connection, options: Dict = None, process: Optional[Popen] = None, closeCallback: Callable[[], Awaitable[None]] = None, **kwargs: Any) -> None: super().__init__() options = merge_dict(options, kwargs) self._ignoreHTTPSErrors = bool(options.get('ignoreHTTPSErrors', False)) self._appMode = bool(options.get('appMode', False)) self._process = process self._screenshotTaskQueue: List = [] self._connection = connection def _dummy_callback() -> Awaitable[None]: fut = asyncio.get_event_loop().create_future() fut.set_result(None) return fut if closeCallback: self._closeCallback = closeCallback else: self._closeCallback = _dummy_callback self._targets: Dict[str, Target] = dict() self._connection.setClosedCallback( lambda: self.emit(Browser.Events.Disconnected) ) self._connection.on('Target.targetCreated', self._targetCreated) self._connection.on('Target.targetDestroyed', self._targetDestroyed) self._connection.on('Target.targetInfoChanged', self._targetInfoChanged) # noqa: E501
async def press(self, key: str, options: Dict = None, **kwargs: Any ) -> None: """Press ``key``. If ``key`` is a single character and no modifier keys besides ``Shift`` are being held down, a ``keypress``/``input`` event will also generated. The ``text`` option can be specified to force an input event to be generated. :arg str key: Name of key to press, such as ``ArrowLeft``. This method accepts the following options: * ``text`` (str): If specified, generates an input event with this text. * ``delay`` (int|float): Time to wait between ``keydown`` and ``keyup``. Defaults to 0. .. note:: Modifier keys DO effect :meth:`press`. Holding down ``Shift`` will type the text in upper case. """ options = merge_dict(options, kwargs) await self.down(key, options) if 'delay' in options: await asyncio.sleep(options['delay'] / 1000) await self.up(key)
async def start(self, options: dict = None, **kwargs: Any) -> None: """Start tracing. Only one trace can be active at a time per browser. This method accepts the following options: * ``path`` (str): A path to write the trace file to. * ``screenshots`` (bool): Capture screenshots in the trace. * ``categories`` (List[str]): Specify custom categories to use instead of default. """ options = merge_dict(options, kwargs) defaultCategories = [ '-*', 'devtools.timeline', 'v8.execute', 'disabled-by-default-devtools.timeline', 'disabled-by-default-devtools.timeline.frame', 'toplevel', 'blink.console', 'blink.user_timing', 'latencyInfo', 'disabled-by-default-devtools.timeline.stack', 'disabled-by-default-v8.cpu_profiler', 'disabled-by-default-v8.cpu_profiler.hires', ] categoriesArray = options.get('categories', defaultCategories) if 'screenshots' in options: categoriesArray.append('disabled-by-default-devtools.screenshot') self._path = options.get('path', '') self._recording = True await self._client.send('Tracing.start', { 'transferMode': 'ReturnAsStream', 'categories': ','.join(categoriesArray), })
async def click(self, selector: str, options: dict = None, **kwargs: Any ) -> None: """Click element which matches ``selector``. This method fetches an element with ``selector``, scrolls it into view if needed, and then uses :attr:`mouse` to click in the center of the element. If there's no element matching ``selector``, the method raises ``PageError``. Available options are: * ``button`` (str): ``left``, ``right``, or ``middle``, defaults to ``left``. * ``clickCount`` (int): defaults to 1. * ``delay`` (int|float): Time to wait between ``mousedown`` and ``mouseup`` in milliseconds. defaults to 0. .. note:: If this method triggers a navigation event and there's a separate :meth:`waitForNavigation`, you may end up with a race condition that yields unexpected results. The correct pattern for click and wait for navigation is the following:: await asyncio.wait([ page.waitForNavigation(waitOptions), page.click(selector, clickOptions), ]) """ 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 type(self, text: str, options: Dict = None, **kwargs: Any ) -> None: """Type characters into a focused element. This method sends ``keydown``, ``keypress``/``input``, and ``keyup`` event for each character in the ``text``. To press a special key, like ``Control`` or ``ArrowDown``, use :meth:`press` method. :arg str text: Text to type into a focused element. :arg dict options: Options can have ``delay`` (int|float) field, which specifies time to wait between key presses in milliseconds. Defaults to 0. .. note:: Modifier keys DO NOT effect :meth:`type`. Holding down ``shift`` will not type the text in upper case. """ options = merge_dict(options, kwargs) delay = options.get('delay', 0) for char in text: if char in keyDefinitions: await self.press(char, {'delay': delay}) else: await self.sendCharacter(char) if delay: await asyncio.sleep(delay / 1000)
def __init__(self, frameManager: FrameManager, frame: Frame, timeout: int, options: Dict = None, **kwargs: Any) -> None: """Make new navigator watcher.""" options = merge_dict(options, kwargs) self._validate_options(options) self._frameManeger = frameManager self._frame = frame self._initialLoaderId = frame._loaderId self._timeout = timeout self._eventListeners = [ helper.addEventListener( self._frameManeger, FrameManager.Events.LifecycleEvent, self._checkLifecycleComplete, ), helper.addEventListener( self._frameManeger, FrameManager.Events.FrameDetached, self._checkLifecycleComplete, ), ] loop = asyncio.get_event_loop() self._lifecycleCompletePromise = loop.create_future() self._navigationPromise = asyncio.ensure_future(asyncio.wait([ self._lifecycleCompletePromise, self._createTimeoutPromise(), ], return_when=concurrent.futures.FIRST_COMPLETED)) self._navigationPromise.add_done_callback( lambda fut: self._cleanup())
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 emulate(self, options: dict = None, **kwargs: Any) -> None: """Emulate viewport and user agent.""" options = merge_dict(options, kwargs) # TODO: if options does not have viewport or userAgent, # skip its setting. await self.setViewport(options.get('viewport', {})) await self.setUserAgent(options.get('userAgent', ''))
def waitFor(self, selectorOrFunctionOrTimeout: Union[str, int, float], options: dict = None, *args: Any, **kwargs: Any ) -> Union[Awaitable, 'WaitTask']: """Wait until `selectorOrFunctionOrTimeout`. Details see :meth:`pyppeteer.page.Page.waitFor`. """ options = merge_dict(options, kwargs) if isinstance(selectorOrFunctionOrTimeout, (int, float)): fut: Awaitable[None] = self._client._loop.create_task( asyncio.sleep(selectorOrFunctionOrTimeout / 1000)) return fut if not isinstance(selectorOrFunctionOrTimeout, str): fut = self._client._loop.create_future() fut.set_exception(TypeError( 'Unsupported target type: ' + str(type(selectorOrFunctionOrTimeout)) )) return fut if args or helper.is_jsfunc(selectorOrFunctionOrTimeout): return self.waitForFunction( selectorOrFunctionOrTimeout, options, *args) if selectorOrFunctionOrTimeout.startswith('//'): return self.waitForXPath(selectorOrFunctionOrTimeout, options) return self.waitForSelector(selectorOrFunctionOrTimeout, options)
def waitForSelector(self, selector: str, options: dict = None, **kwargs: Any) -> 'WaitTask': """Wait until element which matches ``selector`` appears on page. Details see :meth:`pyppeteer.page.Page.waitForSelector`. """ options = merge_dict(options, kwargs) waitForVisible = bool(options.get('visible')) waitForHidden = bool(options.get('hidden')) predicate = ''' (selector, waitForVisible, waitForHidden) => { const node = document.querySelector(selector); if (!node) return waitForHidden; if (!waitForVisible && !waitForHidden) return node; const style = window.getComputedStyle(node); const isVisible = style && style.visibility !== 'hidden' && hasVisibleBoundingBox(); const success = (waitForVisible === isVisible || waitForHidden === !isVisible) return success ? node : null function hasVisibleBoundingBox() { const rect = node.getBoundingClientRect(); return !!(rect.top || rect.bottom || rect.width || rect.height); } } ''' # noqa: E501 return self.waitForFunction( predicate, options, selector, waitForVisible, waitForHidden)
def __init__(self, options: Dict[str, Any] = None, # noqa: C901 **kwargs: Any) -> None: """Make new launcher.""" self.options = merge_dict(options, kwargs) self.port = get_free_port() self.url = f'http://127.0.0.1:{self.port}' self.chrome_args: List[str] = [] self._loop = self.options.get('loop', asyncio.get_event_loop()) logLevel = self.options.get('logLevel') if logLevel: logging.getLogger('pyppeteer').setLevel(logLevel) if not self.options.get('ignoreDefaultArgs', False): self.chrome_args.extend(DEFAULT_ARGS) self.chrome_args.append( f'--remote-debugging-port={self.port}', ) self.chromeClosed = True if self.options.get('appMode', False): self.options['headless'] = False elif not self.options.get('ignoreDefaultArgs', False): self.chrome_args.extend(AUTOMATION_ARGS) self._tmp_user_data_dir: Optional[str] = None self._parse_args() if self.options.get('devtools'): self.chrome_args.append('--auto-open-devtools-for-tabs') self.options['headless'] = False if 'headless' not in self.options or self.options.get('headless'): self.chrome_args.extend([ '--headless', '--disable-gpu', '--hide-scrollbars', '--mute-audio', ]) def _is_default_url() -> bool: for arg in self.options['args']: if not arg.startswith('-'): return False return True if (not self.options.get('ignoreDefaultArgs') and isinstance(self.options.get('args'), list) and _is_default_url()): self.chrome_args.append('about:blank') if 'executablePath' in self.options: self.exec = self.options['executablePath'] else: if not check_chromium(): download_chromium() self.exec = str(chromium_executable()) self.cmd = [self.exec] + self.chrome_args
def waitForXPath(self, xpath: str, options: dict = None, **kwargs: Any) -> 'WaitTask': """Wait until element which matches ``xpath`` appears on page. Details see :meth:`pyppeteer.page.Page.waitForXPath`. """ options = merge_dict(options, kwargs) return self._waitForSelectorOrXPath(xpath, True, options)
async def goForward(self, options: dict = None, **kwargs: Any ) -> Optional[Response]: """Navigate to the next page in history. Available options are same as :meth:`goto` method. """ options = merge_dict(options, kwargs) return await self._go(+1, options)
async def type(self, text: str, options: Dict = None, **kwargs: Any ) -> None: """Focus the element and then type text. Details see :meth:`pyppeteer.input.Keyboard.type` method. """ options = merge_dict(options, kwargs) await self.focus() await self._page.keyboard.type(text, options)
async def create(connection: Connection, options: dict = None, process: Optional[Popen] = None, closeCallback: Callable[[], Awaitable[None]] = None, **kwargs: Any) -> 'Browser': """Create browser object.""" options = merge_dict(options, kwargs) browser = Browser(connection, options, process, closeCallback) await connection.send('Target.setDiscoverTargets', {'discover': True}) return browser
async def screenshot(self, options: Dict = None, **kwargs: Any) -> bytes: """Take a screenshot of this element. If the element is detached from DOM, this method raises an ``ElementHandleError``. Available options are same as :meth:`pyppeteer.page.Page.screenshot`. """ options = merge_dict(options, kwargs) needsViewportReset = False boundingBox = await self.boundingBox() if not boundingBox: raise ElementHandleError( 'Node is either not visible or not an HTMLElement') original_viewport = copy.deepcopy(self._page.viewport) if (boundingBox['width'] > original_viewport['width'] or boundingBox['height'] > original_viewport['height']): newViewport = { 'width': max( original_viewport['width'], math.ceil(boundingBox['width']) ), 'height': max( original_viewport['height'], math.ceil(boundingBox['height']) ), } new_viewport = copy.deepcopy(original_viewport) new_viewport.update(newViewport) await self._page.setViewport(new_viewport) needsViewportReset = True await self._scrollIntoViewIfNeeded() boundingBox = await self.boundingBox() if not boundingBox: raise ElementHandleError( 'Node is either not visible or not an HTMLElement') _obj = await self._client.send('Page.getLayoutMetrics') pageX = _obj['layoutViewport']['pageX'] pageY = _obj['layoutViewport']['pageY'] clip = {} clip.update(boundingBox) clip['x'] = clip['x'] + pageX clip['y'] = clip['y'] + pageY opt = {'clip': clip} opt.update(options) imageData = await self._page.screenshot(opt) if needsViewportReset: await self._page.setViewport(original_viewport) return imageData
def waitForFunction(self, pageFunction: str, options: dict = None, *args: Any, **kwargs: Any) -> 'WaitTask': """Wait until the function completes. Details see :meth:`pyppeteer.page.Page.waitForFunction`. """ options = merge_dict(options, kwargs) timeout = options.get('timeout', 30000) # msec polling = options.get('polling', 'raf') return WaitTask(self, pageFunction, polling, timeout, *args)
async def startCSSCoverage(self, options: Dict = None, **kwargs: Any ) -> None: """Start CSS coverage measurement. Available options are: * ``resetOnNavigation`` (bool): Whether to reset coverage on every navigation. Defaults to ``True``. """ options = merge_dict(options, kwargs) await self._cssCoverage.start(options)
async def reload(self, options: dict = None, **kwargs: Any ) -> Optional[Response]: """Reload this page. Available options are same as :meth:`goto` method. """ options = merge_dict(options, kwargs) response = (await asyncio.gather( self.waitForNavigation(options), self._client.send('Page.reload'), ))[0] return response
async def click(self, selector: str, options: dict = None, **kwargs: Any ) -> None: """Click element which matches ``selector``. Details see :meth:`pyppeteer.page.Page.click`. """ 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 type(self, selector: str, text: str, options: dict = None, **kwargs: Any) -> None: """Type ``text`` on the element which matches ``selector``. Details see :meth:`pyppeteer.page.Page.type`. """ options = merge_dict(options, kwargs) handle = await self.querySelector(selector) if handle is None: raise PageError('Cannot find {} on this page'.format(selector)) await handle.type(text, options) await handle.dispose()
async def goto(self, url: str, options: dict = None, **kwargs: Any ) -> Optional[Response]: """Go to the ``url``. :arg string url: URL to navigate page to. The url should include scheme, e.g. ``https://``. Available options are: * ``timeout`` (int): Maximum navigation time in milliseconds, defaults to 30 seconds, pass ``0`` to desable timeout. The default value can be changed by using the :meth:`setDefaultNavigationTimeout` method. * ``waitUntil`` (str|List[str]): When to consider navigation succeeded, defaults to ``load``. Given a list of event strings, navigation is considered to be successful after all events have been fired. Events can be either: * ``load``: when ``load`` event is fired. * ``documentloaded``: when the ``DOMContentLoaded`` event is fired. * ``networkidle0``: when there are no more than 0 network connections for at least 500 ms. * ``networkidle2``: when there are no more than 2 network connections for at least 500 ms. """ options = merge_dict(options, kwargs) referrer = self._networkManager.extraHTTPHeaders().get('referer', '') requests: Dict[str, Request] = dict() eventListeners = [helper.addEventListener( self._networkManager, NetworkManager.Events.Request, lambda request: requests.__setitem__(request.url, request) )] 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) result = await self._navigate(url, referrer) if result is not None: raise PageError(result) result = await watcher.navigationPromise() watcher.cancel() helper.removeEventListeners(eventListeners) error = result[0].pop().exception() # type: ignore if error: raise error request = requests.get(mainFrame.url) return request.response if request else None
async def addStyleTag(self, options: Dict = None, **kwargs: str ) -> ElementHandle: """Add style or link tag to this page. One of ``url``, ``path`` or ``content`` option is necessary. * ``url`` (string): URL of the link tag to add. * ``path`` (string): Path to the local CSS file to add. * ``content`` (string): CSS string to add. :return ElementHandle: :class:`~pyppeteer.element_handle.ElementHandle` of added tag. """ frame = self.mainFrame if not frame: raise PageError('no main frame.') options = merge_dict(options, kwargs) return await frame.addStyleTag(options)
async def down(self, key: str, options: dict = None, **kwargs: Any ) -> None: """Dispatch a ``keydown`` event with ``key``. If ``key`` is a single character and no modifier keys besides ``Shift`` are being held down, and a ``keypress``/``input`` event will also generated. The ``text`` option can be specified to force an ``input`` event to be generated. If ``key`` is a modifier key, like ``Shift``, ``Meta``, or ``Alt``, subsequent key presses will be sent with that modifier active. To release the modifier key, use :meth:`up` method. :arg str key: Name of key to press, such as ``ArrowLeft``. :arg dict options: Option can have ``text`` field, and if this option specified, generate an input event with this text. .. note:: Modifier keys DO influence :meth:`down`. Holding down ``shift`` will type the text in upper case. """ options = merge_dict(options, kwargs) description = self._keyDescriptionForString(key) autoRepeat = description['code'] in self._pressedKeys self._pressedKeys.add(description['code']) self._modifiers |= self._modifierBit(description['key']) text = options.get('text') if text is None: text = description['text'] await self._client.send('Input.dispatchKeyEvent', { 'type': 'keyDown' if text else 'rawKeyDown', 'modifiers': self._modifiers, 'windowsVirtualKeyCode': description['keyCode'], 'code': description['code'], 'key': description['key'], 'text': text, 'unmodifiedText': text, 'autoRepeat': autoRepeat, 'location': description['location'], 'isKeypad': description['location'] == 3, })
def __init__( self, options: Dict[str, Any] = None, # noqa: C901 **kwargs: Any) -> None: """Make new launcher.""" self.options = merge_dict(options, kwargs) self.port = get_free_port() self.url = f'http://127.0.0.1:{self.port}' self.chrome_args: List[str] = [] if not self.options.get('ignoreDefaultArgs', False): self.chrome_args.extend(DEFAULT_ARGS) self.chrome_args.append(f'--remote-debugging-port={self.port}', ) self.chromeClosed = True if self.options.get('appMode', False): self.options['headless'] = False elif not self.options.get('ignoreDefaultArgs', False): self.chrome_args.extend(AUTOMATION_ARGS) self._tmp_user_data_dir: Optional[str] = None self._parse_args() if self.options.get('devtools'): self.chrome_args.append('--auto-open-devtools-for-tabs') self.options['headless'] = False if 'headless' not in self.options or self.options.get('headless'): self.chrome_args.extend([ '--headless', '--disable-gpu', '--hide-scrollbars', '--mute-audio', ]) if 'executablePath' in self.options: self.exec = self.options['executablePath'] else: if not check_chromium(): download_chromium() self.exec = str(chromium_excutable()) self.cmd = [self.exec] + self.chrome_args
async def screenshot(self, options: Dict = None, **kwargs: Any) -> bytes: 'Take a screenshot of this element.\n\n If the element is detached from DOM, this method raises an\n ``ElementHandleError``.\n\n Available options are same as :meth:`pyppeteer.page.Page.screenshot`.\n ' options = merge_dict(options, kwargs) needsViewportReset = False boundingBox = (await self.boundingBox()) if (not boundingBox): raise ElementHandleError( 'Node is either not visible or not an HTMLElement') original_viewport = copy.deepcopy(self._page.viewport) if ((boundingBox['width'] > original_viewport['width']) or (boundingBox['height'] > original_viewport['height'])): newViewport = { 'width': max(original_viewport['width'], math.ceil(boundingBox['width'])), 'height': max(original_viewport['height'], math.ceil(boundingBox['height'])), } new_viewport = copy.deepcopy(original_viewport) new_viewport.update(newViewport) (await self._page.setViewport(new_viewport)) needsViewportReset = True (await self._scrollIntoViewIfNeeded()) boundingBox = (await self.boundingBox()) if (not boundingBox): raise ElementHandleError( 'Node is either not visible or not an HTMLElement') _obj = (await self._client.send('Page.getLayoutMetrics')) pageX = _obj['layoutViewport']['pageX'] pageY = _obj['layoutViewport']['pageY'] clip = {} clip.update(boundingBox) clip['x'] = (clip['x'] + pageX) clip['y'] = (clip['y'] + pageY) opt = { 'clip': clip, } opt.update(options) imageData = (await self._page.screenshot(opt)) if needsViewportReset: (await self._page.setViewport(original_viewport)) return imageData
def __init__(self, options: Dict[str, Any] = None, # noqa: C901 **kwargs: Any) -> None: """Make new launcher.""" self.options = merge_dict(options, kwargs) self.port = get_free_port() self.url = f'http://127.0.0.1:{self.port}' self.chrome_args: List[str] = [] if not self.options.get('ignoreDefaultArgs', False): self.chrome_args.extend(DEFAULT_ARGS) self.chrome_args.append( f'--remote-debugging-port={self.port}', ) self.chromeClosed = True if self.options.get('appMode', False): self.options['headless'] = False elif not self.options.get('ignoreDefaultArgs', False): self.chrome_args.extend(AUTOMATION_ARGS) self._tmp_user_data_dir: Optional[str] = None self._parse_args() if self.options.get('devtools'): self.chrome_args.append('--auto-open-devtools-for-tabs') self.options['headless'] = False if 'headless' not in self.options or self.options.get('headless'): self.chrome_args.extend([ '--headless', '--disable-gpu', '--hide-scrollbars', '--mute-audio', ]) if 'executablePath' in self.options: self.exec = self.options['executablePath'] else: if not check_chromium(): download_chromium() self.exec = str(chromium_excutable()) self.cmd = [self.exec] + self.chrome_args
async def up(self, options: dict = None, **kwargs: Any) -> None: """Release pressed button (dispatches ``mouseup`` event). This method accepts the following options: * ``button`` (str): ``left``, ``right``, or ``middle``, defaults to ``left``. * ``clickCount`` (int): defaults to 1. """ options = merge_dict(options, kwargs) self._button = 'none' await self._client.send('Input.dispatchMouseEvent', { 'type': 'mouseReleased', 'button': options.get('button', 'left'), 'x': self._x, 'y': self._y, 'modifiers': self._keyboard._modifiers, 'clickCount': options.get('clickCount') or 1, })
async def down(self, key: str, options: dict = None, **kwargs: Any) -> None: """Dispatches a ``keydown`` event with ``key``. If ``key`` is a single character and no modifier keys besides ``shift`` are being held down, and a ``keyparess``/``input`` event will also generated. The ``text`` option can be specified to force an ``input`` event to be generated. If ``key`` is a modifier key, like ``Shift``, ``Meta``, or ``Alt``, subsequent key presses will be sent with that modifier active. To release the modifier key, use :meth:`up` method. :arg str key: Name of key to press, such as ``ArrowLeft``. :arg dict options: Option can have ``text`` field, and if this option spedified, generate an input event with this text. """ options = merge_dict(options, kwargs) description = self._keyDescriptionForString(key) autoRepeat = description['key'] in self._pressedKeys self._pressedKeys.add(description['code']) self._modifiers |= self._modifierBit(description['key']) text = options.get('text') if text is None: text = description['text'] await self._client.send( 'Input.dispatchKeyEvent', { 'type': 'keyDown' if text else 'rawKeyDown', 'modifiers': self._modifiers, 'windowsVirtualKeyCode': description['keyCode'], 'code': description['code'], 'key': description['key'], 'text': text, 'unmodifiedText': text, 'autoRepeat': autoRepeat, 'location': description['location'], 'isKeypad': description['location'] == 3, })
def __init__( self, pageurl, sitekey, proxy=None, proxy_auth=None, options={}, **kwargs, ): self.options = merge_dict(options, kwargs) self.url = pageurl self.sitekey = sitekey self.proxy = proxy self.proxy_auth = proxy_auth self.headless = settings["headless"] self.cookies = [] self.proc_id = self.proc_count type(self).proc_count += 1
async def click(self, options: dict = None, **kwargs: Any) -> None: """Click the center of this element. If needed, this method scrolls element into view. If the element is detached from DOM, the method raises ``ElementHandleError``. ``options`` can contain the following fields: * ``button`` (str): ``left``, ``right``, of ``middle``, defaults to ``left``. * ``clickCount`` (int): Defaults to 1. * ``delay`` (int|float): Time to wait between ``mousedown`` and ``mouseup`` in milliseconds. Defaults to 0. """ options = merge_dict(options, kwargs) obj = await self._visibleCenter() x = obj.get('x', 0) y = obj.get('y', 0) await self._page.mouse.click(x, y, options)
async def screenshot(self, options: dict = None, **kwargs: Any) -> bytes: """Take a screen shot. The following options are available: * ``path`` (str): The file path to save the image to. The screenshot type will be inferred from the file extension. * ``type`` (str): Specify screenshot type, can be either ``jpeg`` or ``png``. Defaults to ``png``. * ``quality`` (int): The quality of the image, between 0-100. Not applicable to ``png`` image. * ``fullPage`` (bool): When true, take a screenshot of the full scrollable page. Defaults to ``False``. * ``clip`` (dict): An object which specifies clipping region of the page. This option should have the following fields: * ``x`` (int): x-coordinate of top-left corner of clip area. * ``y`` (int): y-coordinate of top-left corner of clip area. * ``width`` (int): width of clipping area. * ``height`` (int): height of clipping area. * ``omitBackground`` (bool): Hide default white background and allow capturing screenshot with transparency. """ options = merge_dict(options, kwargs) screenshotType = None if 'type' in options: screenshotType = options['type'] if screenshotType not in ['png', 'jpeg']: raise ValueError(f'Unknown type value: {screenshotType}') elif 'path' in options: mimeType, _ = mimetypes.guess_type(options['path']) if mimeType == 'image/png': screenshotType = 'png' elif mimeType == 'image/jpeg': screenshotType = 'jpeg' else: raise ValueError('Unsupported screenshot ' f'mime type: {mimeType}') if not screenshotType: screenshotType = 'png' return await self._screenshotTask(screenshotType, options)
def _waitForSelectorOrXPath(self, selectorOrXPath: str, isXPath: bool, options: dict = None, **kwargs: Any ) -> 'WaitTask': options = merge_dict(options, kwargs) timeout = options.get('timeout', 30000) waitForVisible = bool(options.get('visible')) waitForHidden = bool(options.get('hidden')) polling = 'raf' if waitForHidden or waitForVisible else 'mutation' predicate = ''' (selectorOrXPath, isXPath, waitForVisible, waitForHidden) => { const node = isXPath ? document.evaluate(selectorOrXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue : document.querySelector(selectorOrXPath); if (!node) return waitForHidden; if (!waitForVisible && !waitForHidden) return node; const element = /** @type {Element} */ (node.nodeType === Node.TEXT_NODE ? node.parentElement : node); const style = window.getComputedStyle(element); const isVisible = style && style.visibility !== 'hidden' && hasVisibleBoundingBox(); const success = (waitForVisible === isVisible || waitForHidden === !isVisible) return success ? node : null function hasVisibleBoundingBox() { const rect = element.getBoundingClientRect(); return !!(rect.top || rect.bottom || rect.width || rect.height); } } ''' # noqa: E501 return WaitTask( self, predicate, f'{"XPath" if isXPath else "selector"} "{selectorOrXPath}"', polling, timeout, self._client._loop, selectorOrXPath, isXPath, waitForVisible, waitForHidden, )
async def click(self, x: float, y: float, options: dict = None, **kwargs: Any) -> None: """Click button at (``x``, ``y``). Shortcut to :meth:`move`, :meth:`down`, and :meth:`up`. This method accepts the following options: * ``button`` (str): ``left``, ``right``, or ``middle``, defaults to ``left``. * ``clickCount`` (int): defaults to 1. * ``delay`` (int|float): Time to wait between ``mousedown`` and ``mouseup`` in milliseconds. Defaults to 0. """ options = merge_dict(options, kwargs) await self.move(x, y) await self.down(options) if options and options.get('delay'): await asyncio.sleep(options.get('delay', 0) / 1000) await self.up(options)
async def press(self, key: str, options: Dict = None, **kwargs: Any ) -> None: """Press ``key`` onto the element. This method focuses the element, and then uses :meth:`pyppeteer.input.keyboard.down` and :meth:`pyppeteer.input.keyboard.up`. :arg str key: Name of key to press, such as ``ArrowLeft``. This method accepts the following options: * ``text`` (str): If specified, generates an input event with this text. * ``delay`` (int|float): Time to wait between ``keydown`` and ``keyup``. Defaults to 0. """ options = merge_dict(options, kwargs) await self.focus() await self._page.keyboard.press(key, options)
async def startJSCoverage(self, options: Dict = None, **kwargs: Any ) -> None: """Start JS coverage measurement. Available options are: * ``resetOnNavigation`` (bool): Whether to reset coverage on every navigation. Defaults to ``True``. * ``reportAnonymousScript`` (bool): Whether anonymous script generated by the page should be reported. Defaults to ``False``. .. note:: Anonymous scripts are ones that don't have an associated url. These are scripts that are dynamically created on the page using ``eval`` of ``new Function``. If ``reportAnonymousScript`` is set to ``True``, anonymous scripts will have ``__pyppeteer_evaluation_script__`` as their url. """ options = merge_dict(options, kwargs) await self._jsCoverage.start(options)
async def click(self, options: dict = None, **kwargs: Any) -> None: """Click the center of this element. If needed, this method scrolls element into view. If the element is detached from DOM, the method raises ``ElementHandleError``. ``options`` can contain the following fields: * ``button`` (str): ``left``, ``right``, of ``middle``, defaults to ``left``. * ``clickCount`` (int): Defaults to 1. * ``delay`` (int|float): Time to wait between ``mousedown`` and ``mouseup`` in milliseconds. Defaults to 0. """ options = merge_dict(options, kwargs) await self._scrollIntoViewIfNeeded() obj = await self._clickablePoint() x = obj.get('x', 0) y = obj.get('y', 0) await self._page.mouse.click(x, y, options)
async def connect(options: dict = None, **kwargs: Any) -> Browser: "Connect to the existing chrome.\n\n ``browserWSEndpoint`` option is necessary to connect to the chrome. The\n format is ``ws://${host}:${port}/devtools/browser/<id>``. This value can\n get by :attr:`~pyppeteer.browser.Browser.wsEndpoint`.\n\n Available options are:\n\n * ``browserWSEndpoint`` (str): A browser websocket endpoint to connect to.\n (**required**)\n * ``ignoreHTTPSErrors`` (bool): Whether to ignore HTTPS errors. Defaults to\n ``False``.\n * ``slowMo`` (int|float): Slow down pyppeteer's by the specified amount of\n milliseconds.\n * ``logLevel`` (int|str): Log level to print logs. Defaults to same as the\n root logger.\n * ``loop`` (asyncio.AbstractEventLoop): Event loop (**experimental**).\n " options = merge_dict(options, kwargs) logLevel = options.get('logLevel') if logLevel: logging.getLogger('pyppeteer').setLevel(logLevel) browserWSEndpoint = options.get('browserWSEndpoint') if (not browserWSEndpoint): raise BrowserError('Need `browserWSEndpoint` option.') connectionDelay = options.get('slowMo', 0) connection = Connection(browserWSEndpoint, options.get('loop', asyncio.get_event_loop()), connectionDelay) browserContextIds = (await connection.send('Target.getBrowserContexts')).get( 'browserContextIds', []) ignoreHTTPSErrors = bool(options.get('ignoreHTTPSErrors', False)) return (await Browser.create(connection, browserContextIds, ignoreHTTPSErrors, True, None, (lambda: connection.send('Browser.close'))))
def defaultArgs(options: Dict = None, **kwargs: Any) -> List[str]: # noqa: C901,E501 """Get the default flags the chromium will be launched with. ``options`` or keyword arguments are set of configurable options to set on the browser. Can have the following fields: * ``headless`` (bool): Whether to run browser in headless mode. Defaults to ``True`` unless the ``devtools`` option is ``True``. * ``args`` (List[str]): Additional arguments to pass to the browser instance. The list of chromium flags can be found `here <http://peter.sh/experiments/chromium-command-line-switches/>`__. * ``userDataDir`` (str): Path to a User Data Directory. * ``devtools`` (bool): Whether to auto-open DevTools panel for each tab. If this option is ``True``, the ``headless`` option will be set ``False``. """ options = merge_dict(options, kwargs) devtools = options.get('devtools', False) headless = options.get('headless', not devtools) args = options.get('args', list()) userDataDir = options.get('userDataDir') chromeArguments = copy(DEFAULT_ARGS) if userDataDir: chromeArguments.append(f'--user-data-dir={userDataDir}') if devtools: chromeArguments.append('--auto-open-devtools-for-tabs') if headless: chromeArguments.extend(( '--headless', '--hide-scrollbars', '--mute-audio', )) if current_platform().startswith('win'): chromeArguments.append('--disable-gpu') if all(map(lambda arg: arg.startswith('-'), args)): # type: ignore chromeArguments.append('about:blank') chromeArguments.extend(args) return chromeArguments
def __init__(self, frameManager: FrameManager, frame: Frame, timeout: int, options: Dict = None, **kwargs: Any) -> None: """Make new navigator watcher.""" options = merge_dict(options, kwargs) self._validate_options(options) self._frameManager = frameManager self._frame = frame self._initialLoaderId = frame._loaderId self._timeout = timeout self._hasSameDocumentNavigation = False self._eventListeners = [ helper.addEventListener( self._frameManager, FrameManager.Events.LifecycleEvent, self._checkLifecycleComplete, ), helper.addEventListener( self._frameManager, FrameManager.Events.FrameNavigatedWithinDocument, self._navigatedWithinDocument, ), helper.addEventListener( self._frameManager, FrameManager.Events.FrameDetached, self._checkLifecycleComplete, ), ] self._loop = self._frameManager._client._loop self._lifecycleCompletePromise = self._loop.create_future() self._navigationPromise = self._loop.create_task( asyncio.wait([ self._lifecycleCompletePromise, self._createTimeoutPromise(), ], return_when=concurrent.futures.FIRST_COMPLETED)) self._navigationPromise.add_done_callback(lambda fut: self._cleanup())
def waitFor(self, selectorOrFunctionOrTimeout: Union[(str, int, float)], options: dict = None, *args: Any, **kwargs: Any) -> Union[(Awaitable, 'WaitTask')]: 'Wait until `selectorOrFunctionOrTimeout`.\n\n Details see :meth:`pyppeteer.page.Page.waitFor`.\n ' options = merge_dict(options, kwargs) if isinstance(selectorOrFunctionOrTimeout, (int, float)): fut = asyncio.ensure_future( asyncio.sleep((selectorOrFunctionOrTimeout / 1000))) return fut if (not isinstance(selectorOrFunctionOrTimeout, str)): fut = asyncio.get_event_loop().create_future() fut.set_exception( TypeError(('Unsupported target type: ' + str(type(selectorOrFunctionOrTimeout))))) return fut if (args or helper.is_jsfunc(selectorOrFunctionOrTimeout)): return self.waitForFunction(selectorOrFunctionOrTimeout, options, *args) return self.waitForSelector(selectorOrFunctionOrTimeout, options)
async def waitForNavigation(self, options: dict = None, **kwargs: Any) -> Optional[Response]: """Wait for navigation completes.""" options = merge_dict(options, kwargs) mainFrame = self._frameManager.mainFrame if mainFrame is None: raise PageError('No main frame.') watcher = NavigatorWatcher(self._frameManager, mainFrame, 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 start(self, options: Dict = None, **kwargs: Any) -> None: """Start coverage measurement.""" options = merge_dict(options, kwargs) if self._enabled: raise PageError('CSSCoverage is already enabled.') self._resetOnNavigation = (True if 'resetOnNavigation' not in options else bool(options['resetOnNavigation'])) self._enabled = True self._stylesheetURLs.clear() self._stylesheetSources.clear() self._eventListeners = [ helper.addEventListener( self._client, 'CSS.styleSheetAdded', lambda e: self._client. _loop.create_task(self._onStyleSheet(e))), helper.addEventListener(self._client, 'Runtime.executionContextsCleared', self._onExecutionContextsCleared), ] await self._client.send('DOM.enable') await self._client.send('CSS.enable') await self._client.send('CSS.startRuleUsageTracking')
async def start(self, options: dict = None, **kwargs: Any) -> None: """Start tracing. Only one trace can be active at a time per browser. This method accepts the following options: * ``path`` (str): A path to write the trace file to. * ``screenshots`` (bool): Capture screenshots in the trace. * ``categories`` (List[str]): Specify custom categories to use instead of default. """ options = merge_dict(options, kwargs) defaultCategories = [ '-*', 'devtools.timeline', 'v8.execute', 'disabled-by-default-devtools.timeline', 'disabled-by-default-devtools.timeline.frame', 'toplevel', 'blink.console', 'blink.user_timing', 'latencyInfo', 'disabled-by-default-devtools.timeline.stack', 'disabled-by-default-v8.cpu_profiler', 'disabled-by-default-v8.cpu_profiler.hires', ] categoriesArray = options.get('categories', defaultCategories) if 'screenshots' in options: categoriesArray.append('disabled-by-default-devtools.screenshot') self._path = options.get('path', '') self._recording = True await self._client.send( 'Tracing.start', { 'transferMode': 'ReturnAsStream', 'categories': ','.join(categoriesArray), })
def __init__(self, pageurl, loop=None, proxy=None, proxy_auth=None, options=None, lang='en-US', chromePath=None, **kwargs): self.url = pageurl self.loop = loop or util.get_event_loop() self.proxy = proxy self.proxy_auth = proxy_auth self.options = merge_dict({} if options is None else options, kwargs) self.chromePath = chromePath super(Solver, self).__init__(loop=loop, proxy=proxy, proxy_auth=proxy_auth, language=lang, options=options, chromePath=chromePath)
async def press(self, key: str, options: Dict = None, **kwargs: Any) -> None: """Press ``key`` onto the element. This method focuses the element, and then uses :meth:`pyppeteer.input.keyboard.down` and :meth:`pyppeteer.input.keyboard.up`. :arg str key: Name of key to press, such as ``ArrowLeft``. This method accepts the following options: * ``text`` (str): If specified, generates an input event with this text. * ``delay`` (int|float): Time to wait between ``keydown`` and ``keyup``. Defaults to 0. """ options = merge_dict(options, kwargs) await self.focus() await self._page.keyboard.press(key, options)
def _waitForSelectorOrXPath(self, selectorOrXPath: str, isXPath: bool, options: dict = None, **kwargs: Any ) -> 'WaitTask': options = merge_dict(options, kwargs) timeout = options.get('timeout', 30000) waitForVisible = bool(options.get('visible')) waitForHidden = bool(options.get('hidden')) polling = 'raf' if waitForHidden or waitForVisible else 'mutation' predicate = ''' (selectorOrXPath, isXPath, waitForVisible, waitForHidden) => { const node = isXPath ? document.evaluate(selectorOrXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue : document.querySelector(selectorOrXPath); if (!node) return waitForHidden; if (!waitForVisible && !waitForHidden) return node; const element = /** @type {Element} */ (node.nodeType === Node.TEXT_NODE ? node.parentElement : node); const style = window.getComputedStyle(element); const isVisible = style && style.visibility !== 'hidden' && hasVisibleBoundingBox(); const success = (waitForVisible === isVisible || waitForHidden === !isVisible) return success ? node : null function hasVisibleBoundingBox() { const rect = element.getBoundingClientRect(); return !!(rect.top || rect.bottom || rect.width || rect.height); } } ''' # noqa: E501 return self.waitForFunction( predicate, {'timeout': timeout, 'polling': polling}, selectorOrXPath, isXPath, waitForVisible, waitForHidden, )
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')) 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 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 screenshot(self, options: Dict = None, **kwargs: Any) -> bytes: """Take a screenshot of this element. If the element is detached from DOM, this method raises an ``ElementHandleError``. Available options are same as :meth:`pyppeteer.page.Page.screenshot`. """ options = merge_dict(options, kwargs) await self._scrollIntoViewIfNeeded() _obj = await self._client.send('Page.getLayoutMetrics') pageX = _obj['layoutViewport']['pageX'] pageY = _obj['layoutViewport']['pageY'] clip = await self.boundingBox() if not clip: raise ElementHandleError('Node is not visible.') clip['x'] = clip['x'] + pageX clip['y'] = clip['y'] + pageY opt = {'clip': clip} opt.update(options) return await self._page.screenshot(opt)
async def move(self, x: float, y: float, options: dict = None, **kwargs: Any) -> None: """Move mouse cursor (dispatches a ``mousemove`` event). Options can accepts ``steps`` (int) field. If this ``steps`` option specified, Sends intermediate ``mousemove`` events. Defaults to 1. """ options = merge_dict(options, kwargs) fromX = self._x fromY = self._y self._x = x self._y = y steps = options.get('steps', 1) for i in range(1, steps + 1): x = round(fromX + (self._x - fromX) * (i / steps)) y = round(fromY + (self._y - fromY) * (i / steps)) await self._client.send('Input.dispatchMouseEvent', { 'type': 'mouseMoved', 'button': self._button, 'x': x, 'y': y, 'modifiers': self._keyboard._modifiers, })
async def connect(options: dict = None, **kwargs: Any) -> Browser: """Connect to the existing chrome. ``browserWSEndpoint`` option is necessary to connect to the chrome. The format is ``ws://${host}:${port}/devtools/browser/<id>``. This value can get by :attr:`~pyppeteer.browser.Browser.wsEndpoint`. Available options are: * ``browserWSEndpoint`` (str): A browser websocket endpoint to connect to. (**required**) * ``ignoreHTTPSErrors`` (bool): Whether to ignore HTTPS errors. Defaults to ``False``. * ``slowMo`` (int|float): Slow down pyppeteer's by the specified amount of milliseconds. """ options = merge_dict(options, kwargs) browserWSEndpoint = options.get('browserWSEndpoint') if not browserWSEndpoint: raise BrowserError('Need `browserWSEndpoint` option.') connection = Connection(browserWSEndpoint) return await Browser.create(connection, options, None, lambda: connection.send('Browser.close'))
def __init__( self, options, # noqa: C901 **kwargs) -> None: """Make new launcher.""" self.options = merge_dict(options, kwargs) self.port = get_free_port() self.url = f'http://127.0.0.1:{self.port}' self.chrome_args = [f'--remote-debugging-port={self.port}'] self._loop = self.options.get('loop', asyncio.get_event_loop()) logLevel = self.options.get('logLevel') if logLevel: logging.getLogger('pyppeteer').setLevel(logLevel) self.chromeClosed = True if self.options.get('appMode', False): self.options['headless'] = False self._tmp_user_data_dir = None self._parse_args() if self.options.get('devtools'): self.chrome_args.append('--auto-open-devtools-for-tabs') self.options['headless'] = False if 'headless' not in self.options or self.options.get('headless'): self.chrome_args.extend([ '--headless', '--disable-gpu', '--hide-scrollbars', '--mute-audio', ]) if 'executablePath' in self.options: self.exec = self.options['executablePath'] else: if not check_chromium(): download_chromium() self.exec = str(chromium_excutable()) self.cmd = [self.exec] + self.chrome_args
def __init__(self, connection: Connection, options: Dict=None, process: Optional[Popen]=None, closeCallback: Callable[([], Awaitable[None])]=None, **kwargs: Any) -> None: super().__init__() options = merge_dict(options, kwargs) self._ignoreHTTPSErrors = bool(options.get('ignoreHTTPSErrors', False)) self._appMode = bool(options.get('appMode', False)) self._process = process self._screenshotTaskQueue = [] self._connection = connection def _dummy_callback() -> Awaitable[None]: fut = asyncio.get_event_loop().create_future() fut.set_result(None) return fut if closeCallback: self._closeCallback = closeCallback else: self._closeCallback = _dummy_callback self._targets = dict() self._connection.setClosedCallback( (lambda: self.emit(Browser.Events.Disconnected))) self._connection.on('Target.targetCreated', self._targetCreated) self._connection.on('Target.targetDestroyed', self._targetDestroyed) self._connection.on('Target.targetInfoChanged', self._targetInfoChanged)
async def waitForTarget(self, predicate, options=None, **kwargs): options = merge_dict(options, kwargs) timeout = options.get("timeout", 30000) with contextlib.suppress(StopIteration): return next(filter(lambda u: u == predicate, self.targets())) resolve = self._connection._loop.create_future() def check(target): if predicate(target): resolve.set_result(target) self.on(Browser.Events.TargetCreated, check) self.on(Browser.Events.TargetChanged, check) try: if timeout: return await asyncio.wait_for(resolve, timeout=timeout) return await resolve except asyncio.TimeoutError: raise TimeoutError( "waiting for target target: " f"timeout {timeout * 1000}ms exceeded") from None finally: self.remove_listener(Browser.Events.TargetCreated, check) self.remove_listener(Browser.Events.TargetChanged, check)
async def press(self, key: str, options: Dict = None, **kwargs: Any ) -> None: """Press ``key``. If ``key`` is a single character and no modifier keys besides ``Shift`` are being held down, a ``keypress``/``input`` event will also generated. The ``text`` option can be specified to force an input event to be generated. :arg str key: Name of key to press, such as ``ArrowLeft``. This method accepts the following options: * ``text`` (str): If specified, generates an input event with this text. * ``delay`` (int|float): Time to wait between ``keydown`` and ``keyup``. Defaults to 0. """ options = merge_dict(options, kwargs) await self.down(key, options) if options and options.get('delay'): await asyncio.sleep(options['delay'] / 1000) await self.up(key)
def __init__( self, options: Dict[str, Any] = None, # noqa: C901 **kwargs: Any) -> None: """Make new launcher.""" options = merge_dict(options, kwargs) self.port = get_free_port() self.url = f'http://127.0.0.1:{self.port}' self._loop = options.get('loop', asyncio.get_event_loop()) self.chromeClosed = True ignoreDefaultArgs = options.get('ignoreDefaultArgs', False) args: List[str] = options.get('args', list()) self.dumpio = options.get('dumpio', False) executablePath = options.get('executablePath') self.env = options.get('env') self.handleSIGINT = options.get('handleSIGINT', True) self.handleSIGTERM = options.get('handleSIGTERM', True) self.handleSIGHUP = options.get('handleSIGHUP', True) self.ignoreHTTPSErrors = options.get('ignoreHTTPSErrors', False) self.defaultViewport = options.get('defaultViewport', { 'width': 800, 'height': 600 }) # noqa: E501 self.slowMo = options.get('slowMo', 0) self.timeout = options.get('timeout', 30000) self.autoClose = options.get('autoClose', True) logLevel = options.get('logLevel') if logLevel: logging.getLogger('pyppeteer').setLevel(logLevel) self.chromeArguments: List[str] = list() if not ignoreDefaultArgs: self.chromeArguments.extend(defaultArgs(options)) elif isinstance(ignoreDefaultArgs, list): self.chromeArguments.extend( filter( lambda arg: arg not in ignoreDefaultArgs, defaultArgs(options), )) else: self.chromeArguments.extend(args) self.temporaryUserDataDir: Optional[str] = None if not any(arg for arg in self.chromeArguments if arg.startswith('--remote-debugging-')): self.chromeArguments.append(f'--remote-debugging-port={self.port}') if not any(arg for arg in self.chromeArguments if arg.startswith('--user-data-dir')): if not CHROME_PROFILE_PATH.exists(): CHROME_PROFILE_PATH.mkdir(parents=True) self.temporaryUserDataDir = tempfile.mkdtemp( dir=str(CHROME_PROFILE_PATH)) # noqa: E501 self.chromeArguments.append( f'--user-data-dir={self.temporaryUserDataDir}') # noqa: E501 self.chromeExecutable = executablePath if not self.chromeExecutable: if not check_chromium(): download_chromium() self.chromeExecutable = str(chromium_executable()) self.cmd = [self.chromeExecutable] + self.chromeArguments
async def startCSSCoverage(self, options: Dict = None, **kwargs: Any) -> None: 'Start CSS coverage measurement.\n\n Available options are:\n\n * ``resetOnNavigation`` (bool): Whether to reset coverage on every\n navigation. Defaults to ``True``.\n ' options = merge_dict(options, kwargs) (await self._cssCoverage.start(options))
async def pdf(self, options: dict = None, **kwargs: Any) -> bytes: """Generate a pdf of the page. Options: * ``path`` (str): The file path to save the PDF. * ``scale`` (float): Scale of the webpage rendering, defaults to ``1``. * ``displayHeaderFooter`` (bool): Display header and footer. Defaults to ``False``. * ``headerTemplate`` (str): HTML template for the print header. Should be valid HTML markup with following classes. * ``data``: formatted print date * ``title``: document title * ``url``: document location * ``pageNumber``: current page number * ``totalPages``: total pages in the document * ``footerTemplate`` (str): HTML template for the print footer. Should use the same template as ``headerTemplate``. * ``printBackground`` (bool): Print background graphics. Defaults to ``False``. * ``landscape`` (bool): Paper orientation. Defaults to ``False``. * ``pageRanges`` (string): Paper ranges to print, e.g., '1-5,8,11-13'. Defaults to empty string, which means all pages. * ``foramt`` (str): Paper format. If set, takes prioprity over ``width`` or ``height``. Defaults to ``Letter``. * ``width`` (str): Paper width, accepts values labeled with units. * ``height`` (str): Paper height, accepts values labeled with units. * ``margin`` (dict): Paper margins, defaults to ``None``. * ``top`` (str): Top margin, accepts values labeled with units. * ``right`` (str): Right margin, accepts values labeled with units. * ``bottom`` (str): Bottom margin, accepts values labeled with units. * ``left`` (str): Left margin, accepts values labeled with units. :return bytes: Return generated PDF ``bytes`` object. """ options = merge_dict(options, kwargs) scale = options.get('scale', 1) displayHeaderFooter = bool(options.get('displayHeaderFooter')) headerTemplate = options.get('headerTemplate', '') footerTemplate = options.get('footerTemplate', '') printBackground = bool(options.get('printBackground')) landscape = bool(options.get('landscape')) pageRanges = options.get('pageRanges', '') paperWidth = 8.5 paperHeight = 11.0 if 'format' in options: fmt = Page.PaperFormats.get(options['format'].lower()) if not fmt: raise ValueError('Unknown paper format: ' + options['format']) paperWidth = fmt['width'] paperHeight = fmt['height'] else: paperWidth = convertPrintParameterToInches( options.get('width')) or paperWidth # noqa: E501 paperHeight = convertPrintParameterToInches( options.get('height')) or paperHeight # noqa: E501 marginOptions = options.get('margin', {}) marginTop = convertPrintParameterToInches( marginOptions.get('top')) or 0 # noqa: E501 marginLeft = convertPrintParameterToInches( marginOptions.get('left')) or 0 # noqa: E501 marginBottom = convertPrintParameterToInches( marginOptions.get('bottom')) or 0 # noqa: E501 marginRight = convertPrintParameterToInches( marginOptions.get('right')) or 0 # noqa: E501 result = await self._client.send( 'Page.printToPDF', dict(landscape=landscape, displayHeaderFooter=displayHeaderFooter, headerTemplate=headerTemplate, footerTemplate=footerTemplate, printBackground=printBackground, scale=scale, paperWidth=paperWidth, paperHeight=paperHeight, marginTop=marginTop, marginBottom=marginBottom, marginLeft=marginLeft, marginRight=marginRight, pageRanges=pageRanges)) buffer = base64.b64decode(result.get('data', b'')) if 'path' in options: with open(options['path'], 'wb') as f: f.write(buffer) return buffer