来宾市网站建设_网站建设公司_营销型网站_seo优化
2025/12/26 15:11:43 网站建设 项目流程

Python实现知乎图片爬虫(无需登录)

那天,朋友小牛突然问我:“有没有能爬知乎小姐姐照片的脚本?”
我笑了笑回了句:“可以有。”
于是就有了这个轻量却实用的小工具。

但今天我们要说的,不是AI生成数字人视频——而是如何用最简单的方式,批量抓取知乎上那些高清、精美、围绕特定主题的图片资源。整个过程无需登录、无验证码、不触发反爬机制,代码干净利落,开箱即用。

这不仅适用于“小姐姐”这类关键词,更广泛用于风景、摄影、插画、设计灵感等视觉内容采集。对于做素材库、AI训练数据准备,甚至短视频创作前的参考图收集,都非常友好。


技术背景与设计思路

知乎作为一个高质量内容社区,用户在回答中常常会附上精心拍摄或制作的配图。这些图片往往分辨率高、构图讲究,且围绕具体问题展开,具备很强的主题一致性。

传统方式是手动翻页保存,效率极低;而主流爬虫方案动辄依赖Selenium模拟浏览器,配置复杂、运行缓慢。我们能不能走一条更轻巧的路?

答案是可以的。

关键在于:知乎的搜索结果和问题页面对未登录用户是完全开放的。只要我们伪装成普通浏览器访问,就能直接从HTML中提取出<img>标签里的真实图片链接,尤其是带有_hd.jpg后缀的高清版本。

因此,我们采用requests + BeautifulSoup + re三件套组合:
-requests负责发起HTTP请求
-BeautifulSoup解析HTML结构
-re正则匹配提取高清图源

整个流程避开JavaScript渲染、免去登录态维护,真正做到低依赖、高可用。


环境准备

pip install requests beautifulsoup4 lxml

建议使用 Python 3.7+ 版本运行。lxml作为解析器比内置html.parser更快更稳定。


完整代码实现

#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Filename: zhihu_image_spider.py """ Info - author : "your_name" - email : "your_email@example.com" - date : "2025.04.05" - desc : "爬取知乎指定关键词下所有回答中的高清图片,无需登录" """ import os import re import sys import time import platform from urllib.parse import quote import requests from bs4 import BeautifulSoup class ZhihuImageSpider: def __init__(self, keyword, save_path): self.keyword = keyword.strip() self.save_path = save_path.rstrip('/\\') self.session = requests.Session() # 设置请求头,伪装成浏览器访问 self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' '(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', 'Referer': f'https://www.zhihu.com/search?q={quote(keyword)}', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', } def create_save_dir(self): """创建保存目录""" if not os.path.exists(self.save_path): try: os.makedirs(self.save_path, mode=0o755) print(f'✅ 成功创建目录:{self.save_path}') return True except Exception as e: print(f'❌ 创建目录失败:{str(e)}') return False else: print(f'📁 目录已存在:{self.save_path}') return True def get_search_page(self): """获取搜索结果页面HTML""" url = f"https://www.zhihu.com/search?q={quote(self.keyword)}&type=content" print(f'🔍 正在请求搜索页:{url}') try: response = self.session.get(url, headers=self.headers, timeout=10) if response.status_code == 200: print('✅ 搜索页获取成功') return response.text else: print(f'❌ 请求失败,状态码:{response.status_code}') return None except Exception as e: print(f'❌ 请求异常:{str(e)}') return None def parse_question_links(self, html): """从搜索页提取问题链接列表""" soup = BeautifulSoup(html, 'lxml') links = set() # 匹配所有以 /question 开头的问题链接 for a in soup.find_all('a', href=re.compile(r'/question/\d+')): href = a['href'] if href.startswith('/'): full_url = 'https://www.zhihu.com' + href links.add(full_url) print(f'📌 共发现 {len(links)} 个相关问题链接') return list(links) def get_question_page(self, url): """获取单个问题页面内容""" print(f'📖 正在加载问题页:{url}') try: response = self.session.get(url, headers=self.headers, timeout=10) if response.status_code == 200: return response.text else: print(f'❌ 加载问题页失败:{url},状态码:{response.status_code}') return None except Exception as e: print(f'❌ 请求异常:{str(e)}') return None def extract_images_from_html(self, html): """从页面中提取所有图片URL(优先高清图)""" img_urls = [] # 使用正则匹配所有 .jpg 结尾的图片,优先捕获 _hd.jpg 高清图 pattern = re.compile(r'src="(https://[^"]+?\.zhimg\.com[^"]*?_hd\.jpg)"') matches = pattern.findall(html) img_urls.extend(matches) # 补充普通图(非_hd) low_res_pattern = re.compile(r'src="(https://[^"]+?\.zhimg\.com[^"]*?\.jpg)"') low_matches = low_res_pattern.findall(html) # 去除已收录的高清图对应的低清图 for low_url in low_matches: base = low_url.replace('_hd.jpg', '.jpg').replace('_xs.jpg', '.jpg') high_version = base.replace('.jpg', '_hd.jpg') if high_version not in matches and low_url not in img_urls: img_urls.append(low_url) # 过滤无效或缩略图 filtered = [] exclude_keywords = ['icon', 'avatar', 'logo', 'banner', 'ic_', 'smy', 'rating'] for u in img_urls: if any(kw in u.lower() for kw in exclude_keywords): continue if u not in filtered: filtered.append(u) print(f'🖼️ 从该页面提取到 {len(filtered)} 张有效图片') return filtered def download_image(self, img_url, filename): """下载图片并保存""" filepath = os.path.join(self.save_path, filename) try: response = self.session.get(img_url, headers=self.headers, stream=True, timeout=10) if response.status_code == 200: with open(filepath, 'wb') as f: for chunk in response.iter_content(1024): f.write(chunk) print(f'💾 已保存:{filename}') return True else: print(f'❌ 下载失败:{img_url},状态码:{response.status_code}') return False except Exception as e: print(f'❌ 下载异常:{filename} -> {str(e)}') return False def run(self): """启动爬虫主流程""" print('=' * 60) print(f'🚀 开始爬取关键词【{self.keyword}】的相关图片...') print('=' * 60) # 1. 创建保存目录 if not self.create_save_dir(): print('⛔ 目录创建失败,终止爬虫') return # 2. 获取搜索页内容 search_html = self.get_search_page() if not search_html: print('⛔ 搜索页获取失败,终止') return # 3. 解析问题链接 question_urls = self.parse_question_links(search_html) if not question_urls: print('⚠️ 未找到任何问题链接,请检查关键词是否有效') return # 4. 遍历每个问题页 all_images = [] for q_url in question_urls: time.sleep(1) # 控制频率,避免被限流 q_html = self.get_question_page(q_url) if q_html: imgs = self.extract_images_from_html(q_html) all_images.extend(imgs) else: print(f'跳过此问题页') # 5. 去重 unique_images = list(set(all_images)) print(f'\n📊 总共收集到 {len(unique_images)} 张唯一图片链接') # 6. 下载图片 success_count = 0 for idx, img_url in enumerate(unique_images, start=1): ext = os.path.splitext(img_url)[1] if '.' in img_url else '.jpg' filename = f'{idx:04d}{ext}' if self.download_image(img_url, filename): success_count += 1 time.sleep(0.5) # 小延迟,友好访问 # 7. 完成提示 print('\n' + '=' * 60) print(f'🎉 爬虫执行完毕!') print(f'📄 关键词:{self.keyword}') print(f'📂 保存路径:{self.save_path}') print(f'🖼️ 图片总数:{success_count} 张') print(f'🔗 失败数量:{len(unique_images) - success_count} 张') print('=' * 60) # ========== 用户输入区 ========== if __name__ == '__main__': keyword_input = input('请输入要搜索的关键词(如:风景、摄影、插画):').strip() while not keyword_input: keyword_input = input('关键词不能为空,请重新输入:').strip() path_input = input('请输入保存图片的目录路径(直接回车使用默认路径):').strip() # 默认路径逻辑 if not path_input: sys_info = platform.system() if sys_info == 'Windows': default_path = os.path.expanduser(r'~\Documents\ZhihuImages') else: default_path = os.path.expanduser('~/ZhihuImages') final_path = os.path.join(default_path, keyword_input) else: final_path = path_input # 实例化并运行 spider = ZhihuImageSpider(keyword=keyword_input, save_path=final_path) spider.run()

实际运行效果

运行脚本后交互如下:

python zhihu_image_spider.py

输入示例:

请输入要搜索的关键词(如:风景、摄影、插画):樱花 请输入保存图片的目录路径(直接回车使用默认路径): ✅ 成功创建目录:/Users/xxx/Documents/ZhihuImages/樱花 🔍 正在请求搜索页:https://www.zhihu.com/search?q=%E6%A8%B1%E8%8A%B1&type=content ✅ 搜索页获取成功 📌 共发现 18 个相关问题链接 📖 正在加载问题页:https://www.zhihu.com/question/12345678 🖼️ 从该页面提取到 23 张有效图片 📖 正在加载问题页:https://www.zhihu.com/question/87654321 🖼️ 从该页面提取到 15 张有效图片 ... 💾 已保存:0001.jpg 💾 已保存:0002.jpg ... 🎉 爬虫执行完毕! 📄 关键词:樱花 📂 保存路径:/Users/xxx/Documents/ZhihuImages/樱花 🖼️ 图片总数:137 张 🔗 失败数量:3 张

几分钟内即可完成上百张高清图的自动下载,命名规整,便于后续处理。


为什么它能绕过反爬?

很多人以为爬知乎必须登录、必须处理Token、必须模拟点击加载更多……其实不然。

核心点在于:我们只抓公开内容,不碰私域数据

知乎的搜索页和问题详情页对未登录用户是可访问的。虽然部分内容会折叠,但HTML中仍然包含完整的<img src="...">标签信息。这意味着我们可以直接从中提取原始图片地址,尤其是形如:

https://pica.zhimg.com/v2-xxxxxx_r.jpg https://pica.zhimg.com/v2-xxxxxx_hd.jpg ← 我们优先抓这个

其中_hd.jpg就是高清原图,清晰度远高于缩略图。通过正则精准匹配,我们能有效过滤掉头像、图标等干扰项。

此外,请求头中设置合理的User-AgentReferer,让服务器误以为是正常浏览器访问,大大降低被拦截概率。


反爬优化技巧

别忘了,即使目标网站允许访问,也要做个“礼貌的访客”。

以下是几个工程实践中总结的经验:

  • 加延时控制:每次请求间隔time.sleep(1),防止短时间内大量请求。
  • 随机UA轮换(进阶):可维护一个UA池,每次请求随机选取,进一步降低指纹识别风险。
  • 失败重试机制:网络波动可能导致个别请求失败,加入最多2次重试可提升稳定性。
  • 跳过低质量页面:若某问题页返回空或异常,记录日志后继续下一个,不影响整体流程。

⚠️ 注意:不要使用多线程暴力并发。知乎虽未严格封禁IP,但高频请求仍可能触发临时限制。


可拓展方向

这个脚本只是一个起点。根据实际需求,你可以轻松扩展以下功能:

功能实现方式
支持翻页加载更多内容使用 Selenium 模拟滚动到底部,抓取动态加载的回答
提取图片对应文字描述在保存图片的同时,将上下文段落一并存为.txt文件
内容级去重计算每张图的MD5或感知哈希值(pHash),剔除重复素材
并发下载加速引入ThreadPoolExecutor实现多图并行下载,提升效率
GUI界面操作用 Tkinter 或 PyQt 做个图形化窗口,方便非程序员使用

例如,并发下载部分可以这样改造:

from concurrent.futures import ThreadPoolExecutor def download_all_images(self, image_list): success_count = 0 with ThreadPoolExecutor(max_workers=4) as executor: futures = [] for idx, img_url in enumerate(image_list, start=1): ext = os.path.splitext(img_url)[1] or '.jpg' filename = f'{idx:04d}{ext}' futures.append(executor.submit(self.download_image, img_url, filename)) for future in futures: if future.result(): success_count += 1 return success_count

只需几行改动,下载速度就能提升数倍。


最后的思考:从一张图开始的AI创作链

回到开头那个“小牛”的需求。他真正想要的,从来都不是一堆静态照片。

他是想——用自己的声音,让某个喜欢的人物“开口说话”

而这正是当前火爆的AI 数字人技术的典型应用场景。

比如腾讯与浙大联合推出的Sonic 模型,仅需一张肖像照 + 一段音频,就能生成唇形同步、表情自然的说话视频。

如果我们把这套爬虫当作“前端采集模块”,后续接入 AI 视频生成工作流,会发生什么?

  1. 用本脚本批量下载明星/动漫角色肖像;
  2. 录一段自己配音的台词(比如“今天你学习了吗?”);
  3. 用 Sonic 驱动图像生成动态嘴型;
  4. 导出为 MP4,用于短视频平台发布。

整个流程全自动,零手工剪辑。

你会发现,真正的生产力,来自于工具之间的串联

而我们写的每一行代码,都是通往未来的拼图碎片。


✅ 示例项目地址(可选):https://github.com/example/zhihu-image-spider
💬 欢迎留言交流你的改进想法!

转载请注明出处。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询