Skip to content

Commit bc2adb8

Browse files
Add "take_screenshot" method for page and element (#102)
* Add "take_screenshot" for page and element it returns the screenshot as base64 string instead of saving to local file * Update CHANGELOG.md --------- Co-authored-by: Stephan Lensky <8302875+stephanlensky@users.noreply.github.com>
1 parent c3e9848 commit bc2adb8

File tree

3 files changed

+88
-37
lines changed

3 files changed

+88
-37
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
### Added
1313

14+
- Added `tab.take_screenshot` and `element.take_screenshot` methods to return screenshot as base64 string @falmar
15+
1416
### Changed
1517

18+
- Changed `take.save_screenshot` and `element.save_screenshot` to use `self.take_screenshot` to fetch screenshot data and only perform the file saving @falmar
19+
1620
### Removed
1721

1822
## [0.6.1] - 2025-04-25

zendriver/core/element.py

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,46 @@ async def query_selector(self, selector):
810810
await self.update()
811811
return await self.tab.query_selector(selector, self)
812812

813+
async def take_screenshot(
814+
self,
815+
format: str = "jpeg",
816+
scale: typing.Optional[typing.Union[int, float]] = 1,
817+
):
818+
"""
819+
Takes a screenshot of this element (only)
820+
This is not the same as :py:obj:`Tab.take_screenshot`, which takes a "regular" screenshot
821+
822+
When the element is hidden, or has no size, or is otherwise not capturable, a RuntimeError is raised
823+
824+
:param format: jpeg or png (defaults to jpeg)
825+
:type format: str
826+
:param scale: the scale of the screenshot, eg: 1 = size as is, 2 = double, 0.5 is half
827+
:return: screenshot data as base64 encoded
828+
:rtype: str
829+
"""
830+
pos = await self.get_position()
831+
if not pos:
832+
raise RuntimeError(
833+
"could not determine position of element. probably because it's not in view, or hidden"
834+
)
835+
viewport = pos.to_viewport(scale)
836+
await self.tab.sleep()
837+
838+
data = await self._tab.send(
839+
cdp.page.capture_screenshot(
840+
format, clip=viewport, capture_beyond_viewport=True
841+
)
842+
)
843+
844+
if not data:
845+
from .connection import ProtocolException
846+
847+
raise ProtocolException(
848+
"could not take screenshot. most possible cause is the page has not finished loading yet."
849+
)
850+
851+
return str(data)
852+
813853
async def save_screenshot(
814854
self,
815855
filename: typing.Optional[PathLike] = "auto",
@@ -830,14 +870,8 @@ async def save_screenshot(
830870
:return: the path/filename of saved screenshot
831871
:rtype: str
832872
"""
833-
pos = await self.get_position()
834-
if not pos:
835-
raise RuntimeError(
836-
"could not determine position of element. probably because it's not in view, or hidden"
837-
)
838-
viewport = pos.to_viewport(scale)
839-
path = None
840873
await self.tab.sleep()
874+
841875
if not filename or filename == "auto":
842876
parsed = urllib.parse.urlparse(self.tab.target.url)
843877
parts = parsed.path.split("/")
@@ -848,26 +882,15 @@ async def save_screenshot(
848882
ext = ""
849883
if format.lower() in ["jpg", "jpeg"]:
850884
ext = ".jpg"
851-
format = "jpeg"
852885
elif format.lower() in ["png"]:
853886
ext = ".png"
854-
format = "png"
855887
path = pathlib.Path(candidate + ext)
856888
else:
857889
path = pathlib.Path(filename)
858890

859891
path.parent.mkdir(parents=True, exist_ok=True)
860-
data = await self._tab.send(
861-
cdp.page.capture_screenshot(
862-
format, clip=viewport, capture_beyond_viewport=True
863-
)
864-
)
865-
if not data:
866-
from .connection import ProtocolException
867892

868-
raise ProtocolException(
869-
"could not take screenshot. most possible cause is the page has not finished loading yet."
870-
)
893+
data = await self.take_screenshot(format, scale)
871894

872895
data_bytes = base64.b64decode(data)
873896
if not path:

zendriver/core/tab.py

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,7 +1285,7 @@ async def download_file(self, url: str, filename: Optional[PathLike] = None):
12851285
arguments=[cdp.runtime.CallArgument(object_id=body.object_id)],
12861286
)
12871287
)
1288-
1288+
12891289
async def save_snapshot(self, filename: str = "snapshot.mhtml") -> None:
12901290
"""
12911291
Saves a snapshot of the page.
@@ -1302,6 +1302,44 @@ async def save_snapshot(self, filename: str = "snapshot.mhtml") -> None:
13021302

13031303
with open(filename, "w") as file:
13041304
file.write(data)
1305+
1306+
async def take_screenshot(
1307+
self,
1308+
format: str = "jpeg",
1309+
full_page: bool = False,
1310+
) -> str:
1311+
"""
1312+
Takes a screenshot of the page.
1313+
This is not the same as :py:obj:`Element.take_screenshot`, which takes a screenshot of a single element only
1314+
1315+
:param format: jpeg or png (defaults to jpeg)
1316+
:type format: str
1317+
:param full_page: when False (default) it captures the current viewport. when True, it captures the entire page
1318+
:type full_page: bool
1319+
:return: screenshot data as base64 encoded
1320+
:rtype: str
1321+
"""
1322+
if self.target is None:
1323+
raise ValueError("target is none")
1324+
1325+
await self.sleep() # update the target's url
1326+
1327+
if format.lower() in ["jpg", "jpeg"]:
1328+
format = "jpeg"
1329+
elif format.lower() in ["png"]:
1330+
format = "png"
1331+
1332+
data = await self.send(
1333+
cdp.page.capture_screenshot(
1334+
format_=format, capture_beyond_viewport=full_page
1335+
)
1336+
)
1337+
if not data:
1338+
raise ProtocolException(
1339+
"could not take screenshot. most possible cause is the page has not finished loading yet."
1340+
)
1341+
1342+
return str(data)
13051343

13061344
async def save_screenshot(
13071345
self,
@@ -1322,19 +1360,11 @@ async def save_screenshot(
13221360
:return: the path/filename of saved screenshot
13231361
:rtype: str
13241362
"""
1325-
if self.target is None:
1326-
raise ValueError("target is none")
1327-
1328-
await self.sleep() # update the target's url
1329-
path = None
1330-
13311363
if format.lower() in ["jpg", "jpeg"]:
13321364
ext = ".jpg"
1333-
format = "jpeg"
13341365

13351366
elif format.lower() in ["png"]:
13361367
ext = ".png"
1337-
format = "png"
13381368

13391369
if not filename or filename == "auto":
13401370
parsed = urllib.parse.urlparse(self.target.url)
@@ -1347,15 +1377,9 @@ async def save_screenshot(
13471377
else:
13481378
path = pathlib.Path(filename)
13491379
path.parent.mkdir(parents=True, exist_ok=True)
1350-
data = await self.send(
1351-
cdp.page.capture_screenshot(
1352-
format_=format, capture_beyond_viewport=full_page
1353-
)
1354-
)
1355-
if not data:
1356-
raise ProtocolException(
1357-
"could not take screenshot. most possible cause is the page has not finished loading yet."
1358-
)
1380+
1381+
data = await self.take_screenshot(format=format, full_page=full_page)
1382+
13591383
import base64
13601384

13611385
data_bytes = base64.b64decode(data)

0 commit comments

Comments
 (0)