Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/multi.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,29 @@ You can include desired `height`, `width`, `quality`, `wait` and `wait_for` opti
wait_for: document.querySelector('#bighead')
```

To save a WebP image, use an `output` filename ending in `.webp`. WebP is lossless by default; add `quality` from 0 to 99 for lossy compression:

```yaml
- output: simon.webp
url: https://simonwillison.net/
height: 800
- output: bighead.webp
url: https://simonwillison.net/
selector: "#bighead"
quality: 80
```

You can also specify `format: png`, `format: jpeg` or `format: webp` on each item. This takes precedence over the output filename extension:

```yaml
- output: screenshot
url: https://simonwillison.net/
format: webp
quality: 80
```

Without `format` or a `.webp` output filename, `quality` selects JPEG. Combining `format: png` with `quality` is an error. See {ref}`screenshots` for more about image formats and quality.

(multi-har)=
## Recording to an HTTP Archive

Expand Down
60 changes: 55 additions & 5 deletions docs/screenshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ The `--js-file` option is also available for the `pdf`, `html`, `accessibility`

## Using JPEGs instead of PNGs

Screenshots default to PNG. You can save as a JPEG by specifying a `-o` filename that ends with `.jpg`.
Screenshots default to PNG. Use a `-o` filename ending in `.jpg` or `.jpeg` to save as JPEG, or `.webp` to save as WebP.

You can also use `--quality X` to save as a JPEG with the specified quality, in order to reduce the filesize. 80 is a good value to use here:
```bash
Expand All @@ -136,6 +136,47 @@ shot-scraper https://simonwillison.net/ \
% ls -lah simonwillison.jpg
-rw-r--r--@ 1 simon staff 168K Mar 9 13:53 simonwillison.jpg
```
## Using WebP

Save a screenshot as WebP by specifying a filename ending in `.webp`:

```bash
shot-scraper https://simonwillison.net/ -h 800 -o simonwillison.webp
```

WebP screenshots are lossless by default, equivalent to `--quality 100`. Use a lower quality to apply lossy compression:

```bash
shot-scraper https://simonwillison.net/ \
-h 800 -o simonwillison.webp --quality 80
```

WebP also works with selectors:

```bash
shot-scraper https://simonwillison.net/ -s '#bighead' -o bighead.webp
```

WebP images are limited to [16,383 pixels in either dimension](https://developers.google.com/speed/webp/faq#what_is_the_maximum_size_a_webp_image_can_be), so use `--height` or a selector when capturing a page that exceeds that limit.

## Specifying the image format

Use `--format png`, `--format jpeg` or `--format webp` to select the format explicitly. This takes precedence over the output filename extension.

Without `-o`, the automatic filename uses the selected format. This writes to `datasette-io.webp`:

```bash
shot-scraper https://datasette.io/ --format webp
```

Use `--format` with `-o -` to write WebP or JPEG to standard output:

```bash
shot-scraper https://datasette.io/ --format webp -o - > datasette.webp
```

The `--quality` option accepts values from 0 to 100 for JPEG and WebP. Combining `--format png` with `--quality` is an error because PNG does not use a quality setting.

## Device scale factor

The `--scale-factor` option sets a specific device scale factor, which effectively simulates different device pixel ratios. This setting is useful for testing high-definition displays or emulating screens with various pixel densities.
Expand Down Expand Up @@ -164,11 +205,17 @@ Note: The `--retina` option should not be used in conjunction with the `--scale-

## Transparent background

The `--omit-background` option instructs the browser to ignore the default background, allowing for the capture of a page with a transparent background. Does not work with JPG images or when `quality` is set.
The `--omit-background` option instructs the browser to ignore the default background, allowing for the capture of a page with a transparent background. The default Chromium browser supports this for PNG and WebP images. JPEG does not support transparency.
```bash
shot-scraper https://simonwillison.net/ -o simon.png \
--width 400 --height 600 --omit-background
```
WebP can preserve transparency when using `--quality`:

```bash
shot-scraper https://simonwillison.net/ -o simon.webp \
--width 400 --height 600 --omit-background --quality 80
```
## Interacting with the page

Sometimes it's useful to be able to manually interact with a page before the screenshot is captured.
Expand Down Expand Up @@ -363,9 +410,12 @@ Options:
together with '--scale-factor'.
--omit-background Omit the default browser background from the
shot, making it possible take advantage of
transparency. Does not work with JPEGs or when
using --quality.
--quality INTEGER Save as JPEG with this quality, e.g. 80
transparency. Does not work with JPEGs.
--format [png|jpeg|webp] Image format. Overrides the output extension;
use with -o - for stdout.
--quality INTEGER RANGE JPEG or WebP quality, e.g. 80. Defaults to
JPEG unless WebP is selected. WebP defaults to
100 (lossless). [0<=x<=100]
--wait INTEGER Wait this many milliseconds before taking the
screenshot
--wait-for TEXT Wait until this JS expression returns true
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies = [
"click",
"PyYAML",
"pydantic",
"playwright>=1.61.0",
"playwright>=1.62.0",
"click-default-group",
]

Expand Down
57 changes: 50 additions & 7 deletions shot_scraper/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
)

BROWSERS = ("chromium", "firefox", "webkit", "chrome", "chrome-beta")
SCREENSHOT_FORMATS = ("png", "jpeg", "webp")


def console_log(msg):
Expand Down Expand Up @@ -271,9 +272,19 @@ def cli():
@click.option(
"--omit-background",
is_flag=True,
help="Omit the default browser background from the shot, making it possible take advantage of transparency. Does not work with JPEGs or when using --quality.",
help="Omit the default browser background from the shot, making it possible take advantage of transparency. Does not work with JPEGs.",
)
@click.option(
"--format",
"format_",
type=click.Choice(SCREENSHOT_FORMATS, case_sensitive=False),
help="Image format. Overrides the output extension; use with -o - for stdout.",
)
@click.option(
"--quality",
type=click.IntRange(0, 100),
help="JPEG or WebP quality, e.g. 80. Defaults to JPEG unless WebP is selected. WebP defaults to 100 (lossless).",
)
@click.option("--quality", type=int, help="Save as JPEG with this quality, e.g. 80")
@click.option(
"--wait", type=int, help="Wait this many milliseconds before taking the screenshot"
)
Expand Down Expand Up @@ -324,6 +335,7 @@ def shot(
retina,
scale_factor,
omit_background,
format_,
quality,
wait,
wait_for,
Expand Down Expand Up @@ -370,8 +382,9 @@ def shot(
shot-scraper https://simonwillison.net -s '#bighead'
"""
javascript = _resolve_javascript(javascript, js_file)
format_ = _screenshot_format(output, format_, quality)
if output is None:
ext = "jpg" if quality else None
ext = "jpg" if format_ == "jpeg" else format_
output = filename_for_url(url, ext=ext, file_exists=os.path.exists)

scale_factor = normalize_scale_factor(retina, scale_factor)
Expand All @@ -385,6 +398,7 @@ def shot(
"javascript": javascript,
"width": width,
"height": height,
"format": format_,
"quality": quality,
"wait": wait,
"wait_for": wait_for,
Expand Down Expand Up @@ -2180,6 +2194,28 @@ def _get_viewport(width, height):
return {}


def _screenshot_format(output, format_, quality):
"Resolve explicit formats and quality, or let Playwright infer from the path."
if quality is not None and (
not isinstance(quality, int)
or isinstance(quality, bool)
or not 0 <= quality <= 100
):
raise click.ClickException("quality must be an integer between 0 and 100")
if format_ is not None:
if not isinstance(format_, str) or format_.lower() not in SCREENSHOT_FORMATS:
raise click.ClickException("format must be one of: png, jpeg, webp")
format_ = format_.lower()
elif pathlib.Path((output or "").strip()).suffix.lower() == ".webp":
format_ = "webp"
elif quality is not None:
# Preserve the existing behavior of --quality selecting JPEG.
format_ = "jpeg"
if format_ == "png" and quality is not None:
raise click.ClickException("quality is not supported for PNG images")
return format_


def take_shot(
context_or_page,
shot,
Expand All @@ -2201,9 +2237,14 @@ def take_shot(
url = url_or_file_path(url, file_exists=_check_and_absolutize)

output = (shot.get("output") or "").strip()
if not output and not return_bytes:
output = filename_for_url(url, ext="png", file_exists=os.path.exists)
quality = shot.get("quality")
format_ = _screenshot_format(output, shot.get("format"), quality)
if not output and not return_bytes:
output = filename_for_url(
url,
ext="jpg" if format_ == "jpeg" else format_,
file_exists=os.path.exists,
)
omit_background = shot.get("omit_background")
wait = shot.get("wait")
wait_for = shot.get("wait_for")
Expand Down Expand Up @@ -2281,8 +2322,10 @@ def on_response(response):
page.wait_for_function(wait_for)

screenshot_args = {}
if quality:
screenshot_args.update({"quality": quality, "type": "jpeg"})
if format_ is not None:
screenshot_args["type"] = format_
if quality is not None:
screenshot_args["quality"] = quality
if omit_background:
screenshot_args.update({"omit_background": True})
if not return_bytes:
Expand Down
Loading
Loading