import re import os import requests import time from urllib.parse import urlparse, unquote import hashlib # 请求网址 url = 'https://mp.weixin.qq.com/s/DWsowFDU7D5baNyTGXDWGw' # 请求头(添加更多头部信息以提高成功率) headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36', 'referer': 'https://mp.weixin.qq.com/', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', 'cache-control': 'max-age=0', 'connection': 'keep-alive', } try: response = requests.get(url=url, headers=headers, timeout=30) response.raise_for_status() response.encoding = response.apparent_encoding except Exception as e: print(f"请求失败: {e}") exit() # 改进的正则表达式,更精确地匹配图片链接 # 匹配微信文章中的图片链接格式 patterns = [ # 匹配微信图片链接(带wx_fmt参数) r'data-src="(https?://mmbiz\.qpic\.cn/[^"]+?wx_fmt=(?:jpeg|png|gif|bmp|webp)[^"]*?)"', r'src="(https?://mmbiz\.qpic\.cn/[^"]+?wx_fmt=(?:jpeg|png|gif|bmp|webp)[^"]*?)"', # 匹配其他常见的图片链接 r'(https?://mmbiz\.qpic\.cn/[^"\s]+?\.(?:jpg|jpeg|png|gif|bmp|webp)[^\s"]*)', ] all_image_urls = [] for pattern in patterns: matches = re.findall(pattern, response.text, re.IGNORECASE) all_image_urls.extend(matches) # 过滤掉不需要的链接 def is_valid_image_url(url): """检查是否是有效的图片链接""" # 排除JS文件、GIF占位图等 exclude_patterns = [ 'js', 'pic_blank.gif', '//res.wx.qq.com/', '.concat', 'hit_biz_headimg', 'emoji_url', ] for pattern in exclude_patterns: if pattern in url: return False # 必须是有效的URL if not url.startswith(('http://', 'https://')): return False # 检查是否是图片格式 img_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'] for ext in img_extensions: if ext in url.lower(): return True # 或者包含微信图片格式参数 if 'wx_fmt=' in url.lower(): return True return False # 过滤和去重 valid_image_urls = [] for img_url in all_image_urls: if is_valid_image_url(img_url) and img_url not in valid_image_urls: valid_image_urls.append(img_url) print(f"找到 {len(valid_image_urls)} 个有效图片链接:") for i, img_url in enumerate(valid_image_urls, 1): print(f"{i:2d}. {img_url}") # 指定保存路径 save_path = r"F:\测试\图片" # 确保目录存在 if not os.path.exists(save_path): os.makedirs(save_path) print(f"创建目录: {save_path}") # 下载图片 success_count = 0 fail_count = 0 for i, img_url in enumerate(valid_image_urls, 1): try: # 生成文件名 # 从URL中提取有用的部分作为文件名 parsed_url = urlparse(img_url) # 尝试获取图片格式 fmt_match = re.search(r'wx_fmt=(\w+)', img_url, re.IGNORECASE) if fmt_match: ext = fmt_match.group(1).lower() if ext == 'jpeg': ext = 'jpg' else: # 从URL路径猜测扩展名 path_lower = parsed_url.path.lower() if '.jpg' in path_lower or '.jpeg' in path_lower: ext = 'jpg' elif '.png' in path_lower: ext = 'png' elif '.gif' in path_lower: ext = 'gif' elif '.webp' in path_lower: ext = 'webp' else: ext = 'jpg' # 默认 # 使用序号和URL哈希作为文件名(避免特殊字符) url_hash = hashlib.md5(img_url.encode()).hexdigest()[:6] filename = f"weixin_image_{i:03d}_{url_hash}.{ext}" filepath = os.path.join(save_path, filename) # 如果文件已存在,跳过 if os.path.exists(filepath): print(f"{i:2d}. 跳过: {filename} (已存在)") success_count += 1 continue print(f"{i:2d}. 正在下载: {filename}") # 下载图片(添加图片请求特定的headers) img_headers = headers.copy() img_headers['accept'] = 'image/webp,image/apng,image/*,*/*;q=0.8' img_response = requests.get(img_url, headers=img_headers, timeout=30, stream=True) img_response.raise_for_status() # 检查是否是图片 content_type = img_response.headers.get('Content-Type', '').lower() if 'image' not in content_type: print(f" 警告: 响应不是图片,类型: {content_type}") # 获取文件大小 content_length = img_response.headers.get('Content-Length') if content_length: file_size = int(content_length) else: file_size = len(img_response.content) # 保存图片 with open(filepath, 'wb') as f: if hasattr(img_response, 'iter_content'): # 使用流式下载 for chunk in img_response.iter_content(chunk_size=8192): if chunk: f.write(chunk) else: f.write(img_response.content) # 验证文件大小 actual_size = os.path.getsize(filepath) if file_size > 0 and actual_size < file_size * 0.9: # 如果实际大小小于预期90% print(f" 警告: 文件可能未完整下载 (预期: {file_size}, 实际: {actual_size})") print(f" 保存成功: {filename} ({actual_size/1024:.1f} KB)") success_count += 1 # 延迟,避免请求过快 time.sleep(0.3) except requests.exceptions.RequestException as e: print(f"{i:2d}. 下载失败: {e}") fail_count += 1 except Exception as e: print(f"{i:2d}. 处理失败: {e}") fail_count += 1 print(f"\n下载完成!") print(f"成功: {success_count} 张") print(f"失败: {fail_count} 张") print(f"保存路径: {save_path}") # 显示保存的文件 if success_count > 0: print("\n保存的文件:") img_files = [f for f in os.listdir(save_path) if f.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'))] for i, filename in enumerate(sorted(img_files)[:10], 1): # 只显示前10个 filepath = os.path.join(save_path, filename) size_kb = os.path.getsize(filepath) / 1024 print(f"{i:2d}. {filename} ({size_kb:.1f} KB)") if len(img_files) > 10: print(f"... 还有 {len(img_files) - 10} 个文件")