Jichen

Back

基于Python的数据采集实战:网站图片爬虫开发与反爬虫策略详解Blur image

引言#

在当今互联网时代,数据采集已成为许多业务和技术研究的基础工作。本文将详细介绍一个实用的Python图片爬虫开发过程,该爬虫能够从目标网站随机获取页面,提取其中的图片资源,并自动整理保存到本地。我们将深入探讨爬虫的各个技术环节,包括随机页面获取、HTML解析、图片下载和本地文件管理等。

项目概述#

这个爬虫项目主要实现以下功能:

  1. 从目标网站随机获取一个页面
  2. 解析页面内容,提取标题和图片URL
  3. 对图片URL进行去重处理
  4. 创建本地目录并按顺序保存图片
  5. 自动打开保存图片的文件夹

技术栈分析#

1. 核心库介绍#

import os
import platform
import re
from pathlib import Path
from urllib.parse import urlparse
import requests
from bs4 import BeautifulSoup
python
  • requests:用于发送HTTP请求,获取网页内容
  • BeautifulSoup:HTML解析库,用于提取页面中的特定元素
  • pathlib:提供面向对象的文件系统路径操作
  • urllib.parse:用于URL解析和处理
  • platform:获取操作系统信息,实现跨平台功能

2. 随机页面获取机制#

def get_random_page_url():
    response = requests.get(f"{BASE_URL}/random.html", headers=HEADERS, timeout=10)
    soup = BeautifulSoup(response.text, 'html.parser')
    script = soup.find('script', string=lambda t: t and 'location=' in t)
    return script.text.split('location="')[1].split('"')[0] if script else None
python

这段代码通过访问网站的/random.html页面获取随机跳转链接,利用BeautifulSoup解析页面中的JavaScript跳转代码,提取目标URL。

3. 页面内容解析#

def extract_page_data(url):
    response = requests.get(url, headers=HEADERS, timeout=10)
    soup = BeautifulSoup(response.text, 'html.parser')
    title = soup.find('h1', class_='text-xxl').get_text(strip=True)
    images = [img['data-src'] for img in soup.find_all('img', {'data-src': True})]
    return title, [img for img in images if img not in DUPLICATE_URLS]
python

这里我们:

  1. 通过CSS选择器定位标题元素
  2. 提取所有带有data-src属性的图片标签
  3. 使用预设的DUPLICATE_URLS集合进行URL去重

4. 文件名处理#

def sanitize_filename(filename):
    title = re.sub(r'\s*\[\d+\]\s*$', '', filename.strip())
    invalid_chars = '<>:"/\\|?*'
    processed_title = []
    for char in title:
        processed_title.append('_' if char in invalid_chars else char)
    return ''.join(processed_title).strip()
python

文件名处理包含三个关键步骤:

  1. 移除标题末尾的[数字]格式内容
  2. 替换Windows文件名非法字符为下划线
  3. 去除首尾空白字符

5. 图片下载#

def download_image(url, path):
    response = requests.get(url, headers=HEADERS, timeout=10, stream=True)
    with open(path, 'wb') as f:
        for chunk in response.iter_content(1024):
            f.write(chunk)
python

使用stream=True参数实现流式下载,避免大文件占用过多内存,通过分块写入提高下载稳定性。

关键技术深度解析#

1. 反爬虫策略应对#

  • User-Agent设置:模拟浏览器访问
HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
}
python
  • 请求超时处理:避免长时间等待
requests.get(..., timeout=10)
python
  • 异常处理机制:增强程序健壮性
try:
    # 可能失败的代码
except Exception as e:
    print(f"错误信息: {e}")
python

2. 跨平台文件处理#

def open_folder(path):
    if platform.system() == "Windows":
        os.startfile(path)
    elif platform.system() == "Darwin":
        os.system(f'open "{path}"')
    else:
        os.system(f'xdg-open "{path}"')
python

根据操作系统类型选择不同的命令打开文件管理器,提升用户体验。

3. 性能优化实践#

  • URL去重:使用集合(Set)实现高效查找
DUPLICATE_URLS = {
    "https://pic1.imgdb.cn/item/67f9c15f88c538a9b5cb7a98.jpg",
    # 其他已知重复URL...
}
python
  • 流式下载:节省内存,支持大文件
response = requests.get(..., stream=True)
for chunk in response.iter_content(1024):
    # 处理数据块
python

项目扩展思路#

  1. 多线程下载:使用concurrent.futures实现并行下载,提高效率
  2. 断点续传:记录下载进度,支持中断后继续
  3. 代理支持:添加代理池应对IP封锁
  4. GUI界面:使用PyQt或Tkinter开发图形界面
  5. 自动压缩:下载完成后打包为ZIP文件

完整代码下载#

点我获取=>随机JK下载器.exe

import os
import platform
import re
from pathlib import Path
from urllib.parse import urlparse

import requests
from bs4 import BeautifulSoup

# 请求头配置
HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
}

BASE_URL = "https://www.jk.rs"
DUPLICATE_URLS = {
    "https://pic1.imgdb.cn/item/67f9c15f88c538a9b5cb7a98.jpg",
    "https://pic.imgdb.cn/item/675fcf9bd0e0a243d4e487f2.webp",
    "https://pic.imgdb.cn/item/675fd214d0e0a243d4e48999.webp",
    "https://pic1.imgdb.cn/item/67fc873988c538a9b5cf9e84.jpg",
    "https://pic.imgdb.cn/item/676ba651d0e0a243d4e9d3aa.webp",
    "https://pic.imgdb.cn/item/6767d6d8d0e0a243d4e8043f.webp",
    "https://pic.imgdb.cn/item/6767d846d0e0a243d4e804b5.webp",
    "https://pic.imgdb.cn/item/675fd089d0e0a243d4e48879.webp"
}


def get_random_page_url():
    """获取随机页面URL"""
    try:
        response = requests.get(f"{BASE_URL}/random.html", headers=HEADERS, timeout=10)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')
        script = soup.find('script', string=lambda t: t and 'location=' in t)
        return script.text.split('location="')[1].split('"')[0] if script else None
    except Exception as e:
        print(f"获取随机页面失败: {e}")
        return None


def extract_page_data(url):
    """提取页面中的标题和图片URL"""
    try:
        response = requests.get(url, headers=HEADERS, timeout=10)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')

        # 提取标题
        title = soup.find('h1', class_='text-xxl').get_text(strip=True)
        for sup in soup.find_all('sup'):
            sup.decompose()

        # 提取图片URL并去重
        images = [img['data-src'] for img in soup.find_all('img', {'data-src': True})]
        return title, [img for img in images if img not in DUPLICATE_URLS]
    except Exception as e:
        print(f"解析页面失败: {e}")
        return None, []


def sanitize_filename(filename):
    """处理文件名的非法字符和末尾的[数字]内容"""
    if not filename:
        return "未命名"

    # 移除末尾的[数字]内容
    title = re.sub(r'\s*\[\d+\]\s*$', '', filename.strip())

    # 替换文件名非法字符
    invalid_chars = '<>:"/\\|?*'
    processed_title = []
    for char in title:
        if char in invalid_chars:
            processed_title.append('_')
        else:
            processed_title.append(char)

    # 去除首尾空白并返回
    return ''.join(processed_title).strip()


def download_image(url, path):
    """下载图片"""
    try:
        response = requests.get(url, headers=HEADERS, timeout=10, stream=True)
        response.raise_for_status()
        with open(path, 'wb') as f:
            for chunk in response.iter_content(1024):
                f.write(chunk)
        return True
    except Exception as e:
        print(f"下载失败 {url}: {e}")
        return False


def open_folder(path):
    """跨平台打开文件夹"""
    try:
        if platform.system() == "Windows":
            os.startfile(path)
        elif platform.system() == "Darwin":  # macOS
            os.system(f'open "{path}"')
        else:  # Linux
            os.system(f'xdg-open "{path}"')
    except Exception as e:
        print(f"无法打开文件夹: {e}")


def main():
    """主执行函数"""
    # 获取随机页面
    page_url = get_random_page_url()
    if not page_url:
        print("无法获取随机页面,程序退出")
        return

    print(f"开始处理页面: {page_url}")

    # 提取页面数据
    title, image_urls = extract_page_data(page_url)
    if not title or not image_urls:
        print("未找到有效数据,程序退出")
        return

    # 创建保存目录
    clean_title = sanitize_filename(title)
    save_dir = Path(clean_title)
    save_dir.mkdir(parents=True, exist_ok=True)
    print(f"创建目录: {save_dir}")

    # 下载图片
    success = 0
    for i, url in enumerate(image_urls, 1):
        ext = os.path.splitext(urlparse(url).path)[1] or '.jpg'
        filename = f"{i}{ext}"
        filepath = save_dir / filename

        print(f"正在下载 ({i}/{len(image_urls)}): {url}")
        if download_image(url, filepath):
            success += 1

    print(f"\n下载完成! 成功下载 {success}/{len(image_urls)} 张图片")
    print(f"图片保存在: {save_dir.absolute()}")
    open_folder(save_dir.absolute())


if __name__ == "__main__":
    main()
python

结语#

本文详细讲解了一个实用网站图片爬虫的开发过程,涵盖了从页面获取、内容解析到文件保存的完整流程。通过这个项目,我们不仅学习了Python网络爬虫的基础技术,还探讨了反爬虫策略、跨平台开发和性能优化等进阶话题。读者可以根据实际需求扩展功能,打造更强大的数据采集工具。

注意事项:在实际使用爬虫时,请遵守目标网站的robots.txt协议和相关法律法规,尊重网站的数据版权和访问限制。

基于Python的数据采集实战:网站图片爬虫开发与反爬虫策略详解
https://shujichen.com/blog/jkspider
Author Jichen
Published at July 2, 2025
Comment seems to stuck. Try to refresh?✨