-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathimage_gui.py
More file actions
198 lines (153 loc) · 7.53 KB
/
Copy pathimage_gui.py
File metadata and controls
198 lines (153 loc) · 7.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import tkinter as tk
from tkinter import ttk, messagebox
import requests
from bs4 import BeautifulSoup
from PIL import Image, ImageTk
import io
import threading
from urllib.parse import urljoin
class ImageScraperApp:
def __init__(self, root):
self.root = root
self.root.title("Webpage Image Extractor (Lazy Load Support)")
self.root.geometry("900x700")
self.current_full_image = None
self.images_data = []
self.thumbnail_refs = []
# --- GUI Layout ---
# 1. Top Section: Image Viewer
self.viewer_frame = tk.Frame(root, bg="#222", height=400)
self.viewer_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.viewer_frame.pack_propagate(False)
self.image_label = tk.Label(self.viewer_frame, text="No Image Selected", bg="#222", fg="#aaa")
self.image_label.pack(expand=True)
# 2. Middle Section: Controls
self.control_frame = tk.Frame(root, padx=10, pady=15)
self.control_frame.pack(side=tk.TOP, fill=tk.X)
tk.Label(self.control_frame, text="URL:").grid(row=0, column=0, sticky="w")
self.url_entry = tk.Entry(self.control_frame, width=60)
self.url_entry.grid(row=0, column=1, padx=5)
self.btn_fetch = tk.Button(self.control_frame, text="Fetch Images", command=self.start_fetch_thread, bg="#ddd")
self.btn_fetch.grid(row=0, column=2, padx=5)
tk.Label(self.control_frame, text="Save Name:").grid(row=1, column=0, sticky="w", pady=5)
self.filename_entry = tk.Entry(self.control_frame, width=30)
self.filename_entry.grid(row=1, column=1, sticky="w", padx=5, pady=5)
self.filename_entry.insert(0, "downloaded_image")
self.btn_download = tk.Button(self.control_frame, text="Download as PNG", command=self.save_image, bg="#ddd")
self.btn_download.grid(row=1, column=2, padx=5, pady=5)
# 3. Bottom Section: Scrollable Thumbnail Grid
self.bottom_container = tk.Frame(root, height=200, bg="#f0f0f0")
self.bottom_container.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=False)
self.canvas = tk.Canvas(self.bottom_container, bg="#f0f0f0")
self.scrollbar = ttk.Scrollbar(self.bottom_container, orient="vertical", command=self.canvas.yview)
self.scrollable_frame = tk.Frame(self.canvas, bg="#f0f0f0")
self.scrollable_frame.bind(
"<Configure>",
lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
)
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.pack(side="left", fill="both", expand=True)
self.scrollbar.pack(side="right", fill="y")
def start_fetch_thread(self):
url = self.url_entry.get()
if not url:
return
self.btn_fetch.config(state="disabled", text="Loading...")
for widget in self.scrollable_frame.winfo_children():
widget.destroy()
self.image_label.config(image='', text="Loading...")
threading.Thread(target=self.fetch_images, args=(url,), daemon=True).start()
def fetch_images(self, url):
# Emulate a real browser to avoid being blocked
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",
"Referer": url
}
found_images = []
try:
response = requests.get(url, headers=headers, timeout=10)
soup = BeautifulSoup(response.text, 'html.parser')
img_tags = soup.find_all('img')
print(f"Found {len(img_tags)} image tags. Processing...")
for img in img_tags:
# --- KEY FIX: Check data-src first ---
# Many sites use 'data-src', 'data-original', or 'data-url' for lazy loading
img_url = img.get('data-src') or img.get('data-original') or img.get('src')
if not img_url:
continue
# Make the URL absolute
full_url = urljoin(url, img_url)
# Filter out tiny icons or tracking pixels often found in headers/footers
# (Optional check: skip if it doesn't look like an image url)
try:
# Download the image content
img_resp = requests.get(full_url, headers=headers, timeout=5)
img_data = io.BytesIO(img_resp.content)
# Attempt to open with PIL
pil_img = Image.open(img_data)
# Filter out tiny images (tracking pixels, icons)
if pil_img.width < 50 or pil_img.height < 50:
continue
found_images.append(pil_img)
except Exception as e:
# Silently skip bad images
continue
except Exception as e:
print(f"Error fetching page: {e}")
# Update GUI on main thread
self.root.after(0, self.populate_grid, found_images)
def populate_grid(self, images):
self.images_data = images
self.thumbnail_refs = []
row, col = 0, 0
max_cols = 6
if not images:
self.image_label.config(text="No valid images found.")
else:
self.image_label.config(text="Select an image below")
for i, pil_img in enumerate(images):
try:
# Create Thumbnail
thumb = pil_img.copy()
thumb.thumbnail((110, 110))
tk_thumb = ImageTk.PhotoImage(thumb)
self.thumbnail_refs.append(tk_thumb)
btn = tk.Button(self.scrollable_frame, image=tk_thumb, bg="white", bd=1,
command=lambda img=pil_img: self.display_large_image(img))
btn.grid(row=row, column=col, padx=5, pady=5)
col += 1
if col >= max_cols:
col = 0
row += 1
except Exception as e:
print(f"Error processing thumbnail: {e}")
self.btn_fetch.config(state="normal", text="Fetch Images")
def display_large_image(self, pil_img):
self.current_full_image = pil_img
# Resize for display only (keep aspect ratio)
display_img = pil_img.copy()
display_img.thumbnail((800, 390))
tk_img = ImageTk.PhotoImage(display_img)
self.image_label.config(image=tk_img, text="")
self.image_label.image = tk_img
def save_image(self):
if not self.current_full_image:
messagebox.showwarning("Warning", "Please select an image first.")
return
name = self.filename_entry.get().strip()
if not name:
name = "image"
if not name.lower().endswith(".png"):
name += ".png"
try:
# Convert to RGB to ensure PNG compatibility (e.g. if original is CMYK or has weird transparency)
save_img = self.current_full_image.convert("RGB")
save_img.save(name, "PNG")
messagebox.showinfo("Success", f"Saved as {name}")
except Exception as e:
messagebox.showerror("Error", f"Failed to save: {e}")
if __name__ == "__main__":
root = tk.Tk()
app = ImageScraperApp(root)
root.mainloop()