python脚本批量导出ZEEKLOG里的文章

python脚本批量导出ZEEKLOG里的文章

一 导出全部已发布文章

首先,需要在本地安装3.8版本以上的python,安装python步骤

检查是否安装成功

pip3 --version 

安装后执行

pip3 install requests beautifulsoup4 markdownify 

新建脚本,脚本名字随意,这里是:ZEEKLOG_downloader.py

脚本内容如下:

# -*- coding: utf-8 -*-import os import re import requests import time from bs4 import BeautifulSoup from markdownify import markdownify as md from urllib.parse import urlparse, unquote import hashlib from pathlib import Path # ================== 配置区 ================== ZEEKLOG_USERNAME ="qq_33417321"# ←←← 修改为你想下载的用户名 SAVE_DIR = Path("ZEEKLOG_articles")# 文章保存根目录(自动跨平台) HEADERS ={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36","Referer":"https://blog.ZEEKLOG.net/"}defsanitize_filename(name:str)->str:"""清理文件名,移除 Windows 非法字符和‘原创’字样""" name = name.replace("原创","").strip()# 移除 Windows 非法字符 name = re.sub(r'[\\/*?:"<>|\r\n]',"_", name)return name or"untitled"defget_article_list(username):"""获取博主文章列表(标题和URL)""" url =f"https://blog.ZEEKLOG.net/{username}/article/list" articles =[] page =1whileTrue: response = requests.get(f"{url}/{page}", headers=HEADERS) soup = BeautifulSoup(response.text,'html.parser') items = soup.select(".article-list .article-item-box")ifnot items:breakfor item in items: title_elem = item.select_one("h4 a")ifnot title_elem:continue title = title_elem.text.strip() link = title_elem["href"] articles.append({"title": title,"url": link}) page +=1 time.sleep(1)return articles defdownload_image(img_url, save_path: Path):"""下载单张图片到本地"""try: img_headers = HEADERS.copy() img_headers["Accept"]="image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" response = requests.get(img_url, headers=img_headers, stream=True, timeout=30)if response.status_code ==200: save_path.parent.mkdir(parents=True, exist_ok=True)withopen(save_path,'wb')as f:for chunk in response.iter_content(chunk_size=8192):if chunk: f.write(chunk)returnTrueelse:print(f"图片下载失败(状态码:{response.status_code}):{img_url}")returnFalseexcept Exception as e:print(f"图片下载异常:{img_url},错误:{str(e)}")returnFalsedefget_image_extension(img_url):"""从URL中获取图片扩展名""" parsed_url = urlparse(img_url) path = parsed_url.path.lower() extensions =['.jpg','.jpeg','.png','.gif','.webp','.bmp','.svg']for ext in extensions:if ext in path:return ext return'.jpg'defprocess_images_in_content(content, article_title):"""处理内容中的图片,下载并替换为本地路径""" soup = BeautifulSoup(content,'html.parser') img_tags = soup.find_all('img')ifnot img_tags:return content # 清理文章标题用于路径 safe_title = sanitize_filename(article_title) global_image_dir = SAVE_DIR /"images" article_image_dir = global_image_dir / safe_title for img in img_tags: img_url = img.get('src','')ifnot img_url:continue# 处理协议相对路径if img_url.startswith('//'): img_url ='https:'+ img_url elifnot img_url.startswith(('http://','https://')):continue# 跳过无法处理的相对路径try: img_hash = hashlib.md5(img_url.encode()).hexdigest()[:8] img_ext = get_image_extension(img_url) img_filename =f"{img_hash}{img_ext}" local_img_path = article_image_dir / img_filename # Markdown 中使用正斜杠(/),兼容所有平台 md_img_path =f"./images/{safe_title}/{img_filename}"ifnot local_img_path.exists():print(f" 下载图片:{img_filename}")if download_image(img_url, local_img_path): img['src']= md_img_path else:print(f" 图片下载失败,保留原链接:{img_url}")else: img['src']= md_img_path except Exception as e:print(f" 处理图片时出错:{img_url},错误:{str(e)}")continuereturnstr(soup)defdownload_article(url, article_title):"""下载单篇文章,处理图片后转为Markdown"""try: response = requests.get(url, headers=HEADERS, timeout=30) soup = BeautifulSoup(response.text,'html.parser') content = soup.select_one("article")ifnot content:print(f" 未找到文章内容")returnNone processed_content = process_images_in_content(str(content), article_title) markdown_content = md(processed_content)return markdown_content except Exception as e:print(f" 下载文章时出错:{str(e)}")returnNonedefsave_to_markdown(title, content, save_dir: Path):"""保存Markdown文件""" save_dir.mkdir(parents=True, exist_ok=True) safe_title = sanitize_filename(title) filename = save_dir /f"{safe_title}.md"withopen(filename,"w", encoding="utf-8")as f: f.write(f"# {title}\n\n") f.write(content)print(f" 已保存:{filename}")return filename if __name__ =="__main__":print("开始获取文章列表...") articles = get_article_list(ZEEKLOG_USERNAME)print(f"找到 {len(articles)} 篇文章") success_count =0 fail_count =0for i, article inenumerate(articles,1): title = article["title"] url = article["url"]print(f"\n[{i}/{len(articles)}] 处理文章:{title}") content = download_article(url, title)if content: save_to_markdown(title, content, SAVE_DIR) success_count +=1else:print(f" 文章下载失败:{title}") fail_count +=1 time.sleep(2)print(f"\n处理完成!成功:{success_count}篇,失败:{fail_count}篇")print(f"文章保存在:{SAVE_DIR.resolve()}")print("图片保存在:./images/ 目录下,Markdown文件可离线查看")

其中,脚本里ZEEKLOG_USERNAME的值,改为你要获取的ZEEKLOG的用户名

获取用户名:点击作者头像后,链接里的这个值就是用户名(红框里的内容)

在这里插入图片描述

执行脚本

python ZEEKLOG_downloader.py 

执行日志入下:

在这里插入图片描述

由于要下载ZEEKLOG文章里的图片,所以很慢,静静等待即可。下载过程中,会在脚本所在目录生成一个ZEEKLOG_articles文件夹,里边是md文件以及存md里的图片的文件夹。

在这里插入图片描述

二 导出指定日期后的文章

上边的脚本,一次性导出了所有已发布的文章,但是有时候我们的文章太多,每次备份不需要全部导出,只导出指定时间以后的文章,那么使用如下脚本即可,如脚本名为new.py,内容如下

# -*- coding: utf-8 -*-import os import re import requests import time from bs4 import BeautifulSoup from markdownify import markdownify as md from urllib.parse import urlparse, unquote import hashlib from pathlib import Path from datetime import datetime # ================== 配置区 ================== ZEEKLOG_USERNAME ="qq_33417321"# ←←← 修改为你想下载的用户名 SAVE_DIR = Path("ZEEKLOG_articles")# 文章保存根目录(自动跨平台)# ⬇️ 新增:设置最小发布日期(含) MIN_PUBLISH_DATE = datetime(2026,2,7)# 只下载 2026年2月1日及之后的文章 HEADERS ={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36","Referer":"https://blog.ZEEKLOG.net/"}defsanitize_filename(name:str)->str:"""清理文件名,移除 Windows 非法字符和‘原创’字样""" name = name.replace("原创","").strip() name = re.sub(r'[\\/*?:"<>|\r\n]',"_", name)return name or"untitled"defparse_publish_date(date_str:str)-> datetime |None:"""尝试解析 ZEEKLOG 日期字符串,支持 '2025-06-15 10:30:00' 或 '2025-06-15' 等""" date_str = date_str.strip()for fmt in["%Y-%m-%d %H:%M:%S",# 带秒,如 2025-06-15 10:30:09"%Y-%m-%d %H:%M",# 不带秒,如 2025-06-15 10:30"%Y-%m-%d"# 只有日期,如 2025-06-15]:try:return datetime.strptime(date_str, fmt)except ValueError:continueprint(f" 无法解析日期:{date_str}")returnNonedefget_article_list(username, min_date=None):"""获取博主文章列表(标题、URL、发布时间),可选按 min_date 过滤""" url =f"https://blog.ZEEKLOG.net/{username}/article/list" articles =[] page =1 early_stop =Falsewhilenot early_stop:print(f" 正在抓取第 {page} 页...") response = requests.get(f"{url}/{page}", headers=HEADERS) soup = BeautifulSoup(response.text,'html.parser') items = soup.select(".article-list .article-item-box")ifnot items:break current_page_has_valid =False# 当前页是否有满足条件的文章for item in items: title_elem = item.select_one("h4 a") date_elem = item.select_one(".date")# ZEEKLOG 通常用 .date 类表示发布时间ifnot title_elem:continue title = title_elem.text.strip() link = title_elem["href"] pub_date =Noneif date_elem: raw_date = date_elem.text.strip() pub_date = parse_publish_date(raw_date)# 如果设置了最小日期,且文章发布时间早于该日期,则跳过if min_date and pub_date and pub_date < min_date:continue# 如果发布时间未知但设置了 min_date,保守起见也跳过(或可选择保留)if min_date andnot pub_date:print(f" 警告:无法获取文章 [{title}] 的发布时间,跳过(因设置了日期过滤)")continue articles.append({"title": title,"url": link,"publish_date": pub_date }) current_page_has_valid =True# 如果当前页没有任何有效文章(全部早于 min_date),可提前终止if min_date andnot current_page_has_valid and page >1:print(" 后续页面文章均早于指定日期,停止翻页。") early_stop =True page +=1 time.sleep(1)return articles # ========== 以下函数保持不变 ==========defdownload_image(img_url, save_path: Path):try: img_headers = HEADERS.copy() img_headers["Accept"]="image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" response = requests.get(img_url, headers=img_headers, stream=True, timeout=30)if response.status_code ==200: save_path.parent.mkdir(parents=True, exist_ok=True)withopen(save_path,'wb')as f:for chunk in response.iter_content(chunk_size=8192):if chunk: f.write(chunk)returnTrueelse:print(f"图片下载失败(状态码:{response.status_code}):{img_url}")returnFalseexcept Exception as e:print(f"图片下载异常:{img_url},错误:{str(e)}")returnFalsedefget_image_extension(img_url): parsed_url = urlparse(img_url) path = parsed_url.path.lower() extensions =['.jpg','.jpeg','.png','.gif','.webp','.bmp','.svg']for ext in extensions:if ext in path:return ext return'.jpg'defprocess_images_in_content(content, article_title): soup = BeautifulSoup(content,'html.parser') img_tags = soup.find_all('img')ifnot img_tags:return content safe_title = sanitize_filename(article_title) global_image_dir = SAVE_DIR /"images" article_image_dir = global_image_dir / safe_title for img in img_tags: img_url = img.get('src','')ifnot img_url:continueif img_url.startswith('//'): img_url ='https:'+ img_url elifnot img_url.startswith(('http://','https://')):continuetry: img_hash = hashlib.md5(img_url.encode()).hexdigest()[:8] img_ext = get_image_extension(img_url) img_filename =f"{img_hash}{img_ext}" local_img_path = article_image_dir / img_filename md_img_path =f"./images/{safe_title}/{img_filename}"ifnot local_img_path.exists():print(f" 下载图片:{img_filename}")if download_image(img_url, local_img_path): img['src']= md_img_path else:print(f" 图片下载失败,保留原链接:{img_url}")else: img['src']= md_img_path except Exception as e:print(f" 处理图片时出错:{img_url},错误:{str(e)}")continuereturnstr(soup)defdownload_article(url, article_title):try: response = requests.get(url, headers=HEADERS, timeout=30) soup = BeautifulSoup(response.text,'html.parser') content = soup.select_one("article")ifnot content:print(f" 未找到文章内容")returnNone processed_content = process_images_in_content(str(content), article_title) markdown_content = md(processed_content)return markdown_content except Exception as e:print(f" 下载文章时出错:{str(e)}")returnNonedefsave_to_markdown(title, content, save_dir: Path): save_dir.mkdir(parents=True, exist_ok=True) safe_title = sanitize_filename(title) filename = save_dir /f"{safe_title}.md"withopen(filename,"w", encoding="utf-8")as f: f.write(f"# {title}\n\n") f.write(content)print(f" 已保存:{filename}")return filename # ========== 主程序入口 ==========if __name__ =="__main__":print("开始获取文章列表...") articles = get_article_list(ZEEKLOG_USERNAME, min_date=MIN_PUBLISH_DATE)print(f"找到 {len(articles)} 篇符合条件的文章(发布日期 ≥ {MIN_PUBLISH_DATE.strftime('%Y-%m-%d')})") success_count =0 fail_count =0for i, article inenumerate(articles,1): title = article["title"] url = article["url"] pub_date = article.get("publish_date") date_str = pub_date.strftime("%Y-%m-%d")if pub_date else"未知"print(f"\n[{i}/{len(articles)}] 处理文章:{title} (发布于 {date_str})") content = download_article(url, title)if content: save_to_markdown(title, content, SAVE_DIR) success_count +=1else:print(f" 文章下载失败:{title}") fail_count +=1 time.sleep(2)print(f"\n处理完成!成功:{success_count}篇,失败:{fail_count}篇")print(f"文章保存在:{SAVE_DIR.resolve()}")print("图片保存在:./images/ 目录下,Markdown文件可离线查看")

记得修改MIN_PUBLISH_DATE 里的开始日期。之后执行脚本后,下载下来就是指定日期后的文章了。

Read more

【Linux】编译器gcc/g++、动静态库

【Linux】编译器gcc/g++、动静态库

编译器gcc/g++、动静态库 * 1.gcc / g++的工作流程 * 1.编译过程 * 2.链接过程 * 3.三大问题 * 1.如何理解条件编译? * 2.为什么C/C++编译,要先变成汇编语言? * 3.编译器的发展? * 4.函数库 * 1.什么是库? * 2.动态库和静态库 * 3.动态链接和静态链接 * 4.gcc / g++:默认动态链接,使用动态库 * 5.库和链接的本质 1.gcc / g++的工作流程 1.编译过程 1. 预处理:宏替换,取注释,头文件展开,条件编译。 2.

By Ne0inhk
Flutter 三方库 http_core_client 的鸿蒙化适配指南 - 打造极简、健壮的 OpenHarmony 网络请求核心组件

Flutter 三方库 http_core_client 的鸿蒙化适配指南 - 打造极简、健壮的 OpenHarmony 网络请求核心组件

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 http_core_client 的鸿蒙化适配指南 - 打造极简、健壮的 OpenHarmony 网络请求核心组件 在 Flutter 应用开发中,网络请求层(Networking Layer)是应用的生命线。虽然 dio 功能强大,但对于追求轻量化、高性能的鸿蒙应用来说,一个精简且核心功能完备的客户端库往往更具优势。http_core_client 为开发者提供了一套基于 BaseClient 封装的极简模型。在 OpenHarmony(鸿蒙)环境下,如何结合其底层网络栈、处理系统的代理配置以及优化连接复用,是构建高标准鸿蒙应用的必修课。本文将带大家深入探讨其适配要点。 前言 随着鸿蒙系统(HarmonyOS)进入原生应用开发的新阶段,网络栈的稳定性与安全性(如 TLS

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 icon_font_generator 自动化将 SVG 图标集转化为字体文件(鸿蒙矢量资源全自动管理)

Flutter for OpenHarmony: Flutter 三方库 icon_font_generator 自动化将 SVG 图标集转化为字体文件(鸿蒙矢量资源全自动管理)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在 OpenHarmony 应用中,为了保证在不同分辨率屏幕(手机、折叠屏、平板)下图标都能保持绝对清晰,且为了减小 HAP 包体积,使用“字体图标”取代“位图图片”是业界公认的标准方案。 icon_font_generator 是一个强大的命令行工具。它能将一整组 SVG 图标自动打包成一个 .ttf 字体文件,并同步生成 Dart 类。开发者只需关注 SVG 文件的增删,剩余的同步工作全部自动化。 一、全自动构建链路 命令行扫描 强类型访问 assets/ohos_icons/*.svg (原始素材) icon_font_generator

By Ne0inhk
Flutter 组件 activity_files 适配鸿蒙 HarmonyOS 实战:文件活动流治理,构建高性能存储沙箱访问与资产全生命周期管理架构

Flutter 组件 activity_files 适配鸿蒙 HarmonyOS 实战:文件活动流治理,构建高性能存储沙箱访问与资产全生命周期管理架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 activity_files 适配鸿蒙 HarmonyOS 实战:文件活动流治理,构建高性能存储沙箱访问与资产全生命周期管理架构 前言 在鸿蒙(OpenHarmony)生态迈向全场景分布式协同、涉及海量多媒体资产处理及严苛应用沙箱(Sandbox)隔离的背景下,如何实现一套既能穿透复杂的层级目录、又能实时追踪文件变更活动且具备极高 I/O 吞吐能力的存储治理架构,已成为决定应用性能广度与数据安全深度。在鸿蒙设备这类强调 AOT 极致性能与受限文件权限周期的环境下,如果应用依然采用陈旧的同步文件读取或缺乏活动追踪的直接 I/O,由于由于频繁的磁盘竞争,极易由于由于“主线程阻塞”或“资产状态不同步”导致用户在管理大型媒体库时发生明显的感知性卡顿。 我们需要一种能够解耦文件路径、支持异步流式追踪(Activity Tracking)且符合鸿蒙分布式文件系统安全范式的操作框架。 activity_files 为 Flutter 开发者引入了“

By Ne0inhk