1. 为什么用Selenium来“抓”猫猫图?
你可能试过用普通的爬虫工具去抓取Google Images上的图片,结果发现要么啥也抓不到,要么抓回来的是一堆看不懂的代码,根本不是你要的图片。这事儿我刚开始也头疼过,后来发现,问题出在Google Images这个页面的“脾气”上。
它不像一个静态的、把所有东西都一次性给你的老式网页。你打开它,搜索“cat”,它先给你加载几十张图。当你用鼠标滚轮往下滑,想看更多的时候,神奇的事情发生了:页面没有刷新,也没有跳转,但新的图片就像变魔术一样,“唰”地一下出现在了你眼前。这种技术,我们通常叫它“动态加载”或者“无限滚动”。对于传统的、只抓取网页初始HTML代码的爬虫(比如用requests库加BeautifulSoup)来说,它们只能看到你第一眼看到的那几十张图,后面“变”出来的那些,它们根本“看”不见。
这时候,就该Selenium登场了。你可以把它想象成一个由代码操控的“机器人浏览器”。它能像真人一样,打开Chrome(或者Firefox等),输入网址,滚动页面,点击按钮。我们写的Python代码,就是指挥这个机器人浏览员的“遥控器”。因为Selenium操作的是真实的、渲染完毕的浏览器页面,所以页面上动态加载出来的所有内容,包括那些随着滚动才出现的猫猫图片,它都能“看”得一清二楚,并且能从中把图片的地址给“抠”出来。这就是为什么我们要用Selenium来对付Google Images——它用魔法(动态加载),我们就用更强的魔法(模拟真人操作的浏览器)来打败它。
这个方法的另一个巨大好处是“所见即所得”。你在浏览器里能看到什么,Selenium就能抓到什么,几乎不存在误判。对于咱们这个“抓猫猫图”的小项目来说,目标明确,工具趁手,再合适不过了。接下来,我就带你一步步搭建这个“机器人”,并把它调教得既高效又聪明,避开那些我当初踩过的坑。
2. 开工前的准备:安装与配置
工欲善其事,必先利其器。在开始写代码指挥“机器人”之前,我们得先把“机器人”本体和它的“操作手册”准备好。这里主要需要两样东西:Selenium库,以及一个浏览器驱动。
2.1 安装Selenium库
这一步最简单。打开你的命令行(Windows上是CMD或PowerShell,Mac或Linux上是Terminal),输入下面这行命令,回车:
pip install selenium
如果你用的是Python3,并且系统里同时有Python2和Python3,那可能需要用pip3:
pip3 install selenium
安装过程通常很快,看到“Successfully installed selenium-xx.xx.xx”就表示搞定了。我实测过很多次,网络正常的话基本不会出问题。
2.2 下载并配置浏览器驱动
这是稍微容易卡住的一步,但只要你跟着做,一分钟就能解决。Selenium需要调用一个真实的浏览器来工作,而它和浏览器之间的“翻译官”,就是浏览器驱动。我们以最常用的Chrome浏览器为例。
首先,你需要确认自己电脑上Chrome的版本。打开Chrome,点击右上角的三个点 -> 帮助 -> 关于Google Chrome。记下那个版本号,比如“版本 128.0.6613.138”。
然后,我们需要下载对应版本的ChromeDriver。这里我强烈推荐去淘宝的镜像站下载,速度比官方快得多。地址是:https://registry.npmmirror.com/binary.html?path=chromedriver/。打开这个页面后,找到与你Chrome版本号前三位一致的文件夹。比如你的Chrome是128.0.6613.xxx,就进入128.0.6613.x/这个目录(通常选最新的子版本就行)。
根据你的操作系统,下载对应的压缩包:
- Windows系统:下载
chromedriver_win32.zip - Mac系统(Intel芯片):下载
chromedriver_mac64.zip - Mac系统(Apple Silicon芯片):下载
chromedriver_mac_arm64.zip - Linux系统:下载
chromedriver_linux64.zip
下载完成后,解压压缩包,你会得到一个名为 chromedriver(Windows下是chromedriver.exe)的文件。接下来是关键:把这个文件放在一个系统能够找到的地方。有三种常见方法:
- (推荐)放在Python的Scripts目录下:这个目录通常在你的Python安装路径里,比如
C:\Users\你的用户名\AppData\Local\Programs\Python\Python39\Scripts\或者D:\python\Scripts\。放这里之后,你可以在命令行任何位置直接运行chromedriver。 - 放在项目文件夹里:直接把
chromedriver.exe扔到和你即将要写的Python脚本同一个文件夹里。这样代码里指定路径最简单。 - 添加到系统环境变量PATH中:这是一个一劳永逸的方法,但步骤稍复杂。你可以把
chromedriver.exe所在的文件夹路径(比如D:\webdrivers\)添加到系统的PATH环境变量里。具体怎么加,可以搜索“Windows添加环境变量”或“Mac添加PATH”,教程很多。
为了最省事,我一般就用第二种方法,项目文件夹里干干净净,驱动和脚本在一起。配置好之后,你可以打开命令行,切换到驱动所在的目录,输入 chromedriver(Windows下输入chromedriver.exe)回车。如果出现“Starting ChromeDriver...”等字样,并且没有立刻关闭,说明驱动已经准备好了。按Ctrl+C可以停止它。
3. 核心代码拆解:一步步打造爬虫机器人
环境准备好了,现在我们来写代码,给这个“机器人”注入灵魂。我会把完整的代码拆成几个功能模块,一块一块讲清楚,你完全可以跟着我的注释和解释,自己敲一遍,理解会更深刻。
3.1 初始化与浏览器启动
我们用一个类来组织代码,这样结构清晰,以后想爬别的关键词也方便复用。首先,导入必要的工具包,并设定好搜索目标。
from selenium import webdriver
import time
import os
import requests
# 你想搜索什么,就在这里改。今天我们的主角是‘cat’!
keyword = 'cat'
# 拼接出Google Images的搜索网址
url = f'https://www.google.com.hk/search?q={keyword}&tbm=isch'
class CatImageCrawler:
def __init__(self):
self.url = url # 把网址存为类的一个属性
这里我们定义了一个类CatImageCrawler。__init__是初始化方法,创建这个爬虫对象时,它会自动把我们要爬的网址存好。f‘...{keyword}...’ 是Python的格式化字符串,非常方便,它会将变量keyword的值(也就是‘cat’)填充到字符串里,形成完整的网址。
接下来,我们要启动“机器人浏览器”了。
def init_browser(self):
# 创建Chrome的配置选项
chrome_options = webdriver.ChromeOptions()
# 这个参数可以关闭浏览器顶部“Chrome正受到自动测试软件控制”的提示栏,让环境更干净
chrome_options.add_argument("--disable-infobars")
# 如果你把chromedriver放在了项目文件夹,需要指定路径,例如:
# browser = webdriver.Chrome(executable_path='./chromedriver', options=chrome_options)
# 如果已经配置了PATH环境变量或放在Scripts目录,直接这样写就行:
browser = webdriver.Chrome(options=chrome_options)
# 命令浏览器打开我们的目标网址
browser.get(self.url)
# 最大化窗口,确保一屏能看到更多图片,方便我们后续抓取
browser.maximize_window()
# 等待2秒,让页面充分加载
time.sleep(2)
return browser # 把这个启动好的浏览器对象返回,后面要用
webdriver.Chrome(options=chrome_options) 这一行就是召唤Chrome浏览器的咒语。browser.get() 命令它访问目标网址。maximize_window() 让窗口最大化,这是一个小技巧,因为页面布局可能会随着窗口大小变化,最大化能获得更稳定的元素定位。最后time.sleep(2)是让程序等一下,给网络和浏览器一点时间把图片加载出来,避免后续操作时页面元素还没准备好。
3.2 模拟滚动与图片抓取逻辑
这是整个爬虫最核心的部分:如何让页面不断滚动,并把滚动后新出现的图片地址抓出来。
def download_images(self, browser, scroll_times=10):
# 创建一个文件夹来存我们可爱的猫猫图片
save_path = './cat_pictures'
if not os.path.exists(save_path):
os.makedirs(save_path)
# 用一个集合(Set)来存储已经抓到的图片URL。集合天生能去重,比用列表效率高。
downloaded_urls = set()
img_count = 0 # 给下载的图片编个号
current_scroll_position = 0 # 记录当前滚动到的位置
print("开始滚动页面并抓取图片...")
for i in range(scroll_times):
# 每次滚动,把滚动位置增加800像素。这个数可以调整,模拟人类滚动的幅度。
current_scroll_position += 800
# 这是一段JavaScript代码,通过它来控制浏览器滚动条的位置
scroll_script = f"window.scrollTo(0, {current_scroll_position});"
browser.execute_script(scroll_script)
# 滚动后等待1.5秒,让新图片加载出来。时间太短可能图没加载完,太长则效率低。
time.sleep(1.5)
# 魔法时刻:找到当前浏览器窗口里所有的<img>标签
img_elements = browser.find_elements(By.TAG_NAME, 'img')
# 遍历每一个图片元素
for element in img_elements:
# 获取图片最关键的属性——它的网络地址(src)
img_url = element.get_attribute('src')
# 关键过滤步骤开始!
if img_url and isinstance(img_url, str):
# 过滤条件1:URL长度。Google Images前几张推荐图或广告图的URL长得离谱,不是直接图片。
if len(img_url) > 200:
continue
# 过滤条件2:检查URL中是否包含‘images’字样。真正的图片链接通常都有这个特征。
# 注意:Google的Logo的src里没有‘images’,会被过滤掉。
if 'images' not in img_url:
continue
# 过滤条件3:去重。如果这个URL我们已经下载过了,就跳过。
if img_url in downloaded_urls:
continue
# 通过了所有过滤!现在下载它
try:
downloaded_urls.add(img_url) # 先加入集合,标记为已下载
filename = os.path.join(save_path, f"{img_count}.jpg")
# 使用requests库发起网络请求,获取图片的二进制数据
response = requests.get(img_url, timeout=10)
response.raise_for_status() # 检查请求是否成功(状态码200)
# 将二进制数据写入文件,这就是保存图片的过程
with open(filename, 'wb') as f:
f.write(response.content)
img_count += 1
print(f'成功下载第 {img_count} 张图片: {filename}')
# 礼貌地等待0.3秒再下载下一张,避免请求过于频繁
time.sleep(0.3)
except Exception as e:
# 如果某张图片下载失败,打印错误信息,但不要中断整个程序
print(f'下载失败(URL: {img_url[:50]}...): {e}')
continue
print(f"所有轮次滚动完成。共尝试抓取 {len(downloaded_urls)} 个唯一URL,成功下载 {img_count} 张图片。")
这段代码里有几个我踩过坑后总结的精髓点:
- 用集合(
set())而不是列表(list())存URL:这是为了高效去重。集合查找元素的速度极快,能避免我们反复下载同一张图。原始文章里用列表,每次都要遍历整个列表来检查是否重复,图片多了会非常慢。 - 三层过滤网:
- 长度过滤:直接丢弃超长的URL,它们通常是广告或推荐卡片,不是直接的图片文件。
- 关键词过滤:通过判断
‘images’是否在URL中,可以精准地过滤掉Google的Logo、按钮图标等无关图片。 - 去重过滤:利用集合的特性,保证每张图只下一次。
browser.execute_script():这是Selenium执行JavaScript代码的方法。我们用window.scrollTo来精确控制滚动位置,模拟人的滚动行为。- 异常处理(
try...except):网络请求总有可能失败(图片失效、网络波动)。用try包住下载过程,即使某一张图挂了,也不会影响后续所有图片的抓取,程序会继续运行。
3.3 组装与运行
最后,我们把各个部分组装起来,并提供一个简单的启动入口。
def run(self):
print(f"启动爬虫,目标关键词:'{keyword}'")
browser = self.init_browser() # 启动浏览器
try:
self.download_images(browser, scroll_times=15) # 开始滚动和下载,这里设定了滚动15次
except KeyboardInterrupt:
print("\n用户中断了程序。")
except Exception as e:
print(f"爬虫运行过程中出现未知错误: {e}")
finally:
# 无论成功还是出错,最后都要关闭浏览器,释放资源
browser.quit()
print("浏览器已关闭。")
# 如果直接运行这个脚本,就启动爬虫
if __name__ == '__main__':
crawler = CatImageCrawler()
crawler.run()
run方法就像总指挥。try...except...finally结构确保了程序的健壮性:即使你中途想用Ctrl+C停止,或者程序意外出错,finally块里的browser.quit()都会执行,帮你把打开的浏览器窗口关掉,避免它留在后台占用内存。
4. 优化与实战技巧:让你的爬虫更稳更快
把上面的代码跑起来,你应该已经能收获上百张猫猫图片了。但作为一个有追求的“铲屎官”(指爬虫工程师),我们肯定不满足于此。下面分享几个我实战中总结的优化技巧,能让你的爬虫更智能、更隐蔽、更高效。
4.1 处理页面懒加载与等待策略
你有没有发现,有时候滚动到底部,图片加载会特别慢,或者出现灰色的占位符?这是因为现代网站大量使用“懒加载”技术:图片只有快要进入用户视野时,才去加载真实的高清图。我们的爬虫滚动得太快,可能图片还没加载出来,我们就去抓它的src,抓到的可能是一个模糊的预览图地址或者干脆是空值。
解决方案是使用更聪明的等待。 Selenium提供了两种主要的等待方式:
- 隐式等待:
browser.implicitly_wait(10)。这告诉浏览器,在查找任何一个元素时,如果没立刻找到,就最多等10秒,期间会不断重试。这是一个全局设置。 - 显式等待:针对某个特定条件进行等待,比如等待某个图片元素加载完成。这需要用到
WebDriverWait。
对于我们的场景,可以在每次滚动后,加入一个显式等待,等待页面底部出现新的图片元素。这里我们稍微修改一下滚动循环里的代码:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# ... 在download_images方法的滚动循环内 ...
current_scroll_position += 800
scroll_script = f"window.scrollTo(0, {current_scroll_position});"
browser.execute_script(scroll_script)
# 优化点:等待新图片出现。这里等待最多5秒,直到页面至少比之前多了一个img标签。
try:
wait = WebDriverWait(browser, 5)
# 这个条件比较宽松,实际可以根据需要调整,比如等待某个特定class的图片加载出来
wait.until(lambda driver: len(driver.find_elements(By.TAG_NAME, 'img')) > previous_img_count)
except:
print(f"第{i+1}次滚动后,新图片加载可能较慢或已到底部。")
# 即使没等到,也继续执行,不中断
finally:
previous_img_count = len(browser.find_elements(By.TAG_NAME, 'img'))
time.sleep(1) # 额外再给一点缓冲时间
这个技巧能显著提高抓取图片的完整性和质量,避免抓到占位符。
4.2 反反爬策略与请求头模拟
虽然Selenium模拟的是真人操作,但Google这类大厂依然有办法检测出一些自动化行为。长时间、高频率的访问可能导致IP被暂时限制,或者弹出验证码。我们可以通过一些设置,让我们的“机器人”看起来更像一个真人用户。
- 修改Chrome选项:在
init_browser方法里,我们可以添加更多参数。chrome_options = webdriver.ChromeOptions() chrome_options.add_argument("--disable-infobars") chrome_options.add_argument("--disable-blink-features=AutomationControlled") # 隐藏自动化控制特征 chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) # 同上 chrome_options.add_experimental_option('useAutomationExtension', False) # 可以添加一个真实的User-Agent,但Selenium会自动使用当前Chrome版本的UA,通常不需要改。 # chrome_options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...') - 控制请求频率:我们已经做了两处等待:滚动后等待页面加载(
time.sleep(1.5)),以及每下载一张图后等待(time.sleep(0.3))。千万不要为了图快而把这些等待去掉! 这是最基本的礼貌,也是对目标网站服务器的尊重。你可以微调这些时间(比如改成1秒和0.5秒),但不要进行无间隔的狂轰滥炸。 - 使用代理IP(高级):如果你需要大规模爬取,可以考虑轮换代理IP。这在Selenium中可以通过
chrome_options.add_argument('--proxy-server=http://ip:port')来实现。但请注意,寻找稳定、可靠的免费代理本身就是一个技术活,且本教程主要面向初学者,这里就不深入展开了。对于抓取几百张猫猫图来说,正常速度下的单IP访问完全足够。
4.3 错误处理与日志记录
一个健壮的程序必须能妥善处理各种意外。我们的代码里已经有了基本的try...except来捕获下载错误。我们可以把它做得更好。
- 细分异常类型:
requests.get()可能会抛出连接超时、拒绝连接、HTTP错误(如404)等。我们可以捕获更具体的异常,并做出不同反应。try: response = requests.get(img_url, timeout=10) response.raise_for_status() # 如果状态码不是200,会抛出HTTPError异常 # ... 保存图片 ... except requests.exceptions.Timeout: print(f"超时:{img_url[:50]}...") except requests.exceptions.ConnectionError: print(f"连接错误:{img_url[:50]}...") except requests.exceptions.HTTPError as e: print(f"HTTP错误(状态码 {response.status_code}): {img_url[:50]}...") except Exception as e: print(f"未知下载错误: {e}") - 记录日志到文件:与其只在屏幕上打印,不如把成功和失败的信息都记录到一个日志文件里,方便事后分析。
import logging logging.basicConfig(filename='cat_crawler.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') # 在成功下载后 logging.info(f'成功下载第 {img_count} 张图片,URL: {img_url}') # 在下载失败后 logging.warning(f'下载失败,URL: {img_url}, 错误: {e}')
这些优化能让你的爬虫从“能用”升级到“好用且可靠”。最后,把所有这些代码块按顺序组合在一起,你就得到了一个功能完整、经过优化的Google Images猫猫图片爬虫。运行它,泡杯咖啡,回来就能看到一个装满可爱猫咪的文件夹了。记住,技术是用来创造乐趣和价值的,请务必遵守网站的服务条款,合理、有节制地使用爬虫。
1380



被折叠的 条评论
为什么被折叠?



