掘金 人工智能 09月03日
Python网页图片抓取工具
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了一款基于Python的网页图片抓取工具,该工具能够处理动态加载的网页内容,并提供可视化界面供用户选择和下载图片。它集成了Selenium、Tkinter、Requests、BeautifulSoup等库,实现了从网页抓取、懒加载修复、图片预览到批量打包下载的功能。该工具支持多线程下载以提高效率,并能将选中的图片打包成ZIP文件。文章强调了使用该工具时需注意版本兼容性、网站访问权限以及法律合规性,特别是尊重版权和避免非法使用。

🖼️ **强大的动态网页图片抓取能力**:该工具利用Selenium驱动Chrome浏览器,能够有效地处理JavaScript动态加载的内容,并通过执行脚本修复懒加载的图片(如`data-src`或`data-original`属性),确保能够捕获到页面上所有可见的图片资源。

💻 **直观易用的可视化界面**:基于Tkinter构建的GUI界面提供了清晰的操作流程。用户只需输入目标网页URL,点击“抓取图片”即可开始。抓取到的图片会以缩略图形式展示,并带有复选框,方便用户进行选择性下载,同时提供“全选/取消全选”和“保存选中图片”功能。

🚀 **高效下载与打包功能**:该工具采用`ThreadPoolExecutor`实现多线程下载,显著提升了图片抓取的速度。此外,它支持将用户选定的图片批量打包成一个ZIP文件,方便用户一次性保存和管理所有下载的图片,优化了下载体验。

⚖️ **合规使用与风险提示**:文章明确指出,该工具仅供学习和技术交流之用,严禁用于非法用途。用户需要注意尊重版权,避免抓取受版权保护的内容,并了解部分网站可能存在反爬虫机制。同时,也提供了Unsplash、Pexels等合法免费图片资源网站作为学习参考。

📢 免责声明

本文内容仅供 学习与技术交流 之用,禁止任何形式的非法使用。
使用本文代码或相关技术抓取的内容,如涉及版权或法律问题,责任由使用者本人承担
作者不鼓励、也不支持任何违反法律法规及网站使用条款的行为。

✅ 合规指引

    尊重版权

      不要抓取带有版权的图片、视频或数据。商业网站的图片、新闻、影视资源几乎都受保护,未经授权下载、传播可能违法。

    查看 robots.txt 和用户协议

      合法网站通常会通过 robots.txt 或服务条款明确是否允许爬虫访问。如果明确禁止,就不要去爬。

    设置合理的限制

      控制并发线程,避免对目标服务器造成压力。不要绕过反爬虫策略,否则可能涉及“恶意访问”。

    🟢 合法示例网站(学习推荐)

      Unsplash - 免费高质量摄影图片Pexels - 免费照片和视频素材Pixabay - 免费插画、矢量图、视频⚠️ 请注意:即便在这些网站,仍需遵守各自的授权协议。

✨ 功能亮点

环境准备

    Python 版本:推荐使用 Python 3.10+ (本文基于 macOS + Python 3.10 测试)。依赖库
pip install pillow requests selenium beautifulsoup4

Chrome 浏览器与驱动

CHROMEDRIVER_PATH = "/你的路径/chromedriver"CHROME_BINARY_PATH = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

📦 完整代码

支持启动 Chrome、滚动加载、懒加载修复、图片下载、多选保存。

import osimport timeimport requestsfrom io import BytesIOfrom tkinter import Tk, Label, Button, Entry, Canvas, Scrollbar, Frame, filedialog, messagebox, Checkbutton, IntVar, StringVarfrom tkinter import ttkfrom PIL import Image, ImageTk, UnidentifiedImageErrorfrom selenium import webdriverfrom selenium.webdriver.chrome.service import Servicefrom selenium.webdriver.chrome.options import Optionsfrom bs4 import BeautifulSoupfrom urllib.parse import urljoinimport threadingimport zipfilefrom concurrent.futures import ThreadPoolExecutorCHROMEDRIVER_PATH = "xxxxx/chromedriver"CHROME_BINARY_PATH = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"MAX_DOWNLOAD = 1000  # 支持最大下载数量MAX_THREADS = 10  # 最大线程数class ImageScraperApp:    def __init__(self, master):        self.root = master        if isinstance(master, Tk):            self.root.title("网页图片抓取器(动态网页+选择下载+ZIP)")            self.root.geometry("1400x800")        Label(master, text="输入网页 URL:").pack(pady=5)        self.url_entry = Entry(master, width=100)        self.url_entry.pack(pady=5)        Button(master, text="抓取图片", command=self.start_fetch).pack(pady=5)        Button(master, text="全选/取消全选", command=self.toggle_select_all).pack(pady=5)        Button(master, text="保存选中图片", command=self.save_selected).pack(pady=5)        self.status_var = StringVar()        self.status_var.set("状态:等待输入 URL")        Label(master, textvariable=self.status_var).pack(pady=5)        # 进度条        self.progress = ttk.Progressbar(master, orient="horizontal", length=500, mode="determinate")        self.progress.pack(pady=5)        self.frame = Frame(master)        self.frame.pack(fill="both", expand=True)        self.canvas = Canvas(self.frame, bg="white")        self.scrollbar = Scrollbar(self.frame, orient="vertical", command=self.canvas.yview)        self.scrollable_frame = Frame(self.canvas, bg="white")        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")        self.images = []         # (PIL.Image, 原始字节)        self.photo_images = []   # 缩略图缓存        self.image_labels = []   # Checkbutton 控件        self.check_vars = []     # 选中状态        # 窗口宽度变化时,重新排布图片        self.canvas.bind("<Configure>", lambda e: self.rearrange_images())    def start_fetch(self):        threading.Thread(target=self.fetch_images, daemon=True).start()    def fetch_images(self):        url = self.url_entry.get()        if not url:            messagebox.showwarning("提示", "请输入网页 URL")            return        for lbl in self.image_labels:            lbl.destroy()        self.images.clear()        self.photo_images.clear()        self.image_labels.clear()        self.check_vars.clear()        self.status_var.set("状态:启动 ChromeDriver…")        chrome_options = Options()        chrome_options.binary_location = CHROME_BINARY_PATH        chrome_options.add_argument("--headless=new")  # 新版本 headless 模式        service = Service(CHROMEDRIVER_PATH)        try:            driver = webdriver.Chrome(service=service, options=chrome_options)        except Exception as e:            messagebox.showerror("错误", f"ChromeDriver 或 Chrome 浏览器无法启动:\n{e}")            self.status_var.set("状态:启动失败")            return        try:            self.status_var.set("状态:加载网页…")            driver.get(url)            driver.implicitly_wait(5)            # 滚动加载,触发懒加载            last_height = driver.execute_script("return document.body.scrollHeight")            while True:                driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")                time.sleep(2)                new_height = driver.execute_script("return document.body.scrollHeight")                if new_height == last_height:                    break                last_height = new_height            # 修复 lazyload,把 data-src / data-original 填到 src            driver.execute_script("""                document.querySelectorAll('img').forEach(img => {                    if (img.getAttribute('data-src')) {                        img.setAttribute('src', img.getAttribute('data-src'));                    }                    if (img.getAttribute('data-original')) {                        img.setAttribute('src', img.getAttribute('data-original'));                    }                });            """)            html = driver.page_source            self.status_var.set("状态:网页加载完成")        except Exception as e:            messagebox.showerror("错误", f"网页加载失败:\n{e}")            driver.quit()            self.status_var.set("状态:网页加载失败")            return        finally:            driver.quit()        self.status_var.set("状态:解析图片…")        soup = BeautifulSoup(html, "html.parser")        img_tags = soup.find_all("img")        img_urls = [urljoin(url, img.get("src")) for img in img_tags if img.get("src")]        img_urls = [u for u in img_urls if not u.lower().endswith(".gif")]        if not img_urls:            messagebox.showinfo("提示", "没有抓取到图片")            self.status_var.set("状态:未抓取到图片")            return        self.status_var.set(f"状态:开始下载 {len(img_urls)} 张图片…")        # 初始化进度条        total = min(len(img_urls), MAX_DOWNLOAD)        self.progress["maximum"] = total        self.progress["value"] = 0        with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:            futures = [executor.submit(self.download_image, img_url, i) for i, img_url in enumerate(img_urls[:MAX_DOWNLOAD])]            for future in futures:                future.result()        self.rearrange_images()        self.status_var.set(f"状态:抓取完成,共抓取 {len(self.images)} 张图片")        messagebox.showinfo("提示", f"抓取完成,共抓取 {len(self.images)} 张图片")    def download_image(self, img_url, idx):        try:            r = requests.get(img_url, timeout=10)            r.raise_for_status()            img = Image.open(BytesIO(r.content))            if img.format and img.format.lower() == "gif":                return            self.images.append((img, r.content))            var = IntVar(value=1)            chk = Checkbutton(self.scrollable_frame, variable=var, bg="white", relief="flat")            self.check_vars.append(var)            self.image_labels.append(chk)            # 更新进度条            self.progress["value"] = idx + 1            self.root.update_idletasks()        except UnidentifiedImageError:            print(f"无法识别图片: {img_url}")        except Exception as e:            print(f"加载图片失败 {img_url}: {e}")    def rearrange_images(self):        if not self.image_labels or not self.images:            return        col_count = 6  # 固定 6 列        thumb_width = 200  # 固定宽度 200        self.photo_images.clear()        for idx, (chk, (img, _)) in enumerate(zip(self.image_labels, self.images)):            ratio = thumb_width / img.width            h = int(img.height * ratio)            img_preview = img.copy().resize((thumb_width, h))            photo = ImageTk.PhotoImage(img_preview)            chk.config(image=photo)            chk.image = photo            self.photo_images.append(photo)            chk.grid(row=idx // col_count, column=idx % col_count, padx=5, pady=5)    def toggle_select_all(self):        if not self.check_vars:            return        if all(var.get() == 1 for var in self.check_vars):            for var in self.check_vars:                var.set(0)        else:            for var in self.check_vars:                var.set(1)    def save_selected(self):        selected = [i for i, var in enumerate(self.check_vars) if var.get() == 1]        if not selected:            messagebox.showwarning("提示", "没有选择图片")            return        if len(selected) > MAX_DOWNLOAD:            messagebox.showwarning("提示", f"最多支持下载 {MAX_DOWNLOAD} 张图片")            return        folder = filedialog.askdirectory(title="选择保存 ZIP 文件目录")        if not folder:            return        zip_path = os.path.join(folder, "images.zip")        with zipfile.ZipFile(zip_path, "w") as zf:            for idx in selected:                img, content = self.images[idx]                ext = img.format.lower() if img.format else "jpg"                zf.writestr(f"image_{idx+1}.{ext}", content)        messagebox.showinfo("提示", f"选中图片已打包保存到 {zip_path}")def run(master):    app = ImageScraperApp(master)    return appif __name__ == "__main__":    root = Tk()    app = ImageScraperApp(root)    root.mainloop()

🎨 使用效果

运行后会弹出一个 GUI 窗口:

    输入网页 URL → 点击“抓取图片”。界面展示缩略图,可逐一选择或全选。点击“保存选中图片” → 自动打包为 ZIP 文件。

注意事项与风险提示

📌 总结

这份脚本算是一个 学习型小项目,融合了:

你可以在此基础上继续扩展:

📢 再次声明

本文所有内容仅限 学习与交流,请严格遵守法律法规。
抓取的资源请确保 版权合法,不要用于商业或非法用途。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Python 网页抓取 Selenium Tkinter 图片下载 Web Scraping Image Downloading GUI Application
相关文章