scrape-cli 1.3.1__tar.gz → 1.3.2__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.1 → scrape_cli-1.3.2}/PKG-INFO +1 -1
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/pyproject.toml +1 -1
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/scrape_cli/__init__.py +1 -1
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/scrape_cli/scrape.py +25 -14
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/scrape_cli.egg-info/PKG-INFO +1 -1
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/tests/test_scrape.py +37 -0
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/README.md +0 -0
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/scrape_cli.egg-info/SOURCES.txt +0 -0
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/scrape_cli.egg-info/dependency_links.txt +0 -0
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/scrape_cli.egg-info/entry_points.txt +0 -0
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/scrape_cli.egg-info/requires.txt +0 -0
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/scrape_cli.egg-info/top_level.txt +0 -0
- {scrape_cli-1.3.1 → scrape_cli-1.3.2}/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.2
|
|
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
|
|
@@ -56,6 +56,21 @@ def convert_css_to_xpath(expression):
|
|
|
56
56
|
print(f"Error converting CSS selector to XPath: {e}", file=sys.stderr)
|
|
57
57
|
sys.exit(1)
|
|
58
58
|
|
|
59
|
+
def xpath_result_items(result):
|
|
60
|
+
"""XPath functions return a single string, number or boolean instead of a node list."""
|
|
61
|
+
if isinstance(result, list):
|
|
62
|
+
return result
|
|
63
|
+
if isinstance(result, bool):
|
|
64
|
+
return ['true' if result else 'false']
|
|
65
|
+
if isinstance(result, float):
|
|
66
|
+
return [str(int(result)) if result.is_integer() else str(result)]
|
|
67
|
+
return [result] if result else []
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def wrap_in_body(results):
|
|
71
|
+
return "<!DOCTYPE html>\n<html>\n<body>\n" + "".join(r + "\n" for r in results) + "</body>\n</html>\n"
|
|
72
|
+
|
|
73
|
+
|
|
59
74
|
def is_xpath(expression):
|
|
60
75
|
"""
|
|
61
76
|
Check if the expression is XPath by looking for common XPath patterns:
|
|
@@ -120,12 +135,10 @@ examples:
|
|
|
120
135
|
parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
|
|
121
136
|
|
|
122
137
|
# Check for incorrect argument order (-eb instead of -be, -ej instead of -je)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
print("Error: use -je not -ej.\n scrape -je \"//article\" file.html", file=sys.stderr)
|
|
128
|
-
sys.exit(1)
|
|
138
|
+
for wrong, right in (('-eb', '-be'), ('-ej', '-je')):
|
|
139
|
+
if wrong in sys.argv[1:]:
|
|
140
|
+
print(f"Error: use {right} not {wrong}.\n scrape {right} \"//article\" file.html", file=sys.stderr)
|
|
141
|
+
sys.exit(1)
|
|
129
142
|
# Defines the HTML input argument (can be a file, URL or stdin)
|
|
130
143
|
parser.add_argument('html', nargs='?', type=str, default='',
|
|
131
144
|
help="HTML input (file, URL or stdin, default: stdin)", metavar="HTML")
|
|
@@ -266,11 +279,13 @@ examples:
|
|
|
266
279
|
string_results = []
|
|
267
280
|
# For each expression, perform the search in the parsed HTML
|
|
268
281
|
for e in expression:
|
|
269
|
-
|
|
282
|
+
result = document.xpath(e)
|
|
270
283
|
|
|
271
284
|
# If check-existence is enabled, return 0 or 1 depending on the existence of elements
|
|
272
285
|
if args.check_existence:
|
|
273
|
-
sys.exit(
|
|
286
|
+
sys.exit(0 if result else 1)
|
|
287
|
+
|
|
288
|
+
els = xpath_result_items(result)
|
|
274
289
|
|
|
275
290
|
# Extract the text or content of the found elements
|
|
276
291
|
for el in els:
|
|
@@ -314,19 +329,15 @@ examples:
|
|
|
314
329
|
# Only strings (@attr, text()): XML conversion would merge them into one
|
|
315
330
|
parsed = {"html": {"body": values[0] if len(values) == 1 else values}}
|
|
316
331
|
else:
|
|
317
|
-
wrapped = "<!DOCTYPE html>\n<html>\n<body>\n" + "".join(r + "\n" for r in results) + "</body>\n</html>\n"
|
|
318
332
|
try:
|
|
319
|
-
parsed = xmltodict.parse(
|
|
333
|
+
parsed = xmltodict.parse(wrap_in_body(results))
|
|
320
334
|
except ExpatError as e:
|
|
321
335
|
print(f"Error: cannot convert result to JSON, it is not valid XML ({e}). "
|
|
322
336
|
"Attribute names like @click or :bind are not supported by -j.", file=sys.stderr)
|
|
323
337
|
sys.exit(1)
|
|
324
338
|
sys.stdout.write(json.dumps(parsed, indent=2, ensure_ascii=False) + "\n")
|
|
325
339
|
elif args.body and not args.text:
|
|
326
|
-
sys.stdout.write(
|
|
327
|
-
for result in results:
|
|
328
|
-
sys.stdout.write(result + "\n")
|
|
329
|
-
sys.stdout.write("</body>\n</html>\n")
|
|
340
|
+
sys.stdout.write(wrap_in_body(results))
|
|
330
341
|
else:
|
|
331
342
|
# Normal output (or text-only output when -t is used)
|
|
332
343
|
for result in results:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scrape_cli
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
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
|
|
@@ -288,6 +288,43 @@ def test_json_flag_mixed_elements_and_strings_still_converts():
|
|
|
288
288
|
assert body["#text"] == "x&y"
|
|
289
289
|
|
|
290
290
|
|
|
291
|
+
SCALAR_HTML = "<h1>Ciao</h1><a>1</a><a>2</a>"
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def test_xpath_functions_return_a_single_value():
|
|
295
|
+
cases = {
|
|
296
|
+
"string(//h1)": "Ciao\n",
|
|
297
|
+
"count(//a)": "2\n",
|
|
298
|
+
"boolean(//a)": "true\n",
|
|
299
|
+
"boolean(//nulla)": "false\n",
|
|
300
|
+
"string(//nulla)": "",
|
|
301
|
+
}
|
|
302
|
+
for expression, expected in cases.items():
|
|
303
|
+
result = run_scrape("-e", expression, input_data=SCALAR_HTML)
|
|
304
|
+
assert result.returncode == 0, result.stderr
|
|
305
|
+
assert result.stdout == expected, expression
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def test_check_existence_follows_xpath_function_value():
|
|
309
|
+
for expression, expected in {
|
|
310
|
+
"string(//h1)": 0,
|
|
311
|
+
"string(//nulla)": 1,
|
|
312
|
+
"count(//a)": 0,
|
|
313
|
+
"count(//nulla)": 1,
|
|
314
|
+
"boolean(//a)": 0,
|
|
315
|
+
"boolean(//nulla)": 1,
|
|
316
|
+
}.items():
|
|
317
|
+
result = run_scrape("-x", "-e", expression, input_data=SCALAR_HTML)
|
|
318
|
+
assert result.returncode == expected, expression
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def test_json_flag_with_xpath_functions():
|
|
322
|
+
for expression, expected in {"string(//h1)": "Ciao", "count(//a)": "2"}.items():
|
|
323
|
+
result = run_scrape("-je", expression, input_data=SCALAR_HTML)
|
|
324
|
+
assert result.returncode == 0, result.stderr
|
|
325
|
+
assert json.loads(result.stdout)["html"]["body"] == expected
|
|
326
|
+
|
|
327
|
+
|
|
291
328
|
def test_json_flag_text_results_work_on_test_file():
|
|
292
329
|
result = run_scrape(str(TEST_HTML), "-je", "//p/text()")
|
|
293
330
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|