scrapedesk 1.0.0__py3-none-any.whl

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.
scrapedesk/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """ScrapeDesk - a friendly desktop web scraper for images, tables, links and text."""
2
+
3
+ __version__ = "1.0.0"
4
+
5
+ from .core import ImageItem, PageResult, ScrapeSettings, TableItem, WebScraper # noqa: E402
6
+
7
+ __all__ = ["ScrapeSettings", "WebScraper", "PageResult", "ImageItem", "TableItem", "__version__"]
scrapedesk/__main__.py ADDED
@@ -0,0 +1,15 @@
1
+ """`python -m scrapedesk` opens the GUI; `python -m scrapedesk --cli ...` runs the command line."""
2
+
3
+ import sys
4
+
5
+
6
+ def main():
7
+ if len(sys.argv) > 1 and sys.argv[1] == "--cli":
8
+ from .cli import main as cli_main
9
+ return cli_main(sys.argv[2:])
10
+ from .gui.app import main as gui_main
11
+ return gui_main()
12
+
13
+
14
+ if __name__ == "__main__":
15
+ sys.exit(main())
Binary file
Binary file
Binary file
scrapedesk/cli.py ADDED
@@ -0,0 +1,100 @@
1
+ """
2
+ Command-line interface for ScrapeDesk (no GUI needed).
3
+
4
+ Examples
5
+ scrapedesk-cli https://example.com --images --download
6
+ scrapedesk-cli https://en.wikipedia.org/wiki/List_of_largest_cities --tables -o cities.xlsx
7
+ scrapedesk-cli https://books.toscrape.com --crawl --max-pages 20 --report report.xlsx
8
+ """
9
+
10
+ import argparse
11
+ import sys
12
+
13
+ from .core import ScrapeSettings, WebScraper, USER_AGENTS
14
+ from . import exporters, __version__
15
+
16
+
17
+ def main(argv=None):
18
+ ap = argparse.ArgumentParser(prog="scrapedesk-cli", description="ScrapeDesk - scrape images, tables, "
19
+ "links and text from websites (command line)")
20
+ ap.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}")
21
+ ap.add_argument("urls", nargs="+", help="URL(s) or local .html file(s)")
22
+ ap.add_argument("--images", action="store_true", help="extract images")
23
+ ap.add_argument("--tables", action="store_true", help="extract tables")
24
+ ap.add_argument("--links", action="store_true", help="extract links")
25
+ ap.add_argument("--text", action="store_true", help="extract text/meta/contacts")
26
+ ap.add_argument("--download", action="store_true", help="download the images found")
27
+ ap.add_argument("--dir", help="image download folder")
28
+ ap.add_argument("--min-width", type=int, default=0)
29
+ ap.add_argument("--min-height", type=int, default=0)
30
+ ap.add_argument("--types", help="comma separated image types, e.g. jpg,png,webp")
31
+ ap.add_argument("-o", "--output", help="export tables to this file (.xlsx .csv .json .html .db .md)")
32
+ ap.add_argument("--report", help="export EVERYTHING to .xlsx / .json / .db")
33
+ ap.add_argument("--crawl", action="store_true")
34
+ ap.add_argument("--depth", type=int, default=1)
35
+ ap.add_argument("--max-pages", type=int, default=20)
36
+ ap.add_argument("--paginate", action="store_true", help="follow 'next page' links")
37
+ ap.add_argument("--js", choices=["none", "playwright", "selenium"], default="none")
38
+ ap.add_argument("--delay", type=float, default=0.5)
39
+ ap.add_argument("--proxy", default="")
40
+ ap.add_argument("--ua", choices=list(USER_AGENTS), default="Chrome (Windows)")
41
+ ap.add_argument("--css", action="append", default=[], metavar="NAME=SELECTOR[@attr]",
42
+ help="custom CSS field, e.g. price=.price_color or link='h3 a@href'")
43
+ ap.add_argument("--row", default="", help="record container CSS selector for --css fields")
44
+ ap.add_argument("--profile", help="load settings from a profile .json saved by the GUI")
45
+ a = ap.parse_args(argv)
46
+
47
+ if a.profile:
48
+ with open(a.profile, encoding="utf-8") as fh:
49
+ s = ScrapeSettings.from_json(fh.read())
50
+ else:
51
+ s = ScrapeSettings()
52
+ any_flag = a.images or a.tables or a.links or a.text
53
+ if any_flag or not a.profile:
54
+ everything = not any_flag
55
+ s.extract_images = a.images or everything
56
+ s.extract_tables = a.tables or everything
57
+ s.extract_links = a.links or everything
58
+ s.extract_text = s.extract_meta = s.extract_contacts = a.text or everything
59
+ s.crawl, s.max_depth, s.max_pages, s.follow_pagination = a.crawl, a.depth, a.max_pages, a.paginate
60
+ s.render_js, s.delay, s.proxy, s.user_agent = a.js, a.delay, a.proxy, USER_AGENTS[a.ua]
61
+ s.min_width, s.min_height = a.min_width, a.min_height
62
+ if a.dir:
63
+ s.download_dir = a.dir
64
+ if a.types:
65
+ s.image_types = [t.strip().lower() for t in a.types.split(",")]
66
+ for spec in a.css:
67
+ name, _, q = spec.partition("=")
68
+ q, _, attr = q.partition("@")
69
+ s.custom_selectors.append({"name": name, "type": "css", "query": q, "attr": attr or "text"})
70
+ s.custom_row_selector = a.row
71
+
72
+ sc = WebScraper(s, log=print)
73
+ results = sc.run(a.urls)
74
+ images = [i for r in results for i in r.images]
75
+ tables = [t for r in results for t in r.tables]
76
+ print(f"\nPages: {len(results)} Images: {len(images)} Tables: {len(tables)} "
77
+ f"Links: {sum(len(r.links) for r in results)} Custom rows: {sum(len(r.custom) for r in results)}")
78
+ for t in tables[:20]:
79
+ print(f" - {t.name}: {len(t.rows)} rows x {max(len(x) for x in t.rows)} cols")
80
+ if a.download and images:
81
+ sc.download_images(images)
82
+ if a.output:
83
+ custom = [row for r in results for row in r.custom]
84
+ frames = exporters.tables_to_frames(tables)
85
+ if custom:
86
+ frames = {"Custom": exporters.records_to_df(custom), **frames}
87
+ if frames:
88
+ for f in exporters.export_dataframes(frames, a.output):
89
+ print("saved", f)
90
+ else:
91
+ print("No tables / custom data to export.")
92
+ if a.report:
93
+ for f in exporters.export_full_report(results, a.report):
94
+ print("saved", f)
95
+ sc.close()
96
+ return 0
97
+
98
+
99
+ if __name__ == "__main__":
100
+ sys.exit(main())