diff --git a/README.md b/README.md index 7a859fb..c74671b 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,8 @@ Options: -o, --output FILE -s, --selector TEXT Take shot of first element matching this CSS selector + --js-selector TEXT Take shot of first element matching this JS + (el) expression -p, --padding INTEGER When using selectors, add this much padding in pixels -j, --javascript TEXT Execute this JS prior to taking the shot diff --git a/shot_scraper/cli.py b/shot_scraper/cli.py index 550deb5..c3877f4 100644 --- a/shot_scraper/cli.py +++ b/shot_scraper/cli.py @@ -81,6 +81,12 @@ def cli(): help="Take shot of first element matching this CSS selector", multiple=True, ) +@click.option( + "js_selectors", + "--js-selector", + help="Take shot of first element matching this JS (el) expression", + multiple=True, +) @click.option( "-p", "--padding", @@ -119,6 +125,7 @@ def shot( width, height, selectors, + js_selectors, padding, javascript, retina, @@ -162,6 +169,7 @@ def shot( shot = { "url": url, "selectors": selectors, + "js_selectors": js_selectors, "javascript": javascript, "width": width, "height": height, @@ -570,6 +578,10 @@ def take_shot( if shot.get("selector"): selectors.append(shot["selector"]) + js_selectors = shot.get("js_selectors") or [] + if shot.get("js_selector"): + js_selectors.append(shot["js_selector"]) + if not use_existing_page: page = context_or_page.new_page() else: @@ -600,9 +612,15 @@ def take_shot( if not return_bytes: screenshot_args["path"] = output - if not selectors: + if not selectors and not js_selectors: screenshot_args["full_page"] = full_page + if js_selectors: + # Evaluate JavaScript adding classes we can select on + js_selector_javascript, extra_selectors = _js_selector_javascript(js_selectors) + selectors.extend(extra_selectors) + _evaluate_js(page, js_selector_javascript) + if selectors: # Use JavaScript to create a box around those elements selector_javascript, selector_to_shoot = _selector_javascript( @@ -626,6 +644,28 @@ def take_shot( click.echo(message, err=True) +def _js_selector_javascript(js_selectors): + extra_selectors = [] + js_blocks = [] + for js_selector in js_selectors: + klass = "js-selector-{}".format(secrets.token_hex(16)) + extra_selectors.append(".{}".format(klass)) + js_blocks.append( + textwrap.dedent( + """ + Array.from( + document.getElementsByTagName('*') + ).find(el => {}).classList.add("{}"); + """.format( + js_selector, klass + ) + ) + ) + js_selector_javascript = "() => {" + "\n".join(js_blocks) + "}" + print(js_selector_javascript, extra_selectors) + return js_selector_javascript, extra_selectors + + def _selector_javascript(selectors, padding=0): selector_to_shoot = "shot-scraper-{}".format(secrets.token_hex(8)) selector_javascript = textwrap.dedent(