从零搭建CSDN博客爬虫:Python爬虫+多格式导出完整教程

📌 写在前面

在日常工作中,我们经常需要备份自己的博客文章,或者对某个优质博主的文章进行系统性的整理和分析。手动一个一个复制显然效率太低,这时候就需要一个专业的爬虫工具来帮助我们。

本文将带你从零开始,使用 Python 构建一个完整的 CSDN 博客爬虫工具。我们将涵盖:

  • 项目架构设计
  • 爬虫核心实现
  • 多格式导出器
  • 最佳实践

技术栈:Python 3.8+, Requests, BeautifulSoup4, lxml

项目源码csdn-blog-scraper(欢迎 Star ⭐)

个人主页艺杯羹


文章目录

    • [📌 写在前面](#📌 写在前面)
    • 一、项目架构设计
      • [1.1 为什么需要模块化设计?](#1.1 为什么需要模块化设计?)
      • [1.2 模块职责分工](#1.2 模块职责分工)
    • 二、配置管理模块
      • [2.1 使用 `@dataclass` 简化配置类](#2.1 使用 @dataclass 简化配置类)
      • [2.2 配置类的优点](#2.2 配置类的优点)
      • [2.3 序列化支持](#2.3 序列化支持)
    • 三、日志模块封装
      • [3.1 为什么需要专业的日志模块?](#3.1 为什么需要专业的日志模块?)
      • [3.2 日志效果展示](#3.2 日志效果展示)
    • 四、爬虫核心实现
      • [4.1 爬虫类初始化](#4.1 爬虫类初始化)
      • [4.2 请求头与延迟控制](#4.2 请求头与延迟控制)
      • [4.3 安全请求与重试机制](#4.3 安全请求与重试机制)
      • [4.4 页面解析------提取文章信息](#4.4 页面解析——提取文章信息)
      • [4.5 分页爬取与控制](#4.5 分页爬取与控制)
    • 五、多格式导出器设计
      • [5.1 为什么使用工厂模式?](#5.1 为什么使用工厂模式?)
      • [5.2 基类与子类实现](#5.2 基类与子类实现)
      • [5.3 工厂类实现](#5.3 工厂类实现)
      • [5.4 各格式输出效果对比](#5.4 各格式输出效果对比)
    • 六、整合运行与测试
      • [6.1 便捷的保存方法](#6.1 便捷的保存方法)
      • [6.2 完整使用示例](#6.2 完整使用示例)
      • [6.3 运行效果截图](#6.3 运行效果截图)
    • 七、总结与最佳实践
      • [7.1 核心知识点回顾](#7.1 核心知识点回顾)
      • [7.2 最佳实践建议](#7.2 最佳实践建议)
      • [7.3 常见问题解答](#7.3 常见问题解答)
      • [7.4 扩展方向](#7.4 扩展方向)
    • [📦 项目源码](#📦 项目源码)

一、项目架构设计

1.1 为什么需要模块化设计?

一个好的项目,从清晰的架构开始。我们采用模块化设计,将不同功能拆分为独立的模块,每个模块各司其职:

复制代码
csdn-blog-scraper/
├── src/                    # 源代码目录
│   ├── __init__.py        # 包初始化
│   ├── config.py          # 配置管理
│   ├── scraper.py         # 核心爬虫
│   ├── exporters.py       # 多格式导出
│   └── utils.py           # 工具函数
├── main.py                # 命令行入口
└── requirements.txt       # 依赖列表

1.2 模块职责分工

模块 文件 职责
配置管理 config.py 管理爬虫配置参数
核心爬虫 scraper.py HTTP请求、HTML解析、文章提取
多格式导出 exporters.py JSON/CSV/TXT格式导出
工具函数 utils.py 日志、文件名处理等
入口文件 main.py 命令行参数解析

设计原则

  • 单一职责:每个模块只做一件事
  • 开闭原则:对扩展开放,对修改关闭
  • 依赖倒置:高层模块不依赖低层模块

二、配置管理模块

2.1 使用 @dataclass 简化配置类

Python 3.7 引入的 @dataclass 装饰器,可以自动生成 __init____repr__ 等方法,非常适合用于配置管理:

python 复制代码
from dataclasses import dataclass
from typing import Optional
import os


@dataclass
class Config:
    """爬虫配置类"""
    
    blog_url: str = "https://blog.csdn.net/qq_46987323"  # 目标博客URL
    min_delay: float = 1.5                                 # 最小请求延迟(秒)
    max_delay: float = 3.0                                 # 最大请求延迟(秒)
    request_timeout: int = 30                              # 请求超时时间
    max_retries: int = 3                                   # 最大重试次数
    max_pages: Optional[int] = None                        # 最大爬取页数(None=全部)
    output_dir: str = "outputs"                            # 输出目录
    user_agent: Optional[str] = None                       # 自定义UA(None=随机)
    verify_ssl: bool = True                                # 是否验证SSL
    
    def __post_init__(self):
        """初始化后自动验证配置并创建输出目录"""
        if self.min_delay < 0:
            raise ValueError("min_delay 不能为负数")
        if self.max_delay < self.min_delay:
            raise ValueError("max_delay 必须 >= min_delay")
        if self.max_pages is not None and self.max_pages <= 0:
            raise ValueError("max_pages 必须为正整数")
        
        # 自动创建输出目录
        if not os.path.exists(self.output_dir):
            os.makedirs(self.output_dir)

2.2 配置类的优点

为什么选择 @dataclass

  1. 代码简洁 :自动生成 __init____repr____eq__ 等方法
  2. 类型安全:配合类型提示,IDE智能提示更友好
  3. 不可变性 :配合 frozen=True 可创建不可变对象
  4. 可扩展性__post_init__ 方法可做初始化验证

运行截图效果

复制代码
Config 使用示例:
┌─────────────────────────────────────────────┐
│ blog_url: https://blog.csdn.net/qq_46987323  │
│ min_delay: 1.5秒                             │
│ max_delay: 3.0秒                             │
│ max_pages: 全部                              │
│ output_dir: outputs/                         │
└─────────────────────────────────────────────┘

2.3 序列化支持

python 复制代码
def to_dict(self) -> dict:
    """转为字典(方便保存为配置文件)"""
    return {
        "blog_url": self.blog_url,
        "min_delay": self.min_delay,
        "max_delay": self.max_delay,
        # ... 其他字段
    }

@classmethod
def from_dict(cls, data: dict) -> "Config":
    """从字典创建配置(方便读取配置文件)"""
    return cls(**data)

三、日志模块封装

3.1 为什么需要专业的日志模块?

好的日志系统是调试程序的"眼睛"。我们使用 RotatingFileHandler 实现日志轮转,避免日志文件无限增长:

python 复制代码
import logging
from logging.handlers import RotatingFileHandler
from typing import Optional


def setup_logger(
    name: str = "csdn_scraper",
    log_file: Optional[str] = None,
    log_level: int = logging.INFO
) -> logging.Logger:
    """配置日志系统------同时输出到控制台和文件"""
    
    logger = logging.getLogger(name)
    logger.setLevel(log_level)
    logger.handlers.clear()  # 防止重复添加
    
    # 统一的日志格式
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    
    # 控制台输出
    console_handler = logging.StreamHandler()
    console_handler.setLevel(log_level)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    
    # 文件输出(支持日志轮转)
    if log_file:
        file_handler = RotatingFileHandler(
            log_file,
            maxBytes=5 * 1024 * 1024,  # 5MB 轮转
            backupCount=5,               # 保留 5 个备份
            encoding='utf-8'
        )
        file_handler.setLevel(log_level)
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)
    
    return logger

3.2 日志效果展示

运行爬虫时,控制台会实时显示运行状态:

复制代码
2026-05-06 12:34:56 - csdn_scraper - INFO - 🚀 开始爬取文章...
2026-05-06 12:34:57 - csdn_scraper - INFO - 📄 正在爬取第1页: ...
2026-05-06 12:35:00 - csdn_scraper - INFO - ✅ 第1页: +20 篇文章
2026-05-06 12:35:02 - csdn_scraper - INFO - 📊 总计: 20 篇文章

日志轮转机制

  • 单个日志文件最大 5MB
  • 保留最近 5 个备份
  • 超出后自动删除最旧的日志

四、爬虫核心实现

这是项目的核心部分,也是代码量最多的模块。我们将它拆解为几个关键步骤。

4.1 爬虫类初始化

python 复制代码
import time
import random
import requests
from bs4 import BeautifulSoup
from typing import List, Dict, Any, Optional
from urllib.parse import urljoin


class CSDNBlogScraper:
    """CSDN博客爬虫主类"""
    
    def __init__(self, config: Config = None, logger: logging.Logger = None):
        self.config = config or Config()
        self.logger = logger or setup_logger()
        self.session = requests.Session()  # 复用Session提升性能
        
        # 预定义User-Agent池------规避反爬虫
        self.user_agents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
            '(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
            '(KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 '
            '(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) '
            'Gecko/20100101 Firefox/125.0',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) '
            'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.15',
        ]

4.2 请求头与延迟控制

反爬虫是爬虫开发中最常见的挑战。我们通过以下策略来规避:

策略一:动态User-Agent

随机切换浏览器标识,模拟真实用户访问:

python 复制代码
def _get_headers(self, referer: Optional[str] = None) -> dict:
    """构造HTTP请求头------模拟真实浏览器"""
    user_agent = self.config.user_agent or random.choice(self.user_agents)
    
    headers = {
        'User-Agent': user_agent,
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,'
                  'image/webp,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1',
    }
    
    if referer:
        headers['Referer'] = referer  # 模拟页面跳转来源
    
    return headers

策略二:随机延迟

每次请求之间随机等待,避免被识别为机器行为:

python 复制代码
def _random_delay(self):
    """随机延迟------避免请求频率过高被封锁"""
    delay = random.uniform(self.config.min_delay, self.config.max_delay)
    self.logger.debug(f"⏱️ 等待 {delay:.1f} 秒...")
    time.sleep(delay)

4.3 安全请求与重试机制

网络请求不可靠,所以重试机制是必不可少的:

python 复制代码
def _safe_request(self, url: str, retry: int = 0, 
                  referer: Optional[str] = None) -> Optional[requests.Response]:
    """带重试的安全HTTP请求"""
    
    if retry >= self.config.max_retries:
        self.logger.error(f"❌ 请求失败(已重试{retry}次): {url}")
        return None

    try:
        response = self.session.get(
            url,
            headers=self._get_headers(referer),
            timeout=self.config.request_timeout,
            verify=self.config.verify_ssl
        )
        response.raise_for_status()  # 检查HTTP状态码

        # 返回内容过短时触发重试(可能被反爬)
        if len(response.text) < 200:
            self.logger.warning(f"⚠️ 响应过短,正在重试...")
            self._random_delay()
            return self._safe_request(url, retry + 1, referer)

        return response

    except requests.RequestException as e:
        self.logger.warning(f"⚠️ 请求失败: {e}, 正在重试...")
        self._random_delay()
        return self._safe_request(url, retry + 1, referer)

重试流程图

复制代码
开始请求 → 请求成功? → 是 → 内容正常? → 是 → 返回结果
   ↓                              ↓
  否                             否
   ↓                              ↓
等待随机延迟 ← 重试次数+1       等待随机延迟 ← 重试次数+1
   ↓                              ↓
  不超过3次 → 继续请求          不超过3次 → 继续请求
   ↓                              ↓
  超过3次 → 返回None            超过3次 → 返回None

4.4 页面解析------提取文章信息

这是最核心的环节。CSDN的页面结构可能会变化,所以我们使用多重选择器来提升鲁棒性:

python 复制代码
def _parse_article_item(self, item: BeautifulSoup) -> Optional[Dict[str, Any]]:
    """解析单篇文章信息"""
    article_info = {}
    
    # [核心] 提取标题和链接------支持多种选择器
    title_tag = (item.find('h4') or 
                 item.find('a', class_='title') or 
                 item.find('a'))
    if title_tag:
        if title_tag.name == 'a':
            link_tag = title_tag
        else:
            link_tag = title_tag.find('a')
        
        if link_tag:
            article_info['title'] = link_tag.get_text(strip=True)
            article_info['url'] = link_tag.get('href', '')
    
    if 'title' not in article_info or not article_info['title']:
        return None  # 跳过无效条目
    
    # [核心] 提取发布日期
    date_tag = item.find('span', class_='date')
    if date_tag:
        article_info['date'] = date_tag.get_text(strip=True)
    
    # [核心] 提取阅读量
    read_tag = (item.find('span', class_='read-num') or 
                item.find('span', class_='read-count'))
    if read_tag:
        article_info['views'] = read_tag.get_text(strip=True)
    
    return article_info

4.5 分页爬取与控制

CSDN的博客文章是按分页展示的,我们需要自动遍历所有页面:

python 复制代码
def scrape_page(self, page_num: int) -> List[Dict[str, Any]]:
    """爬取单页文章列表"""
    page_url = f"{self.config.blog_url}/article/list/{page_num}"
    
    response = self._safe_request(page_url, referer=self.config.blog_url)
    if not response:
        return []
    
    soup = BeautifulSoup(response.text, 'lxml')
    
    # 支持多种CSS选择器------适配不同页面结构
    selectors = [
        'div.article-item-box',          # 新版列表
        'ul.colu_author_c > li',         # 个人主页
        'article.blog-list-box',         # 文章卡片
        'div.article-list > div',        # 旧版列表
    ]
    
    article_items = []
    for selector in selectors:
        items = soup.select(selector)
        if items:
            article_items = items
            break
    
    if not article_items:
        return []
    
    # 逐一解析
    articles = []
    for item in article_items:
        info = self._parse_article_item(item)
        if info:
            articles.append(info)
    
    return articles

主循环------自动遍历所有分页:

python 复制代码
def scrape_all_articles(self) -> List[Dict[str, Any]]:
    """爬取所有文章(自动遍历所有分页)"""
    self.logger.info("🚀 开始爬取文章...")
    
    all_articles = []
    page_num = 1
    
    while True:
        # 检查页数限制
        if self.config.max_pages and page_num > self.config.max_pages:
            self.logger.info(f"✅ 达到最大页数限制: {self.config.max_pages}")
            break
        
        # 爬取当前页
        articles = self.scrape_page(page_num)
        
        if not articles:
            self.logger.info(f"✅ 没有更多文章(第{page_num}页为空)")
            break
        
        all_articles.extend(articles)
        
        # 随机延迟------礼貌爬取
        self._random_delay()
        page_num += 1
        
        # 安全限制------最多100页
        if page_num > 100:
            break
    
    self.logger.info(f"🎉 爬取完成!共 {len(all_articles)} 篇文章")
    return all_articles

五、多格式导出器设计

5.1 为什么使用工厂模式?

工厂模式是一种创建型设计模式,它定义一个创建对象的接口,让子类决定实例化哪个类。在我们的项目中,它的优势非常明显:

不使用工厂模式

python 复制代码
# 每次都要写大量的if-else
if format == "json":
    exporter = JSONExporter()
elif format == "csv":
    exporter = CSVExporter()
elif format == "txt":
    exporter = TXTExporter()

使用工厂模式

python 复制代码
# 一行代码搞定
exporter = ExporterFactory.get_exporter("json")

5.2 基类与子类实现

python 复制代码
class BaseExporter:
    """导出器基类------定义统一的接口"""
    
    def export(self, articles: List[dict], filepath: str) -> None:
        raise NotImplementedError("子类必须实现 export 方法")


class JSONExporter(BaseExporter):
    """JSON格式导出"""
    
    def export(self, articles: List[dict], filepath: str) -> None:
        output_data = {
            "metadata": {
                "exported_at": datetime.now().isoformat(),
                "article_count": len(articles),
            },
            "articles": articles,
        }
        
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(output_data, f, ensure_ascii=False, indent=2)


class CSVExporter(BaseExporter):
    """CSV格式导出(支持Excel打开)"""
    
    def export(self, articles: List[dict], filepath: str) -> None:
        fieldnames = ["index", "title", "url", "date", "views"]
        
        with open(filepath, 'w', encoding='utf-8-sig', newline='') as f:
            # 使用 utf-8-sig 编码,Excel可以直接打开
            writer = csv.DictWriter(f, fieldnames=fieldnames)
            writer.writeheader()
            
            for idx, article in enumerate(articles, 1):
                writer.writerow({
                    "index": idx,
                    "title": article.get("title", ""),
                    "url": article.get("url", ""),
                    "date": article.get("date", ""),
                    "views": article.get("views", ""),
                })


class TXTExporter(BaseExporter):
    """文本格式导出------清晰易读"""
    
    def export(self, articles: List[dict], filepath: str) -> None:
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write("=" * 80 + "\n")
            f.write("CSDN 博客文章列表\n")
            f.write(f"导出时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"文章总数: {len(articles)}\n")
            f.write("=" * 80 + "\n\n")
            
            for idx, article in enumerate(articles, 1):
                f.write(f"[{idx}] {article.get('title', '')}\n")
                f.write(f"    URL: {article.get('url', '')}\n")
                f.write(f"    日期: {article.get('date', '')}\n")
                f.write(f"    阅读量: {article.get('views', '')}\n\n")

5.3 工厂类实现

python 复制代码
class ExporterFactory:
    """导出器工厂------统一创建导出器实例"""
    
    _exporters = {
        "json": JSONExporter,
        "csv": CSVExporter,
        "txt": TXTExporter,
    }
    
    @classmethod
    def get_exporter(cls, format: str) -> BaseExporter:
        """获取指定格式的导出器"""
        format = format.lower()
        if format not in cls._exporters:
            raise ValueError(f"不支持的格式: {format}")
        return cls._exporters[format]()
    
    @classmethod
    def list_formats(cls) -> List[str]:
        """列出所有支持的格式"""
        return list(cls._exporters.keys())

5.4 各格式输出效果对比

JSON格式(适合程序处理):

json 复制代码
{
  "metadata": {
    "exported_at": "2026-05-06T12:34:56",
    "article_count": 77
  },
  "articles": [
    {
      "title": "文章标题",
      "url": "https://blog.csdn.net/...",
      "date": "2026-05-01",
      "views": "阅读量: 1000"
    }
  ]
}

CSV格式(适合Excel分析):

csv 复制代码
index,title,url,date,views
1,文章标题,https://blog.csdn.net/...,2026-05-01,阅读量: 1000
2,第二篇文章,https://blog.csdn.net/...,2026-04-28,阅读量: 500

TXT格式(适合直接阅读):

复制代码
================================================================================
CSDN 博客文章列表
导出时间: 2026-05-06 12:34:56
文章总数: 77
================================================================================

[1] 文章标题
    URL: https://blog.csdn.net/...
    日期: 2026-05-01
    阅读量: 阅读量: 1000

六、整合运行与测试

6.1 便捷的保存方法

为了让调用更加便捷,我们在爬虫类中添加了简洁的保存方法:

python 复制代码
def save(self, articles, format="txt", filename=None):
    """统一保存接口------支持多种格式"""
    if filename is None:
        filename = f"csdn_articles_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{format}"
    
    filepath = f"{self.config.output_dir}/{filename}"
    exporter = ExporterFactory.get_exporter(format)
    exporter.export(articles, filepath)
    return filepath

def save_to_csv(self, articles, filename=None):
    return self.save(articles, "csv", filename)

def save_to_json(self, articles, filename=None):
    return self.save(articles, "json", filename)

def save_to_txt(self, articles, filename=None):
    return self.save(articles, "txt", filename)

6.2 完整使用示例

python 复制代码
from src import CSDNBlogScraper, Config

# 第一步:创建配置
config = Config(
    blog_url="https://blog.csdn.net/qq_46987323",
    min_delay=1.5,    # 请求间隔1.5-3秒
    max_delay=3.0,
)

# 第二步:创建爬虫
scraper = CSDNBlogScraper(config)

# 第三步:爬取所有文章
articles = scraper.scrape_all_articles()
print(f"共爬取 {len(articles)} 篇文章")

# 第四步:保存为多种格式
scraper.save_to_csv(articles)
scraper.save_to_json(articles)
scraper.save_to_txt(articles)

6.3 运行效果截图

复制代码
🚀 开始爬取文章...
📄 正在爬取第1页: https://blog.csdn.net/qq_46987323/article/list/1
✅ 第1页: +20 篇文章
📊 总计: 20 篇文章
⏱️ 等待 2.3 秒...
📄 正在爬取第2页: https://blog.csdn.net/qq_46987323/article/list/2
✅ 第2页: +20 篇文章
📊 总计: 40 篇文章
⏱️ 等待 1.8 秒...
...(继续爬取)...
🎉 爬取完成!共 77 篇文章

✅ 成功导出77篇文章到 outputs/csdn_articles_20260506_123456.csv
✅ 成功导出77篇文章到 outputs/csdn_articles_20260506_123456.json
✅ 成功导出77篇文章到 outputs/csdn_articles_20260506_123456.txt

七、总结与最佳实践

7.1 核心知识点回顾

知识点 应用场景 关键代码
@dataclass 配置管理 @dataclass class Config
Session复用 HTTP请求优化 requests.Session()
随机延迟 反爬虫规避 random.uniform(min, max)
重试机制 网络容错 _safe_request() 递归重试
多重选择器 页面结构适配 多个CSS选择器依次尝试
工厂模式 格式扩展 ExporterFactory.get_exporter()
日志轮转 日志管理 RotatingFileHandler

7.2 最佳实践建议

1. 请求频率控制

python 复制代码
# ✅ 好的做法:随机延迟,模拟人类行为
delay = random.uniform(1.5, 3.0)
time.sleep(delay)

# ❌ 不好的做法:固定间隔,容易被识别
time.sleep(2)

2. User-Agent 轮换

python 复制代码
# ✅ 好的做法:使用User-Agent池
user_agent = random.choice(user_agent_pool)

# ❌ 不好的做法:固定UA
user_agent = "Chrome/124..."

3. 错误处理

python 复制代码
# ✅ 好的做法:自动重试
response = self._safe_request(url, retry=0, max_retries=3)

# ❌ 不好的做法:一次失败就放弃
response = requests.get(url)  # 没有try-except

7.3 常见问题解答

Q1:为什么爬取不到文章?

  • 检查URL格式是否正确(必须是 https://blog.csdn.net/用户名
  • 检查网络连接是否正常
  • 查看日志文件中的具体错误信息

Q2:如何避免被封IP?

  • 设置合理的请求延迟(建议1.5-3秒)
  • 使用User-Agent轮换
  • 不要爬取频率过高

Q3:添加新的导出格式要怎么做?

  • 继承 BaseExporter
  • 实现 export 方法
  • ExporterFactory._exporters 中注册

7.4 扩展方向

  • GUI可视化界面:使用Tkinter开发图形界面
  • 文章内容爬取:不仅爬取列表,还能爬取全文
  • 多线程优化:使用并发提升爬取效率
  • 代理IP池:配合代理提升稳定性
  • 数据库存储:支持MongoDB、MySQL等

📦 项目源码

本文所有代码源自开源项目:csdn-blog-scraper

开发者艺杯羹

如果本文对你有帮助,欢迎 Star ⭐ 支持!

享受编码! 🚀

相关推荐
Mr.朱鹏1 小时前
3.LangChain零基础速通-Prompt提示词模版和模型调用方法
人工智能·python·深度学习·langchain·llm·prompt·virtualenv
码农小韩1 小时前
QT学习记录(三)——C++学习基础(三)
开发语言·c++·qt·学习·算法·嵌入式软件
buhuizhiyuci1 小时前
【QT-百日筑基篇】找寻安静的落脚处,选择合适的功法进行修炼-QT深度了解对象树的特性
开发语言·qt
wjs20241 小时前
jQuery Mobile 触摸事件详解
开发语言
iAm_Ike1 小时前
JavaScript中模块化在游戏引擎开发中的资源调度作用
jvm·数据库·python
kyriewen111 小时前
你的前端滤镜慢得像PPT?用Rust+WebAssembly,一秒处理4K图
开发语言·前端·javascript·设计模式·rust·ecmascript·powerpoint
小杍随笔1 小时前
【Tauri 2 + Rust 配置 WebView2 缓存数据存储到安装目录】
开发语言·后端·rust·tauri
m0_702036531 小时前
Layui表单input框怎么设置只读或禁用
jvm·数据库·python
weixin_459753941 小时前
php怎么调用快手开放平台_php如何接入快手授权登录流程
jvm·数据库·python