scrape-cli 1.3.4__tar.gz → 1.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/PKG-INFO +5 -1
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/README.md +2 -0
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/pyproject.toml +4 -1
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/scrape_cli/__init__.py +1 -1
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/scrape_cli/scrape.py +42 -15
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/scrape_cli.egg-info/PKG-INFO +5 -1
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/scrape_cli.egg-info/requires.txt +3 -0
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/tests/test_scrape.py +121 -1
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/scrape_cli.egg-info/SOURCES.txt +0 -0
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/scrape_cli.egg-info/dependency_links.txt +0 -0
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/scrape_cli.egg-info/entry_points.txt +0 -0
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/scrape_cli.egg-info/top_level.txt +0 -0
- {scrape_cli-1.3.4 → scrape_cli-1.3.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scrape_cli
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.6
|
|
4
4
|
Summary: It's a command-line tool to extract HTML elements using an XPath query or CSS3 selector.
|
|
5
5
|
Author-email: Andrea Borruso <aborruso@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/aborruso/scrape-cli
|
|
@@ -12,6 +12,8 @@ Requires-Dist: cssselect
|
|
|
12
12
|
Requires-Dist: lxml
|
|
13
13
|
Requires-Dist: requests
|
|
14
14
|
Requires-Dist: xmltodict
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest; extra == "dev"
|
|
15
17
|
|
|
16
18
|
[](https://pypi.org/project/scrape-cli/)
|
|
17
19
|
[](https://pypi.org/project/scrape-cli/)
|
|
@@ -283,6 +285,8 @@ Output:
|
|
|
283
285
|
|
|
284
286
|
`-j` automatically wraps the result in `<html>/<body>` before conversion, so you don't need to pass `-b`. For elements, the output is the same you would get from `scrape -be ... | xq .` (the underlying converter is `xmltodict`, the same library used by `xq`). Expressions that return only strings, such as `//a/@href` or `//p/text()`, give the string in `body` when there is one result and an array when there are more.
|
|
285
287
|
|
|
288
|
+
Use `--json-array` when a script needs one shape for any number of results. The output is a flat list with no `html`/`body` envelope, one item per result, in the order of the expressions. Strings stay strings and elements become objects with the tag as key, so `scrape --json-array -e "//a/@href" -e "b"` gives `["x", "y", {"b": "tre"}]`, and no results give `[]`. It implies `-j` and has the same limits.
|
|
289
|
+
|
|
286
290
|
`-j` is mutually exclusive with `-t`, `-x` (`--check-existence`) and `-a` (`--argument`).
|
|
287
291
|
|
|
288
292
|
`-j` exits with an error when a matched element has an attribute name that is not valid XML, such as `@click` or `:bind`.
|
|
@@ -268,6 +268,8 @@ Output:
|
|
|
268
268
|
|
|
269
269
|
`-j` automatically wraps the result in `<html>/<body>` before conversion, so you don't need to pass `-b`. For elements, the output is the same you would get from `scrape -be ... | xq .` (the underlying converter is `xmltodict`, the same library used by `xq`). Expressions that return only strings, such as `//a/@href` or `//p/text()`, give the string in `body` when there is one result and an array when there are more.
|
|
270
270
|
|
|
271
|
+
Use `--json-array` when a script needs one shape for any number of results. The output is a flat list with no `html`/`body` envelope, one item per result, in the order of the expressions. Strings stay strings and elements become objects with the tag as key, so `scrape --json-array -e "//a/@href" -e "b"` gives `["x", "y", {"b": "tre"}]`, and no results give `[]`. It implies `-j` and has the same limits.
|
|
272
|
+
|
|
271
273
|
`-j` is mutually exclusive with `-t`, `-x` (`--check-existence`) and `-a` (`--argument`).
|
|
272
274
|
|
|
273
275
|
`-j` exits with an error when a matched element has an attribute name that is not valid XML, such as `@click` or `:bind`.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "scrape_cli"
|
|
7
|
-
version = "1.3.
|
|
7
|
+
version = "1.3.6"
|
|
8
8
|
description = "It's a command-line tool to extract HTML elements using an XPath query or CSS3 selector."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -22,6 +22,9 @@ dependencies = [
|
|
|
22
22
|
"xmltodict"
|
|
23
23
|
]
|
|
24
24
|
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = ["pytest"]
|
|
27
|
+
|
|
25
28
|
[project.urls]
|
|
26
29
|
Homepage = "https://github.com/aborruso/scrape-cli"
|
|
27
30
|
|
|
@@ -133,6 +133,7 @@ examples:
|
|
|
133
133
|
scrape -t file.html all body text, no script or style
|
|
134
134
|
scrape -te "//article" file.html text of the matching elements
|
|
135
135
|
scrape -je "a" file.html JSON
|
|
136
|
+
scrape --json-array -e "a" file.html JSON, always a flat list
|
|
136
137
|
scrape -be "//article" file.html wrap output in <html><body>
|
|
137
138
|
scrape -x -e "//h1" file.html test only: exit code, no output
|
|
138
139
|
scrape -e "//h1" https://example.com fetch from URL
|
|
@@ -147,6 +148,8 @@ output:
|
|
|
147
148
|
is an object, several are a list
|
|
148
149
|
strings one is a string, several are a list
|
|
149
150
|
nothing null
|
|
151
|
+
--json-array always a flat list, one item per result, in expression order:
|
|
152
|
+
[] for nothing, strings as strings, elements as {"tag": ...}
|
|
150
153
|
|
|
151
154
|
exit codes:
|
|
152
155
|
0 success, also when nothing matches: empty output, or null body with -j
|
|
@@ -155,6 +158,7 @@ exit codes:
|
|
|
155
158
|
|
|
156
159
|
notes:
|
|
157
160
|
- No match is not an error: use -x to test for it.
|
|
161
|
+
- HTML with no elements, such as only spaces or comments, exits 1.
|
|
158
162
|
- With short flags together, -e goes last: -be, -je, -te. As an argument on
|
|
159
163
|
its own, -eb or -ej is rejected, because -e would take "b" or "j" as value.
|
|
160
164
|
- -x evaluates only the first -e.
|
|
@@ -162,8 +166,8 @@ notes:
|
|
|
162
166
|
- -j fails with exit 1 on attribute names that are not valid XML, such as
|
|
163
167
|
@click or :bind. Use -e without -j on those pages.
|
|
164
168
|
- URLs: 30 s timeout, HTTP errors (4xx, 5xx) exit 1.
|
|
165
|
-
- Charset: from the meta tag, otherwise UTF-8
|
|
166
|
-
|
|
169
|
+
- Charset: from the meta tag, otherwise UTF-8. If the bytes are not valid
|
|
170
|
+
UTF-8: Windows-1252, then ISO-8859-1. Other encodings need the meta tag.
|
|
167
171
|
'''
|
|
168
172
|
)
|
|
169
173
|
|
|
@@ -192,6 +196,8 @@ notes:
|
|
|
192
196
|
# Option to output structured JSON (built-in, no external xq needed)
|
|
193
197
|
parser.add_argument('-j', '--json', action='store_true', default=False,
|
|
194
198
|
help="Output structured JSON, see below. Not with -t, -x, -a")
|
|
199
|
+
parser.add_argument('--json-array', action='store_true', default=False,
|
|
200
|
+
help="Output JSON as a flat list, one item per result, see below. Same limits as -j")
|
|
195
201
|
# Allows to specify one or more XPath or CSS3 selector expressions
|
|
196
202
|
parser.add_argument('-e', '--expression', default=[], action='append',
|
|
197
203
|
help="XPath query or CSS3 selector, detected automatically. Repeatable")
|
|
@@ -207,6 +213,8 @@ notes:
|
|
|
207
213
|
args = parser.parse_args()
|
|
208
214
|
|
|
209
215
|
# JSON flag is mutually exclusive with -t, -x, -a
|
|
216
|
+
if args.json_array:
|
|
217
|
+
args.json = True
|
|
210
218
|
if args.json and args.text:
|
|
211
219
|
print("Error: --json and --text are mutually exclusive.", file=sys.stderr)
|
|
212
220
|
sys.exit(1)
|
|
@@ -307,15 +315,25 @@ notes:
|
|
|
307
315
|
try:
|
|
308
316
|
inp.decode('utf-8')
|
|
309
317
|
except UnicodeDecodeError:
|
|
310
|
-
|
|
318
|
+
# cp1252 rejects five undefined bytes, iso-8859-1 accepts them all
|
|
319
|
+
try:
|
|
320
|
+
inp = inp.decode('cp1252').encode('utf-8')
|
|
321
|
+
except UnicodeDecodeError:
|
|
322
|
+
inp = inp.decode('iso-8859-1').encode('utf-8')
|
|
311
323
|
document = etree.fromstring(inp, html_parser)
|
|
312
324
|
except (etree.XMLSyntaxError, UnicodeDecodeError) as e:
|
|
313
|
-
# Print an error in case of syntax issues in the
|
|
314
|
-
print(f"Error parsing HTML: {e}", file=sys.stderr)
|
|
325
|
+
# Print an error in case of syntax issues in the input
|
|
326
|
+
print(f"Error parsing {'XML' if args.rawinput else 'HTML'}: {e}", file=sys.stderr)
|
|
327
|
+
sys.exit(1)
|
|
328
|
+
|
|
329
|
+
if document is None:
|
|
330
|
+
print("Error parsing HTML: no elements found in the input", file=sys.stderr)
|
|
315
331
|
sys.exit(1)
|
|
316
332
|
|
|
317
333
|
results = []
|
|
318
334
|
string_results = []
|
|
335
|
+
# --json-array items: str for strings, bytes for element markup
|
|
336
|
+
array_items = []
|
|
319
337
|
# For each expression, perform the search in the parsed HTML
|
|
320
338
|
for e in expression:
|
|
321
339
|
try:
|
|
@@ -332,6 +350,13 @@ notes:
|
|
|
332
350
|
|
|
333
351
|
# Extract the text or content of the found elements
|
|
334
352
|
for el in els:
|
|
353
|
+
if args.json_array:
|
|
354
|
+
if isinstance(el, str):
|
|
355
|
+
if el.strip():
|
|
356
|
+
array_items.append(el.strip())
|
|
357
|
+
elif isinstance(el.tag, str): # not a comment or processing instruction
|
|
358
|
+
array_items.append(etree.tostring(el, with_tail=False))
|
|
359
|
+
continue
|
|
335
360
|
if isinstance(el, str):
|
|
336
361
|
# If the element is a string, use the text directly
|
|
337
362
|
text = el
|
|
@@ -352,7 +377,7 @@ notes:
|
|
|
352
377
|
text = clean_text(text)
|
|
353
378
|
elif not args.argument:
|
|
354
379
|
# If no attribute is specified, return the element as HTML string
|
|
355
|
-
text = etree.tostring(el, pretty_print=True).decode('utf-8')
|
|
380
|
+
text = etree.tostring(el, pretty_print=True, with_tail=False).decode('utf-8')
|
|
356
381
|
else:
|
|
357
382
|
# Otherwise, extract the specified attribute
|
|
358
383
|
text = el.get(args.argument)
|
|
@@ -368,16 +393,18 @@ notes:
|
|
|
368
393
|
# Output handling
|
|
369
394
|
if args.json:
|
|
370
395
|
values = [s for s in string_results if s]
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
396
|
+
try:
|
|
397
|
+
if args.json_array:
|
|
398
|
+
parsed = [xmltodict.parse(i) if isinstance(i, bytes) else i for i in array_items]
|
|
399
|
+
elif values and len(string_results) == len(results):
|
|
400
|
+
# Only strings (@attr, text()): XML conversion would merge them into one
|
|
401
|
+
parsed = {"html": {"body": values[0] if len(values) == 1 else values}}
|
|
402
|
+
else:
|
|
376
403
|
parsed = xmltodict.parse(wrap_in_body(results))
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
404
|
+
except ExpatError as e:
|
|
405
|
+
print(f"Error: cannot convert result to JSON, it is not valid XML ({e}). "
|
|
406
|
+
"Attribute names like @click or :bind are not supported by -j.", file=sys.stderr)
|
|
407
|
+
sys.exit(1)
|
|
381
408
|
sys.stdout.write(json.dumps(parsed, indent=2, ensure_ascii=False) + "\n")
|
|
382
409
|
elif args.body and not args.text:
|
|
383
410
|
sys.stdout.write(wrap_in_body(results))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scrape_cli
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.6
|
|
4
4
|
Summary: It's a command-line tool to extract HTML elements using an XPath query or CSS3 selector.
|
|
5
5
|
Author-email: Andrea Borruso <aborruso@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/aborruso/scrape-cli
|
|
@@ -12,6 +12,8 @@ Requires-Dist: cssselect
|
|
|
12
12
|
Requires-Dist: lxml
|
|
13
13
|
Requires-Dist: requests
|
|
14
14
|
Requires-Dist: xmltodict
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest; extra == "dev"
|
|
15
17
|
|
|
16
18
|
[](https://pypi.org/project/scrape-cli/)
|
|
17
19
|
[](https://pypi.org/project/scrape-cli/)
|
|
@@ -283,6 +285,8 @@ Output:
|
|
|
283
285
|
|
|
284
286
|
`-j` automatically wraps the result in `<html>/<body>` before conversion, so you don't need to pass `-b`. For elements, the output is the same you would get from `scrape -be ... | xq .` (the underlying converter is `xmltodict`, the same library used by `xq`). Expressions that return only strings, such as `//a/@href` or `//p/text()`, give the string in `body` when there is one result and an array when there are more.
|
|
285
287
|
|
|
288
|
+
Use `--json-array` when a script needs one shape for any number of results. The output is a flat list with no `html`/`body` envelope, one item per result, in the order of the expressions. Strings stay strings and elements become objects with the tag as key, so `scrape --json-array -e "//a/@href" -e "b"` gives `["x", "y", {"b": "tre"}]`, and no results give `[]`. It implies `-j` and has the same limits.
|
|
289
|
+
|
|
286
290
|
`-j` is mutually exclusive with `-t`, `-x` (`--check-existence`) and `-a` (`--argument`).
|
|
287
291
|
|
|
288
292
|
`-j` exits with an error when a matched element has an attribute name that is not valid XML, such as `@click` or `:bind`.
|
|
@@ -117,6 +117,26 @@ def test_encoding_falls_back_to_iso_8859_1_without_meta(tmp_path):
|
|
|
117
117
|
assert result.stdout == "Caffè\n"
|
|
118
118
|
|
|
119
119
|
|
|
120
|
+
def test_encoding_windows_1252_without_meta(tmp_path):
|
|
121
|
+
html_file = tmp_path / "cp1252.html"
|
|
122
|
+
html_file.write_bytes("<h1>Caffè € ’</h1>".encode("cp1252"))
|
|
123
|
+
|
|
124
|
+
result = run_scrape(str(html_file), "-e", "string(//h1)")
|
|
125
|
+
|
|
126
|
+
assert result.returncode == 0
|
|
127
|
+
assert result.stdout == "Caffè € ’\n"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_encoding_bytes_undefined_in_windows_1252_fall_back_to_iso_8859_1(tmp_path):
|
|
131
|
+
html_file = tmp_path / "undefined.html"
|
|
132
|
+
html_file.write_bytes(b"<h1>Caff\xe8 \x81</h1>")
|
|
133
|
+
|
|
134
|
+
result = run_scrape(str(html_file), "-e", "string(//h1)")
|
|
135
|
+
|
|
136
|
+
assert result.returncode == 0
|
|
137
|
+
assert result.stdout.startswith("Caffè")
|
|
138
|
+
|
|
139
|
+
|
|
120
140
|
def test_encoding_utf8_without_meta_is_not_converted(tmp_path):
|
|
121
141
|
html_file = tmp_path / "utf8.html"
|
|
122
142
|
html_file.write_bytes("<h1>Caffè €</h1>".encode("utf-8"))
|
|
@@ -168,6 +188,12 @@ def test_short_check_existence_flag_x():
|
|
|
168
188
|
assert missing.returncode == 1
|
|
169
189
|
|
|
170
190
|
|
|
191
|
+
def test_rawinput_malformed_xml_reports_xml_error():
|
|
192
|
+
result = run_scrape("-r", "-e", "//a", input_data="<root><a></root>")
|
|
193
|
+
assert result.returncode == 1
|
|
194
|
+
assert "Error parsing XML: " in result.stderr
|
|
195
|
+
|
|
196
|
+
|
|
171
197
|
def test_rawinput_parses_xml_without_html_parser():
|
|
172
198
|
xml_data = "<root><item>one</item><item>two</item></root>"
|
|
173
199
|
result = run_scrape("-e", "//item[2]/text()", "-r", input_data=xml_data)
|
|
@@ -252,7 +278,7 @@ def test_help_documents_the_contract_for_agents():
|
|
|
252
278
|
result = run_scrape("--help")
|
|
253
279
|
|
|
254
280
|
assert result.returncode == 0
|
|
255
|
-
for section in ("output:", "exit codes:", "notes:", "examples:"):
|
|
281
|
+
for section in ("output:", "exit codes:", "notes:", "examples:", "--json-array"):
|
|
256
282
|
assert section in result.stdout
|
|
257
283
|
|
|
258
284
|
|
|
@@ -424,3 +450,97 @@ def test_url_input_downloads_and_extracts_text():
|
|
|
424
450
|
|
|
425
451
|
assert result.returncode == 0
|
|
426
452
|
assert result.stdout.strip() == "Welcome to the Test Page"
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
TAIL_HTML = '<p>Prima <a href="/x">Link</a> dopo.</p>'
|
|
456
|
+
ARRAY_HTML = (
|
|
457
|
+
'<html><body><!-- c --><p class="a">uno</p><p class="a">due</p>'
|
|
458
|
+
'<a href="x">l</a><a href="y">m</a><b>tre</b></body></html>'
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def test_text_after_element_stays_out_of_html_output():
|
|
463
|
+
result = run_scrape("-e", "//a", input_data=TAIL_HTML)
|
|
464
|
+
assert result.stdout.strip() == '<a href="/x">Link</a>'
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def test_text_after_element_stays_out_of_body_output():
|
|
468
|
+
result = run_scrape("-be", "//a", input_data=TAIL_HTML)
|
|
469
|
+
assert "dopo" not in result.stdout
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def test_text_after_element_stays_out_of_json_output():
|
|
473
|
+
result = run_scrape("-je", "//a", input_data=TAIL_HTML)
|
|
474
|
+
assert json.loads(result.stdout) == {"html": {"body": {"a": {"@href": "/x", "#text": "Link"}}}}
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def json_array(*expressions, input_data=ARRAY_HTML):
|
|
478
|
+
args = ["--json-array"]
|
|
479
|
+
for expression in expressions:
|
|
480
|
+
args += ["-e", expression]
|
|
481
|
+
result = run_scrape(*args, input_data=input_data)
|
|
482
|
+
assert result.returncode == 0, result.stderr
|
|
483
|
+
return json.loads(result.stdout)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def test_json_array_no_results():
|
|
487
|
+
assert json_array("p.none") == []
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def test_json_array_one_result():
|
|
491
|
+
assert json_array("b") == [{"b": "tre"}]
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def test_json_array_several_results():
|
|
495
|
+
assert json_array("//a/@href") == ["x", "y"]
|
|
496
|
+
assert json_array("p") == [
|
|
497
|
+
{"p": {"@class": "a", "#text": "uno"}},
|
|
498
|
+
{"p": {"@class": "a", "#text": "due"}},
|
|
499
|
+
]
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def test_json_array_keeps_the_order_of_the_expressions():
|
|
503
|
+
assert json_array("//a/@href", "b") == ["x", "y", {"b": "tre"}]
|
|
504
|
+
assert json_array("b", "//a/@href") == [{"b": "tre"}, "x", "y"]
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def test_json_array_element_item():
|
|
508
|
+
assert json_array("a")[0] == {"a": {"@href": "x", "#text": "l"}}
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def test_json_array_leaves_out_text_after_element():
|
|
512
|
+
assert json_array("b", input_data="<p>a <b>x</b> coda</p>") == [{"b": "x"}]
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def test_json_array_xpath_functions():
|
|
516
|
+
assert json_array("count(//p)") == ["2"]
|
|
517
|
+
assert json_array("string(//none)") == []
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def test_json_array_skips_comments():
|
|
521
|
+
assert json_array("//comment()") == []
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def test_json_array_rejects_conflicting_flags():
|
|
525
|
+
for flags in (["-t"], ["-x"], ["-a", "href"]):
|
|
526
|
+
result = run_scrape("--json-array", *flags, "-e", "a", input_data=ARRAY_HTML)
|
|
527
|
+
assert result.returncode == 1
|
|
528
|
+
assert "Error:" in result.stderr
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def test_json_array_reports_clean_error_on_non_xml_attribute_name():
|
|
532
|
+
result = run_scrape("--json-array", "-e", "a", input_data='<a @click="x">l</a>')
|
|
533
|
+
assert result.returncode == 1
|
|
534
|
+
assert result.stdout == ""
|
|
535
|
+
assert "Error:" in result.stderr
|
|
536
|
+
assert "Traceback" not in result.stderr
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def test_html_input_with_no_elements_reports_clean_error():
|
|
540
|
+
for html in (" ", "<!-- c -->", '<?xml version="1.0"?>'):
|
|
541
|
+
for flags in (["-e", "//a"], ["-t"], ["-j", "-e", "//a"], ["--json-array", "-e", "//a"]):
|
|
542
|
+
result = run_scrape(*flags, input_data=html)
|
|
543
|
+
assert result.returncode == 1
|
|
544
|
+
assert result.stdout == ""
|
|
545
|
+
assert "Error parsing HTML: no elements found" in result.stderr
|
|
546
|
+
assert "Traceback" not in result.stderr
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|