简述
万圣节快到了,需要一些万圣节主题的图片制作成海报,到网站一张一张下载太过麻烦,就想着实现一个简单的Python爬虫工具来获取万圣节相关的图片,由于一些网站可能会通过robots.txt或其他手段禁止爬虫行为,你应当首先确认该网站是否允许被爬取,爬取内容时应当遵守网站的robots.txt规则以及相关的使用条款,避免非法爬取。我今天要爬取的网站是https://www.istockphoto.com/, 该站是允许爬取的。好了,开始了,可以使用requests和BeautifulSoup来爬取网页内容。另外,istockphoto网站通常会使用JavaScript加载图片,这意味着仅使用requests和BeautifulSoup可能无法抓取动态加载的内容。在这种情况下,可以考虑使用Selenium
或Playwright来处理JavaScript渲染。
我今天实现的是基于Selenium的爬虫,它能够访问https://www.istockphoto.com/并查找与“万圣节”相关的图片。
准备:安装Selenium库和ChromeDriver
pip install selenium 安装Selenium库, 用于模拟浏览器行为,特别是加载JavaScript内容。
安装一个WebDriver, 我使用的是ChromeDriver(注意控制Chrome浏览器,你需要下载对应版本的ChromeDriver并设置其路径)ChromeDriver的安装不再赘述,下面简短的说一下windows系统的安装与设置吧:
步骤 1: 检查你的Chrome浏览器版本
ChromeDriver的版本必须与已安装的Chrome浏览器版本匹配。
- 打开Chrome浏览器。
- 在地址栏中输入 chrome://settings/help,然后按下回车。
- 你会看到当前的Chrome版本号。
步骤 2: 下载与Chrome版本匹配的ChromeDriver
- 访问ChromeDriver官网。
- 查找与你的Chrome浏览器版本匹配的ChromeDriver版本(对于版本 115 及更高版本,)您可以在 Chrome for Testing (CfT) 可用性信息中心查看各个发布渠道(稳定版、Beta 版、开发者版、Canary 版)的最新 ChromeDriver 版本。。
- 下载适合你操作系统的文件(Windows, Mac, 或 Linux,我用的是win的)。
步骤 3: 安装ChromeDriver
Windows用户
- 下载的chromedriver_win32.zip文件解压缩后,你会得到chromedriver.exe。
- 将这个文件放置在一个路径中,例如:C:\chromedriver\chromedriver.exe。
将这个路径加入系统的环境变量:
右键点击“此电脑”,选择“属性”。
选择“高级系统设置” → “环境变量”。
在“系统变量”区域中,找到“Path”,选择它并点击“编辑”。
点击“新建”,输入C:\chromedriver\,然后点击“确定”。
步骤 4: 测试ChromeDriver
打开终端或命令提示符,输入
chromedriver --version
如果成功安装,你应该会看到类似于 ChromeDriver 117.0.5938.62 这样的版本号信息。
步骤 5: 使用Selenium调用ChromeDriver
在Python代码中,确保你的Selenium脚本能够找到ChromeDriver。例如,如果没有将ChromeDriver添加到系统路径,可以在代码中直接提供其路径。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# 指定chromedriver的路径
service = Service(executable_path='你的chromedriver所在路径')
# 启动浏览器
driver = webdriver.Chrome(service=service)
代码实现
直接上代码(注释写的很清楚,我相信你们可以看明白)代码实现主要的点:
- 中途提示:防止无休止,每下载完20张图片后(可根据实际情况自行修改这个数值),程序会暂停,等待用户的输入。
- 并行下载:提高下载速度,使用多线程下载来并行处理多个图片的下载任务。每次批次处理后重新创建线程池:通过在 while 循环中每次处理完一批图片后,使用 with ThreadPoolExecutor 来创建一个新的线程池。线程池在每一批次结束后会自动关闭,下一次处理时重新创建一个新的线程池。
- 使用断点续传:如果网络中断或者长时间运行,可以记录已下载的图片,避免重复下载。
# Author: Bruce Kevin Chen
# Create code: 2024/10/1 21:22
import os
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from concurrent.futures import ThreadPoolExecutor
import time
# 配置ChromeDriver
chrome_options = Options()
chrome_options.add_argument("--headless") # 在后台运行
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
# 替换为你自己的ChromeDriver路径
service = Service(executable_path='你的chromedriver.exe路径')
# 创建halloween目录,如果不存在
save_dir = 'halloween'
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 下载图片的函数
def download_image(url, idx):
try:
img_data = requests.get(url).content
file_path = os.path.join(save_dir, f'halloween_image_{idx + 1}.jpg')
with open(file_path, 'wb') as handler:
handler.write(img_data)
print(f"Image {idx + 1} downloaded: {url}")
except Exception as e:
print(f"Failed to download image {idx + 1}: {e}")
# 启动浏览器
driver = webdriver.Chrome(service=service, options=chrome_options)
# 打开目标网站
driver.get("https://www.istockphoto.com/photos/halloween")
# 确保页面加载完成
time.sleep(3)
# 找到图片元素
image_elements = driver.find_elements(By.CSS_SELECTOR, 'img')
# 提取图片的src属性
image_urls = []
for idx, img in enumerate(image_elements):
src = img.get_attribute('src')
if src and 'https://media.istockphoto.com' in src:
image_urls.append((src, idx))
# 关闭浏览器
driver.quit()
# 使用线程池并行下载
def download_in_batches(image_urls, batch_size=20):
total_images = len(image_urls)
current_batch = 0
while current_batch * batch_size < total_images:
start = current_batch * batch_size
end = min((current_batch + 1) * batch_size, total_images)
batch = image_urls[start:end]
# 每批次创建一个新的线程池
# 在每一批次的下载中创建一个新的ThreadPoolExecutor,而不是在整个过程中只使用一个线程池。
# 这样每次询问用户是否继续下载时,都会创建一个新的线程池来处理下载任务,防止由于 executor.shutdown(wait=True) 方法 调用后,
# 线程池被关闭,导致无法提交新的任务。在关闭线程池之后,不能再使用同一个线程池提交新的任务。
with ThreadPoolExecutor(max_workers=5) as executor:
# 下载当前批次的图片
for url, idx in batch:
executor.submit(download_image, url, idx)
current_batch += 1
# 如果下载了20张,询问用户是否继续
if current_batch * batch_size < total_images:
user_input = input(f"{end} images downloaded. Do you want to download more? (y/n): ")
if user_input.lower() != 'y':
print("Download stopped by user.")
break
# 每批次下载20张图片,并询问用户是否继续
download_in_batches(image_urls, batch_size=20)