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 +7 -0
- scrapedesk/__main__.py +15 -0
- scrapedesk/assets/icon.ico +0 -0
- scrapedesk/assets/icon.png +0 -0
- scrapedesk/assets/icon64.png +0 -0
- scrapedesk/cli.py +100 -0
- scrapedesk/core.py +1073 -0
- scrapedesk/exporters.py +171 -0
- scrapedesk/gui/__init__.py +1 -0
- scrapedesk/gui/app.py +1849 -0
- scrapedesk/gui/theme.py +239 -0
- scrapedesk/gui/widgets.py +604 -0
- scrapedesk-1.0.0.dist-info/METADATA +163 -0
- scrapedesk-1.0.0.dist-info/RECORD +18 -0
- scrapedesk-1.0.0.dist-info/WHEEL +5 -0
- scrapedesk-1.0.0.dist-info/entry_points.txt +5 -0
- scrapedesk-1.0.0.dist-info/licenses/LICENSE +21 -0
- scrapedesk-1.0.0.dist-info/top_level.txt +1 -0
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())
|