CSDN博客文章批量导出与文件名规范化实战

news/2025/10/23 10:19:31/文章来源:https://www.cnblogs.com/ethnicity/p/19159767

在日常技术积累中,将CSDN博客的技术文章批量导出并规范化管理,能有效构建个人知识库。本文将详细介绍如何通过Python脚本实现CSDN博客文章批量抓取与Markdown格式转换,再结合Shell命令完成文件名规范化,全程提供可直接复用的代码与操作步骤。

一、实战目标

本教程以CSDN用户(示例用户名:dashen_52315708)为例,实现两大核心需求:

  1. 自动化批量导出:通过Python脚本突破CSDN反爬机制,抓取用户所有技术文章,并转换为轻量易读的Markdown格式本地保存。
  2. 文件名规范化:使用Shell命令清洗导出文件的名称,去除冗余字符(如“原创”“多余下划线”)、统一命名格式,提升文件管理效率。

二、环境准备:依赖工具安装

运行Python导出脚本前,需先在Linux系统中配置基础环境,安装Python运行时、Chrome浏览器(应对复杂反爬场景)及相关依赖库。以下命令可直接复制执行,关键步骤已标注说明。

操作步骤 Bash命令 命令说明
1. 安装Python包管理工具 apt install python3-pip -y 安装pip3,用于后续Python第三方库的安装
2. 安装文件传输/解压工具 apt -y install lrzsz
apt -y install zip unzip
- lrzsz:提供rz/sz命令,支持终端上传/下载文件
- zip/unzip:处理压缩包,便于脚本或依赖的传输
3. 安装批量重命名工具 apt install rename 基于Perl的强大工具,用于后续文件名批量清洗
4. 配置Chrome安装源(清华镜像) `wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub sudo apt-key add -<br>echo "deb [arch=amd64] https://mirrors.tuna.tsinghua.edu.cn/google-chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list`
5. 安装Chrome及依赖修复 apt update
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
dpkg -i google-chrome-stable_current_amd64.deb
apt --fix-broken install -y
- apt update:同步最新软件包索引
- 下载Chrome稳定版deb包并安装,--fix-broken自动修复依赖缺失问题
6. 安装Chrome无头模式依赖库 apt install -y wget unzip libxss1 libappindicator1 libindicator7 fonts-liberation libasound2 libatk-bridge2.0-0 libgtk-3-0 libdrm-common 确保Chrome在“无头模式”(无图形界面)下正常运行,避免因缺少图形/音频库导致启动失败
7. 可选:修复依赖残留 apt --fix-broken install -y 若前序步骤出现安装中断,执行此命令自动修复依赖错误

注意事项

  1. 核心脚本export_csdn_cf.py基于cloudscraper库,不直接依赖Chrome;但后续若需用selenium/playwright应对Turnstile验证码等复杂反爬,Chrome是必需组件。
  2. 清华镜像仅用于加速国内网络环境,若在境外服务器操作,可替换为Google官方源(deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main)。

三、Python环境配置:安装核心依赖库

为避免Python库版本冲突,建议先创建虚拟环境,再安装脚本所需的第三方库。

3.1 配置pip镜像(国内加速)

首先修改pip配置,使用阿里云镜像源,解决PyPI官方源访问慢的问题:

# 编辑pip配置文件(若文件不存在则自动创建)
cat ~/.pip/pip.conf 
# 写入以下内容
[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
trusted-host = pypi.aliyun.com
timeout = 120

3.2 创建并激活Python虚拟环境

# 安装虚拟环境工具(若未安装)
apt install python3.10-venv -y
# 创建名为csdn-env的虚拟环境
python3 -m venv csdn-env
# 激活虚拟环境(Linux/Mac系统)
source csdn-env/bin/activate
# Windows系统激活命令(此处仅作补充,教程以Linux为主)
# csdn-env\Scripts\activate

激活后终端前缀会显示(csdn-env),表示当前处于虚拟环境中。

3.3 安装核心依赖库

pip3 install cloudscraper requests beautifulsoup4 markdownify lxml

依赖库功能说明

依赖库名称 核心作用
cloudscraper 突破CSDN的Cloudflare“5秒盾”反爬,模拟浏览器行为处理JavaScript挑战,是本方案的核心
requests 提供基础HTTP请求能力,cloudscraper内部依赖此库实现网络请求
beautifulsoup4(简称bs4 HTML/XML解析库,用于从抓取的网页源码中定位并提取文章标题、链接、正文等关键信息
markdownify 将HTML格式的文章正文转换为Markdown格式,便于后续阅读和知识库管理
lxml 作为beautifulsoup4的解析后端,相比默认的html.parser,解析速度更快、容错性更强

提示:若未配置pip镜像,可临时通过-i参数指定镜像源安装,例如:

pip3 install -i https://mirrors.aliyun.com/pypi/simple/ cloudscraper requests beautifulsoup4 markdownify lxml

四、核心实现:Python导出脚本(export_csdn_cf.py

以下是完整的Python脚本,包含文章列表抓取、正文解析、Markdown转换及文件名初步清洗功能,关键代码已添加详细注释,便于理解和修改。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CSDN 文章导出脚本 – 基于cloudscraper突破Cloudflare免费5秒盾
适用场景:仅对抗CF免费版反爬;若遇到CF Pro/Turnstile验证码,需升级商业反爬方案
"""
import os
import time
import re
import cloudscraper
from bs4 import BeautifulSoup
import markdownify# -------------------------- 配置参数(需根据自身需求修改) --------------------------
BLOG_USERNAME = "dashen_52315708"  # 替换为你的CSDN用户名(如ethnicitybeta)
OUTPUT_DIR = f"csdn_posts_{BLOG_USERNAME}"  # 输出目录名,格式:csdn_posts_用户名
MAX_PAGE = 10000  # 最大抓取页数(建议先设为10测试,确认正常后再调大)
# ----------------------------------------------------------------------------------# 创建输出目录(若不存在则自动创建)
os.makedirs(OUTPUT_DIR, exist_ok=True)def create_scraper_instance():"""创建cloudscraper实例,模拟Chrome浏览器突破CF反爬"""scraper = cloudscraper.create_scraper(browser={"browser": "chrome",  # 模拟浏览器类型"platform": "linux",  # 模拟操作系统"mobile": False  # 非移动设备})return scraperdef list_articles(scraper):"""抓取CSDN用户的文章列表(含标题和链接):param scraper: cloudscraper实例:return: 文章列表,格式:[(标题1, 链接1), (标题2, 链接2), ...]"""articles = []print(f"[INFO] 开始抓取用户 '{BLOG_USERNAME}' 的文章列表...")for page in range(1, MAX_PAGE + 1):# 构造第page页的文章列表URL(CSDN列表页URL格式固定)list_url = f"https://blog.csdn.net/{BLOG_USERNAME}/article/list/{page}"print(f"[+] 正在拉取列表页 {page}:{list_url}")try:# 发送请求获取列表页源码(超时时间15秒,避免卡壳)response = scraper.get(list_url, timeout=15)# 若状态码非200,说明反爬机制升级,停止抓取if response.status_code != 200:print(f"[-] 抓取失败!CF反爬可能已升级,返回状态码:{response.status_code}")break# 使用lxml解析HTML源码soup = BeautifulSoup(response.text, "lxml")# 定位文章链接:CSDN文章标题链接的特征是href含"article/details",且父标签为h4article_links = soup.select("h4 a[href*='article/details']")# 若当前页无文章链接,说明已到最后一页,停止抓取if not article_links:print("[=] 本页无文章,已到达列表末尾")break# 提取文章标题和链接,添加到列表中for link in article_links:article_title = link.text.strip()  # 去除标题前后空白article_url = link["href"]          # 获取文章链接articles.append((article_title, article_url))# 每次抓取后休眠2秒,避免请求频率过高触发反爬(可根据实际情况调整)time.sleep(2)except Exception as e:print(f"[-] 拉取列表页 {page} 时出错:{str(e)}")continueprint(f"[#] 列表抓取完成,共发现 {len(articles)} 篇文章")return articlesdef sanitize_filename(original_name):"""文件名清洗函数:去除非法字符、统一格式,避免保存时出错功能等效于后续的Shell命令链,提前在Python中处理,减少后续操作:param original_name: 原始文章标题:return: 清洗后的合法文件名(不含后缀)"""# Step 1:将空格、括号、逗号替换为下划线,合并连续多个下划线cleaned = re.sub(r'[ \(\),]+', '_', original_name)cleaned = re.sub(r'_+', '_', cleaned)# Step 2:去除文件名首尾的下划线cleaned = cleaned.strip('_')# Step 3:移除开头的“原创”字样(含可能的换行符)cleaned = re.sub(r'^原创\s*', '', cleaned, flags=re.MULTILINE)# Step 4:删除所有换行符(避免文件名含特殊控制字符)cleaned = cleaned.replace('\n', '')# Step 5:清理首尾空白/下划线,将中间多个空格/下划线合并为单个空格cleaned = re.sub(r'^[ _]+|[ _]+$', '', cleaned)  # 去首尾cleaned = re.sub(r'[ _]{2,}', ' ', cleaned)      # 合并中间# Step 6:移除&和+符号(避免部分系统识别异常)cleaned = cleaned.replace('&', '').replace('+', '')# Step 7:保留合法字符(字母、数字、中文、点、横线、空格,兼容Unicode)cleaned = re.sub(r'[^\w .-]', '', cleaned, flags=re.UNICODE)# Step 8:再次清理因删除特殊字符产生的多余符号cleaned = re.sub(r'^[ _]+|[ _]+$', '', cleaned)cleaned = re.sub(r'_+', '_', cleaned).strip('_')# 防止清洗后文件名为空,默认命名为"untitled"return cleaned or "untitled"def scrape_article_content(scraper, title, url):"""抓取单篇文章正文,转换为Markdown并保存到本地:param scraper: cloudscraper实例:param title: 文章标题:param url: 文章链接"""print(f"[*] 正在处理文章:{title}")try:# 发送请求获取文章详情页源码response = scraper.get(url, timeout=15)soup = BeautifulSoup(response.text, "lxml")# 定位文章正文:CSDN正文可能在3个不同标签中,依次尝试匹配content_tag = soup.select_one("#content_views, #article_content, .htmledit_views")if not content_tag:print("  [-] 未找到文章正文,跳过该文章")return# 将HTML正文转换为Markdown格式(heading_style="ATX"表示用#表示标题)markdown_content = markdownify.markdownify(str(content_tag), heading_style="ATX")# 第一步:初步过滤非法字符(避免文件名含<>:\/|?*等系统禁止字符)temp_filename = re.sub(r'[<>:/\\|?*"\'\n]+', '_', title)[:100]  # 限制长度,避免文件名过长# 第二步:调用清洗函数,生成最终文件名(含.md后缀)cleaned_filename = f"{sanitize_filename(temp_filename)}.md"# 构造文件保存路径save_path = os.path.join(OUTPUT_DIR, cleaned_filename)# 处理文件名重复问题:若文件已存在,在文件名后加计数器(如"xxx_1.md")counter = 1original_cleaned_name = cleaned_filenamewhile os.path.exists(save_path):name_without_ext = os.path.splitext(original_cleaned_name)[0]cleaned_filename = f"{name_without_ext}_{counter}.md"save_path = os.path.join(OUTPUT_DIR, cleaned_filename)counter += 1# 保存Markdown文件(使用UTF-8编码,避免中文乱码)with open(save_path, "w", encoding="utf-8") as f:# 开头添加文章标题(Markdown一级标题格式)f.write(f"# {title}\n\n{markdown_content}")print(f"  [✓] 保存成功:{cleaned_filename}")except Exception as e:print(f"  [-] 处理失败:{str(e)}")def main():"""主函数:串联列表抓取和正文抓取流程"""# 1. 创建cloudscraper实例scraper = create_scraper_instance()# 2. 抓取所有文章的标题和链接articles = list_articles(scraper)# 3. 遍历文章列表,抓取正文并保存if articles:print(f"[INFO] 开始抓取 {len(articles)} 篇文章的正文...")for title, url in articles:scrape_article_content(scraper, title, url)# 休眠1秒,降低请求频率(可根据反爬严格程度调整)time.sleep(1)print(f"[OK] 全部完成!文章已保存至目录:./{OUTPUT_DIR}")else:print("[WARN] 未抓取到任何文章,可能是用户名错误或反爬拦截")# 脚本入口:仅当直接运行脚本时执行主函数
if __name__ == "__main__":main()

五、执行脚本与结果验证

5.1 运行Python脚本

确保虚拟环境已激活(终端显示(csdn-env)),在脚本所在目录执行以下命令:

python3 export_csdn_cf.py

5.2 执行日志示例(成功场景)

脚本运行时会实时输出进度,以下是一次成功执行的日志片段:

(csdn-env) root@wanyan:/opt/csdn# python3 export_csdn_cf.py
[INFO] 开始抓取用户 'ethnicitybeta' 的文章列表...
[+] 正在拉取列表页 1:https://blog.csdn.net/dashen_52315708/article/list/1
[+] 正在拉取列表页 2:https://blog.csdn.net/dashen_52315708/article/list/2
[+] 正在拉取列表页 3:https://blog.csdn.net/dashen_52315708/article/list/3
[+] 正在拉取列表页 4:https://blog.csdn.net/dashen_52315708/article/list/4
[+] 正在拉取列表页 5:https://blog.csdn.net/dashen_52315708/article/list/5
[+] 正在拉取列表页 6:https://blog.csdn.net/dashen_52315708/article/list/6
[+] 正在拉取列表页 7:https://blog.csdn.net/dashen_52315708/article/list/7
[+] 正在拉取列表页 8:https://blog.csdn.net/dashen_52315708/article/list/8
[=] 本页无文章,已到达列表末尾
[#] 列表抓取完成,共发现 320 篇文章
[INFO] 开始抓取 320 篇文章的正文...
[*] 正在处理文章:原创 PHP项目Kubernetes部署与Jenkins CI/CD流水线全栈实践指南[✓] 保存成功:PHP项目Kubernetes部署与Jenkins CI/CD流水线全栈实践指南.md
[*] 正在处理文章:原创 docker-compose部署yapi[✓] 保存成功:docker-compose部署yapi.md
[*] 正在处理文章:原创 PHP 项目容器化与自动化部署实践:从 Docker 改造到 Jenkins Pipeline 滚动更新[✓] 保存成功:PHP 项目容器化与自动化部署实践:从 Docker 改造到 Jenkins Pipeline 滚动更新.md
... (后续文章处理日志)
[OK] 全部完成!文章已保存至目录:./csdn_posts_ethnicitybeta

5.3 关键结果验证

  1. 目录验证:执行ls ./csdn_posts_dashen_52315708,应看到所有文章的.md文件。
  2. 内容验证:用cat或文本编辑器打开任意.md文件,确认正文完整、无乱码(如cat ./csdn_posts_ethnicitybeta/docker-compose部署yapi.md)。
  3. 文件名验证:初步清洗后的文件名应已去除“原创”“特殊符号”,如原始标题“原创 PHP项目...”变为“PHP项目...”。

六、可选操作:Shell命令二次规范化文件名

虽然Python脚本已完成基础文件名清洗,但若需更精细的格式统一(如合并多余空格、移除特定符号),可执行以下Shell命令。此步骤为可选,仅当对文件名格式有更高要求时使用。

6.1 进入输出目录

cd csdn_posts_dashen_52315708

6.2 执行批量清洗命令

# 1. 替换空格/括号/逗号为下划线,合并连续下划线,去除首尾下划线
rename -v 's/[ \(\),]+/_/g; s/__+/_/g; s/^_+//; s/_$//' *.md# 2. 移除文件名开头的“原创”及后续空白(含换行符)
rename -v 's/^原创\s*\n?\s*//' *.md# 3. 删除文件名中残留的换行符
rename -v 's/\n//g' *.md# 4. 终极规范化:精细调整格式(用sed实现复杂逻辑)
ls *.md | while read filename; do# 新文件名处理逻辑:# - 去首尾空格/下划线# - 多个空格/下划线合并为单个空格# - 移除&和+符号# - 仅保留字母/数字/中文/空格/点/横线(兼容Unicode)new_filename=$(echo "$filename" | \sed -E 's/^[ _]+|[ _]+$//g; ' \'s/[ _]{2,}/ /g; ' \'s/[&+]//g; ' \'s/[^[:alnum:] _.\-\p{L}\p{N}]//g')# 若新旧文件名不同,执行重命名(避免无意义操作)[ "$filename" != "$new_filename" ] && mv "$filename" "$new_filename"
done

6.3 清洗效果示例

清洗前文件名 清洗后文件名
原创_PHP_项目容器化与自动化部署实践:从_Docker_改造到_Jenkins_Pipeline_滚动更新.md PHP 项目容器化与自动化部署实践 从 Docker 改造到 Jenkins Pipeline 滚动更新.md
原创_docker-compose部署yapi.md docker-compose部署yapi.md
Python&+大数据_实战.md Python 大数据 实战.md

七、总结与扩展建议

7.1 实战总结

本教程通过“Python脚本抓取+Shell命令清洗”的组合,实现了CSDN博客文章的自动化批量导出与规范化管理,核心优势:

  1. 反爬能力:基于cloudscraper突破Cloudflare免费版“5秒盾”,无需手动处理验证码。
  2. 格式兼容:转换为Markdown格式,可在Obsidian、Notion、VS Code等工具中无缝使用。
  3. 自动化程度高:从列表抓取到正文保存再到文件名清洗,全程无需手动干预。

7.2 扩展建议

  1. 应对复杂反爬:若遇到CF Pro/Turnstile验证码,可替换为playwright库(自动控制浏览器,支持验证码手动输入或对接打码平台)。
  2. 增量抓取:在脚本中添加“已抓取文章URL记录”(如保存到record.txt),下次运行时跳过已抓取文章,避免重复操作。
  3. 多平台适配:修改脚本中的URL格式和解析规则,可扩展到掘金、知乎专栏等其他技术博客平台的文章导出。

通过以上步骤,你可以轻松将CSDN博客的技术积累转化为本地可控的知识库,为后续学习和分享提供便利。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/944020.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025 年加工中心厂家最新推荐榜,技术实力与市场口碑深度解析,助力企业精准选型160/1260/四轴/五轴/数控加工中心厂家推荐

引言 当前加工中心市场竞争激烈,厂商数量繁杂且产品质量、技术水平差异显著,汽车摩托车、工程机械、军工等行业企业在采购时,常面临不知如何筛选符合自身需求设备的困境。部分厂商技术研发薄弱,难以满足个性化加工…

0261-CLAP-使用注解

环境Time 2022-12-03 WSL-Ubuntu 22.04 CLAP 4.0.29前言 说明 参考:https://docs.rs/clap/latest/clap/index.html 目标 使用注解来提供命令行的参数。 Cargo.toml [package] edition = "2021" name = &quo…

详细介绍:微算法科技(NASDAQ MLGO)使用基于深度学习的物理信息神经网络(PINN),增强区块链IoT网络交易中的入侵检测

详细介绍:微算法科技(NASDAQ MLGO)使用基于深度学习的物理信息神经网络(PINN),增强区块链IoT网络交易中的入侵检测2025-10-23 10:15 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important;…

2025 年新材料 / 机械 / 医药 / 化工 / 无效专利律师推荐,冯燕青团队:双证资质与全产业链知识产权服务解析

行业背景 在新材料、机械、医药、化工等技术密集型行业,知识产权已成为企业创新发展的核心保障。随着技术迭代加速与市场竞争加剧,专利确权纠纷、侵权诉讼、技术秘密保护等问题日益凸显,尤其医药领域专利纠纷因技术…

0257-CLAP-校验参数范围

环境Time 2022-12-03 WSL-Ubuntu 22.04 CLAP 4.0.29前言 说明 参考:https://docs.rs/clap/latest/clap/index.html 目标 校验参数值的可选范围。 Cargo.toml [package] edition = "2021" name = "game…

dls

dlsdef create_coefficient_csv_with_ratios(base_models, feature_cols, output_path, df, base_recipe_to_group, base_high_overlap ):"""生成包含系数、分组统计、共现组合和特征依赖的CSV新增参数…

0256-CLAP-参数可选值

环境Time 2022-12-03 WSL-Ubuntu 22.04 CLAP 4.0.29前言 说明 参考:https://docs.rs/clap/latest/clap/index.html 目标 限制参数可以选择的值有哪些。 Cargo.toml [package] edition = "2021" name = &quo…

2025年10月空气净化器产品推荐:全价位段性能榜横向对比

入秋后,北方陆续进入供暖倒计时,南方也迎来装修季叠加雾霾反复,不少家庭把“换一台真正管用的空气净化器”提上日程。京东商智数据显示,2025年9月空气净化器搜索量环比上升42%,其中“甲醛CADR≥700”“母婴认证”…

NEBS / GR-63-CORE

NEBS / GR-63-CORENEBSA report https://cannontech.co.uk/wp-content/uploads/2019/06/Seismic-Cabinet-Download.pdfHOFFMAN-WPCS-Earthquake Environments-UKEN-2109.pdf https://www.nvent.com/sites/default/fi…

【验证码逆向专栏】某 SDN 验证码逆向分析

声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许可禁…

高压电缆生产厂家口碑榜:基于技术实力、客户服务及市场反馈的专业评估

高压电缆作为电力传输的核心组件,其质量直接关系到电网安全、能效和长期稳定性。随着基础设施建设和新能源项目的快速发展,市场对高压电缆的需求持续增长,厂家之间的竞争也日益激烈。本文基于行业调研、客户反馈及产…

oracle long转日期 和日期转long时间戳

oracle long转日期 和日期转long时间戳SELECT TO_DATE(1970-01-01 08:00:00, YYYY-MM-DD HH24:mi:ss) + (TO_NUMBER(1761015597082) / (24 * 60 * 60 * 1000)) AS converted_date FROM dual; --date转long示例 select…

国产DevSecOps工具崛起:安全与效率的双重革命

国产DevSecOps工具崛起:安全与效率的双重革命 在数字化转型浪潮席卷各行各业的今天,软件开发的安全性问题日益凸显。随着《网络安全法》《数据安全法》等法规的密集出台,中国企业正面临前所未有的安全合规压力。这一…

2025 年最新外墙涂料厂家推荐排行榜:聚焦优质产品与实力企业,助力建筑涂装高效选品

引言 当前建筑涂料市场中,外墙涂料作为建筑外观装饰与防护的核心材料,需求持续攀升,但行业乱象也让选购者倍感困扰。部分产品耐候性差、易褪色开裂,环保不达标含高甲醛与 VOC,且市场信息不对称,缺乏权威指引,导…

2025年10月长白山亲子酒店排名榜:亲测十家安心入住

长假将至,不少父母把“带娃看雪”提上日程,长白山因粉雪早至、空气纯净、动植物丰富,成为东北亲子游首选。飞猪《2025冰雪游趋势报告》显示,长白山区域亲子酒店预订量同比增47%,其中带“儿童俱乐部、自然教育”标…

解决windows下启动tomcat乱码问题

解决windows下启动tomcat乱码问题如上图所示 解决方法: 1.打开Tomcat安装目录下的bin/catalina.bat文件,找到set JAVA_OPTS=这一行,注释掉,添加下面的配置 set JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=GBK -Dsun.j…

类组件(Classcomponent)和函数式组件 (Functionalcomponent)之间有何不同?

在 React 中,类组件 (Class Component) 和 函数式组件 (Functional Component) 都是用来定义 UI 的两种方式,但它们在语法、生命周期管理、状态处理等方面有显著区别。 定义方式类型 定义示例类组件 jsx<br>cl…

2025 年仿石漆厂家最新推荐品牌排行榜:聚焦真石漆水包砂等优质产品,助力采购方精准选品

引言 近年来,仿石漆凭借美观、耐用、环保等优势,在建筑装饰领域应用愈发广泛,但市场品牌繁杂、产品质量参差不齐的问题也愈发凸显。部分小品牌产品存在仿石效果失真、耐候性差、环保不达标等问题,不仅影响建筑外观…

D. Secret Passwords

题意:给定n个字符串s,如果两个字符串有任意的相同的字符,那么字符串是equivalent的,并且如果有ab, bc, cd。那么cd和ab也是equivalent的。现在要破解n个字符串的密码,并且n个字符串中只有一个是正确的密码,问最少…

Java EE初阶启程记02---认识线程 - 实践

Java EE初阶启程记02---认识线程 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mo…