尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python+Selenium自动化批量导出微信收藏表情包实战指南

Python+Selenium自动化批量导出微信收藏表情包实战指南 1. 项目概述与核心痛点你是不是也和我一样微信收藏的表情包已经多到翻好几页都翻不完那些精心收集的“斗图神器”、朋友发的搞笑动图、还有舍不得删的经典表情全都静静地躺在微信的收藏夹里。每次想换个设备用或者想整理备份一下就只能对着手机屏幕一张张长按保存效率低到让人抓狂。更别提万一手机出问题这些承载着无数聊天回忆的表情包可能就再也找不回来了。这个痛点困扰了我很久直到我决定不再忍受这种低效的手动操作。作为一名经常和代码打交道的开发者我的第一反应就是能不能用脚本自动化这件事答案是肯定的。通过Python我们可以写一个脚本自动登录网页版微信遍历你的表情包收藏并把它们批量下载到电脑本地。这不仅能解放双手还能实现定期备份彻底告别手动保存的繁琐。听起来很酷对吧但这个过程远没有想象中那么简单里面布满了各种“坑”从模拟登录的验证到动态加载数据的抓取再到文件命名的乱码处理每一步都可能让你卡住。接下来我就把自己从零搭建、踩坑无数最终成功的完整方案以及那些官方文档里绝不会写的避坑细节毫无保留地分享给你。无论你是Python新手想找个有趣的项目练手还是老手想快速解决实际问题这篇指南都能让你少走弯路。2. 整体思路与技术选型解析2.1 为什么选择网页版微信作为入口要实现批量导出首先得找到一个能程序化访问表情包列表的入口。我们有几个潜在选择手机APP、PC客户端、网页版。直接操作手机APP需要涉及Android/iOS逆向复杂度极高PC客户端虽然可能有本地数据库但结构不透明且随版本变化。而网页版微信https://wx.qq.com/提供了一个相对标准的HTTP/WebSocket接口其通信协议经过多年发展已有成熟的逆向工程资料如itchat、wxpy等库的基础这使其成为自动化脚本最可行的切入点。我们的核心思路就是模拟一个浏览器登录网页版微信然后找到获取表情包收藏列表的接口最后批量下载。2.2 核心工具链Selenium vs. 直接请求要模拟浏览器主要有两种技术路径。第一种是使用像requests、httpx这样的库直接发送HTTP请求。这需要手动处理登录的二维码生成、扫码状态轮询、Cookie管理以及后续接口的鉴权参数如pass_ticket,wxsid等复杂度很高且微信的接口稍有变动脚本就可能失效。第二种是使用浏览器自动化工具如Selenium。它直接控制一个真实的浏览器如Chrome所有登录、跳转、Cookie维护都由浏览器本身处理我们只需要模拟用户点击和获取页面数据即可。对于微信这种登录流程复杂、前端渲染动态的网站Selenium方案虽然执行速度稍慢但稳定性和开发效率远高于直接请求。因此本项目选择Selenium ChromeDriver作为核心自动化工具。2.3 辅助库的选择确定了主框架我们还需要一些帮手Pillow (PIL) 用于处理图片例如验证下载的图片文件是否完整或者进行简单的格式转换。os, time, json Python标准库用于文件操作、延时等待和数据解析。re (正则表达式) 从复杂的网页元素或脚本数据中提取表情包的URL和名称。注意 网上有些教程会推荐使用itchat等第三方封装库。但经过我的实测许多这类库对较新版本网页微信的支持并不好且功能受限。从底层原理出发使用Selenium虽然步骤多一些但可控性最强学习价值也最大。3. 环境准备与详细配置步骤3.1 Python环境与库安装首先确保你的电脑安装了Python3.6及以上版本。打开命令行CMD或终端使用pip安装必要的库pip install selenium Pillowselenium是浏览器自动化的核心Pillow是图像处理库。安装过程应该很顺利。3.2 浏览器驱动下载与配置Selenium需要对应的浏览器驱动才能工作。我们以最常用的Chrome为例。查看Chrome版本 打开Chrome浏览器点击右上角三个点 - 帮助 - 关于Google Chrome记下版本号例如114.0.5735.199。下载ChromeDriver 访问ChromeDriver官方下载站或国内镜像站。下载与你的Chrome浏览器主版本号完全相同的驱动例如Chrome是114.x就下载114.x.x.x版本的ChromeDriver。配置驱动路径 将下载的chromedriver.exeWindows或chromedriverMac/Linux文件放在一个你记得住的目录例如C:\WebDriver\。然后你需要将这个目录添加到系统的环境变量PATH中。这样Python代码中就可以直接调用webdriver.Chrome()而无需指定完整路径。实操心得 驱动版本不匹配是Selenium新手最常遇到的错误错误信息通常是This version of ChromeDriver only supports Chrome version XXX。务必确保版本号完全一致。另一个更简单的方法是将chromedriver.exe直接放在你的Python脚本所在的同一个文件夹下这样在初始化时指定路径即可driver webdriver.Chrome(executable_path./chromedriver.exe)。3.3 项目目录结构规划在开始写代码前规划好目录结构能让后续的文件管理清晰很多。我建议创建一个如下结构的文件夹wechat_emoji_export/ ├── export_emojis.py # 主脚本文件 ├── chromedriver.exe # Chrome驱动如果没加PATH就放这里 ├── config.json # 可选配置文件如保存登录状态 └── emojis/ # 脚本运行时自动创建用于存放下载的表情包 ├── 20240515_export/ # 按日期区分的子文件夹 ├── 20240516_export/ └── ...4. 核心脚本编写与逐行解析接下来我们进入最核心的脚本编写环节。我会将完整的脚本拆解成几个功能模块并详细解释每一段代码的作用和背后的逻辑。4.1 初始化浏览器并登录微信from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import os def login_to_wechat(): 初始化浏览器并登录网页版微信 # 1. 初始化Chrome浏览器并设置一些选项以优化体验 options webdriver.ChromeOptions() # 避免浏览器出现“正受到自动测试软件控制”的提示栏降低被检测风险 options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) # 保持浏览器窗口打开方便调试 options.add_experimental_option(detach, True) # 初始化驱动如果chromedriver不在PATH需指定executable_path参数 driver webdriver.Chrome(optionsoptions) driver.get(https://wx.qq.com/) # 打开网页版微信 print(请使用手机微信扫描屏幕上的二维码登录...) # 2. 等待用户扫码登录 # 核心思路登录成功后页面会跳转左侧会出现联系人列表。 # 我们通过等待一个登录后才会出现的元素如“聊天”标签来判断登录成功。 try: # WebDriverWait 是Selenium中处理“等待”的最佳实践避免使用固定的time.sleep # 这里等待最多60秒直到页面中出现包含‘聊天’文本的span元素 wait WebDriverWait(driver, 60) chat_element wait.until( EC.presence_of_element_located((By.XPATH, //span[contains(text(), 聊天)])) ) print(登录成功) return driver except Exception as e: print(f登录超时或失败: {e}) driver.quit() return None代码解析与避坑指南excludeSwitches选项 这个设置非常重要。没有它Chrome浏览器顶部会显示“Chrome正受到自动测试软件的控制”某些网站可能会检测到这个特征并阻止自动化操作。加上这个选项可以让浏览器看起来更“正常”。detach选项 设置为True后当Python脚本执行完毕浏览器窗口不会自动关闭。这非常有利于我们手动检查页面状态、定位元素是调试阶段的利器。等待策略 绝对不要使用大量的time.sleep(10)网络速度和电脑性能不同固定等待不是长了就是短了。WebDriverWait配合EC预期条件是标准做法。这里我们等待“聊天”这个页面元素出现因为它只有在登录成功后才会渲染出来。登录超时 设置了60秒等待时间如果超过一分钟还没扫码脚本会报错退出。你可以根据自己情况调整。4.2 定位并进入表情包收藏页面登录成功后我们需要在左侧导航栏中找到并点击“收藏”图标然后在收藏内容中筛选出“表情”。def navigate_to_emoji_collection(driver): 从主界面导航到表情包收藏列表 print(正在尝试进入收藏夹...) try: # 方法1尝试通过aria-label属性定位收藏按钮更精确 # 使用F12开发者工具检查元素发现收藏按钮的aria-label属性可能是‘收藏’ fav_button driver.find_element(By.CSS_SELECTOR, [aria-label收藏]) fav_button.click() time.sleep(2) # 点击后等待页面加载 except: # 方法2如果方法1失败尝试通过侧边栏的图标顺序来定位稳定性稍差 print(方法1定位失败尝试通过图标顺序定位...) # 通常侧边栏图标顺序是聊天、通讯录、收藏、朋友圈等。收藏通常是第三个。 side_icons driver.find_elements(By.CSS_SELECTOR, .nav-item) # 这个类名需要实际检查 if len(side_icons) 3: side_icons[2].click() # 索引从0开始所以第3个是索引2 time.sleep(2) else: print(无法定位收藏按钮请手动点击后按回车继续...) input() # 暂停脚本等待用户手动点击 print(正在筛选表情包...) # 进入收藏页面后我们需要点击顶部的“表情”分类 # 同样需要利用开发者工具查看这些分类按钮的HTML结构 try: # 假设分类按钮是包含在某个容器里的文本为“表情” emoji_filter WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, //div[contains(text(), 表情)])) ) emoji_filter.click() time.sleep(3) # 等待表情列表加载 print(已成功进入表情包收藏列表。) return True except Exception as e: print(f定位表情分类失败: {e}) # 可以尝试其他定位方式比如通过data-id属性等 return False避坑指南元素定位的脆弱性 这是整个项目最不稳定的一环。微信网页版的前端结构可能会更新导致之前可用的CSS选择器或XPath失效。上述代码中的.nav-item、[aria-label收藏]都是示例你需要用自己的浏览器的开发者工具F12进行实时查看和调整。如何定位元素 在Chrome中打开网页版微信按F12点击左上角的箭头图标然后去页面上点击你想操作的元素如“收藏”图标代码区就会高亮显示对应的HTML代码。右键该代码选择Copy - Copy selector 或 Copy XPath可以快速获得定位路径但通常需要微调。备用方案与手动干预 代码中提供了备用定位方法和手动干预的input()暂停。在实际自动化脚本中考虑到网页结构的潜在变动加入一些容错和手动干预点是非常实用的。4.3 滚动加载与表情包链接提取表情包列表是动态懒加载的不会一次性全部显示。我们需要模拟滚动到底部触发加载更多然后从页面中提取所有表情包的图片链接和名称。def scroll_and_collect_links(driver): 滚动列表以加载全部表情并收集图片链接和名称 print(开始滚动加载表情包列表...) last_height driver.execute_script(return document.body.scrollHeight) emoji_data [] # 用于存储名称, url的列表 seen_urls set() # 用于去重避免重复收集 scroll_attempt 0 max_attempts 20 # 防止无限滚动 while scroll_attempt max_attempts: # 1. 模拟滚动到页面底部 driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) time.sleep(2) # 等待新内容加载 # 2. 尝试从当前页面中提取表情包元素 # 表情包在DOM中通常表现为img标签并且有特定的类名或属性 # 需要再次使用开发者工具分析 emoji_elements driver.find_elements(By.CSS_SELECTOR, img.emoji-img) # 例如类名可能是.emoji-img for elem in emoji_elements: try: img_url elem.get_attribute(src) # 有些可能是base64缩略图我们需要原图链接。原图链接可能在data-src或其他属性中 if not img_url or base64 in img_url: img_url elem.get_attribute(data-src) or elem.get_attribute(data-original) if not img_url or img_url in seen_urls: continue # 获取表情包名称可能从alt属性、父元素或相邻元素中获取 img_name elem.get_attribute(alt) if not img_name or img_name.strip() : # 如果alt为空尝试从附近的元素找名字 # 例如名字可能在一个兄弟div里 parent elem.find_element(By.XPATH, ./..) # 父元素 sibling_text parent.text img_name sibling_text if sibling_text else femoji_{len(emoji_data)} # 简单的名称清理移除非法文件名字符 import re img_name re.sub(r[\\/*?:|], _, img_name)[:100] # 限制长度 emoji_data.append((img_name, img_url)) seen_urls.add(img_url) except Exception as e: print(f提取单个表情信息时出错: {e}) continue # 3. 检查是否滚动到了底部 new_height driver.execute_script(return document.body.scrollHeight) if new_height last_height: # 高度未变可能已加载完毕再等待一次确认 time.sleep(3) new_height driver.execute_script(return document.body.scrollHeight) if new_height last_height: print(已滚动到底部列表加载完毕。) break last_height new_height scroll_attempt 1 print(f已滚动 {scroll_attempt} 次已收集 {len(emoji_data)} 个表情链接。) print(f总共收集到 {len(emoji_data)} 个唯一表情包。) return emoji_data核心难点解析懒加载与滚动driver.execute_script用于执行JavaScript代码。window.scrollTo是滚动页面的标准JS方法。通过不断滚动并比较滚动前后的页面高度我们可以判断是否已加载全部内容。链接提取的复杂性 网页上显示的图片src属性可能是一个小的缩略图或base64数据真正的原图链接往往藏在>import requests from PIL import Image from io import BytesIO def download_emojis(emoji_data, base_dir./emojis): 根据收集到的链接和名称下载表情包 # 创建以日期命名的子文件夹 from datetime import datetime date_str datetime.now().strftime(%Y%m%d_%H%M%S) save_dir os.path.join(base_dir, fexport_{date_str}) os.makedirs(save_dir, exist_okTrue) print(f开始下载表情包到目录: {save_dir}) success_count 0 fail_count 0 for index, (name, url) in enumerate(emoji_data): if not url.startswith(http): print(f跳过无效URL: {url}) fail_count 1 continue # 构造文件名确保唯一性 # 从URL推断文件扩展名如.jpg, .gif, .png file_ext .jpg # 默认 if .gif in url.lower(): file_ext .gif elif .png in url.lower(): file_ext .png # 如果名称已包含扩展名则替换 if name.lower().endswith((.jpg, .jpeg, .gif, .png)): file_name name else: file_name f{name}{file_ext} # 处理文件名冲突 file_path os.path.join(save_dir, file_name) counter 1 while os.path.exists(file_path): name_part, ext_part os.path.splitext(file_name) file_path os.path.join(save_dir, f{name_part}_{counter}{ext_part}) counter 1 # 下载图片 try: headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } response requests.get(url, headersheaders, timeout15) response.raise_for_status() # 检查HTTP错误 # 验证是否为有效图片可选但推荐 try: img Image.open(BytesIO(response.content)) img.verify() # 验证文件完整性 # 如果是GIFPIL的verify可能不够可以尝试重新打开一次 img Image.open(BytesIO(response.content)) img.load() # 强制加载图像数据 except Exception as img_err: print(f文件 {file_name} 下载内容不是有效图片: {img_err}) fail_count 1 continue # 保存文件 with open(file_path, wb) as f: f.write(response.content) success_count 1 if success_count % 10 0: print(f已成功下载 {success_count} 个表情包...) except requests.exceptions.RequestException as req_err: print(f下载失败 [{file_name}]: {req_err}) fail_count 1 except Exception as e: print(f保存失败 [{file_name}]: {e}) fail_count 1 print(f下载完成成功: {success_count}, 失败: {fail_count}) print(f所有表情包已保存至: {os.path.abspath(save_dir)})关键细节与优化文件夹组织 按导出日期时间创建子文件夹方便管理多次导出的结果。文件扩展名推断 微信表情包链接通常包含.gif、.jpg等扩展名信息。我们从URL中提取并作为默认扩展名。如果从页面获取的名称已包含扩展名则优先使用。文件名冲突处理 如果两个表情包名称相同或处理后相同直接保存会覆盖。这里使用了一个简单的循环在文件名后添加数字后缀如_1,_2来解决冲突。图片完整性验证 使用Pillow库打开下载的二进制数据并调用verify()和load()可以有效地过滤掉损坏的图片或非图片文件如下载到的是错误页面HTML确保本地保存的都是可用的图片。请求头设置 在requests.get中设置User-Agent模拟普通浏览器的请求避免被简单的反爬机制拦截。4.5 主函数与流程串联最后我们将所有函数串联起来形成一个完整的脚本。def main(): 主执行函数 print( 微信表情包批量导出脚本启动 ) driver login_to_wechat() if not driver: print(登录失败脚本退出。) return try: if navigate_to_emoji_collection(driver): emoji_list scroll_and_collect_links(driver) if emoji_list: download_emojis(emoji_list) else: print(未收集到任何表情包链接。) else: print(无法导航到表情包收藏页面。) except KeyboardInterrupt: print(\n用户中断操作。) except Exception as e: print(f脚本执行过程中发生未知错误: {e}) finally: # 注释掉 quit因为启动时设置了 detachTrue方便查看最终页面状态 # driver.quit() print(脚本执行结束。浏览器窗口已保留如需关闭请手动操作。) if __name__ __main__: main()5. 常见问题与排查技巧实录即使按照上述步骤操作你也可能会遇到各种问题。下面是我在开发和多次运行中遇到的典型问题及解决方法。5.1 登录相关问题问题二维码不显示或刷新不出来。排查 检查网络连接确保能正常访问https://wx.qq.com。尝试更换网络环境如切换Wi-Fi/手机热点。可能是微信服务器临时问题稍后再试。脚本层面 确保浏览器窗口最大化有时在最小化或非激活状态下页面渲染会出问题。可以在driver.get()后加一个time.sleep(5)给页面充分加载时间。问题扫码后提示“登录环境异常”登录失败。排查 这是微信常见的安全策略。网页版微信对自动化登录比较敏感。解决降低自动化特征 确保使用了excludeSwitches和useAutomationExtension选项。使用已登录的浏览器用户数据 这是最有效的方法。在初始化ChromeOptions时指定一个本地Chrome用户数据目录。options.add_argument(ruser-data-dirC:\Users\YourName\AppData\Local\Google\Chrome\User Data) options.add_argument(profile-directoryDefault) # 默认配置文件这样启动的浏览器就是你日常使用的、已经登录过微信的Chrome通常无需再次扫码。注意使用此方式时关闭所有正在运行的Chrome浏览器实例否则会报错。5.2 元素定位失败问题find_element抛出NoSuchElementException。排查 这是最常见的问题意味着你写的CSS选择器或XPath找不到对应元素。解决手动验证 在脚本运行到报错的地方时浏览器窗口是打开的。立即按F12打开开发者工具在Console里尝试用document.querySelector(‘你的选择器’)验证是否能找到元素。等待时机 元素还没加载出来你就去找它了。在find_element前增加显式等待WebDriverWait确保元素出现、可见或可点击。更新定位器 微信网页版结构可能已更新。重新使用开发者工具检查元素获取新的选择器。XPath虽然灵活但更脆弱优先尝试CSS选择器。使用更通用的定位方式 如果图标是img可以尝试用src属性包含的部分路径来定位例如driver.find_element(By.CSS_SELECTOR, img[src*favicon])。5.3 表情包链接提取为空或错误问题emoji_data列表为空或者收集到的链接都是base64缩略图。排查scroll_and_collect_links函数中的选择器img.emoji-img不对。解决在浏览器中手动滚动到表情包列表右键点击一个表情图片选择“检查”。仔细观察这个img标签的类名、id、以及所有>
返回列表