最近在把公众号文章导入网站,于是做了个小工具把文章转化为markdown。其中图片会转为外链形式,代码框等格式会保留,感觉十分方便,因此分享。
```python
import requests
from bs4 import BeautifulSoup
import markdownify
import re
url = "https://mp.weixin.qq.com/s/your-article-link"
# 添加请求头,模拟浏览器访问
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
html = requests.get(url, headers=headers).text
soup = BeautifulSoup(html, "html.parser")
# 提取标题
title_tag = soup.find(id="activity-name")
if not title_tag:
title_tag = soup.find("h1", class_="rich_media_title")
if not title_tag:
title_tag = soup.find("h2", class_="rich_media_title")
title = title_tag.get_text(strip=True) if title_tag else "未找到标题"
print(f"文章标题: {title}")
# 提取作者和发布时间(如果有)
author_tag = soup.find(id="js_name")
author = author_tag.get_text(strip=True) if author_tag else ""
publish_time_tag = soup.find(id="publish_time")
if not publish_time_tag:
publish_time_tag = soup.find("em", id="post-date")
publish_time = publish_time_tag.get_text(strip=True) if publish_time_tag else ""
if author:
print(f"作者: {author}")
if publish_time:
print(f"发布时间: {publish_time}")
# 打印调试信息
print("\n查找 id='js_content' 的元素...")
content = soup.find(id="js_content")
print(f"找到的内容: {content is not None}")
if content is None:
# 尝试其他可能的选择器
print("尝试其他选择器...")
content = soup.find("div", class_="rich_media_content")
print(f"找到 class='rich_media_content': {content is not None}")
if content:
# 提取所有图片信息
images = content.find_all('img')
print(f"\n找到 {len(images)} 张图片")
image_info = []
for idx, img in enumerate(images, 1):
img_data = {
'index': idx,
'data-src': img.get('data-src', ''),
'src': img.get('src', ''),
'alt': img.get('alt', ''),
'data-ratio': img.get('data-ratio', ''),
'data-w': img.get('data-w', '')
}
image_info.append(img_data)
# 替换图片标签为带链接的markdown格式
img_url = img_data['data-src'] or img_data['src']
if img_url:
# 创建新的img标签,确保src属性正确
img['src'] = img_url
if 'data-src' in img.attrs:
del img['data-src']
print(f"图片 {idx}: {img_url[:100]}..." if len(img_url) > 100 else f"图片 {idx}: {img_url}")
# 在转换为markdown之前,先处理小标题
# 微信公众号的小标题通常用 <h2>, <h3>, <strong>, <p> 加粗等标记
# 查找所有可能的小标题元素
sections = content.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
print(f"找到 {len(sections)} 个标题标签(h1-h6)")
# 查找使用strong或b标签且单独成段的文本(常见的小标题格式)
paragraphs = content.find_all('p')
strong_titles = []
number_titles = []
for p in paragraphs:
text = p.get_text(strip=True)
# 检查段落是否只包含一个strong/b标签
strongs = p.find_all(['strong', 'b'])
if len(strongs) == 1 and text == strongs[0].get_text(strip=True):
# 判断是否像标题(长度合适,不是纯数字)
if 2 <= len(text) <= 50 and not text.isdigit():
strong_titles.append(text)
# 将这个段落转换为h3标签以便markdown转换时识别
new_h3 = soup.new_tag('h3')
new_h3.string = text
p.replace_with(new_h3)
# 检查是否是数字开头的小标题格式(如:"1 改他作甚?")
elif re.match(r'^\d+[\s\.、]+.{2,50}[?!。]?$', text):
number_titles.append(text)
# 将这个段落转换为h2标签
new_h2 = soup.new_tag('h2')
new_h2.string = text
p.replace_with(new_h2)
print(f"找到 {len(strong_titles)} 个加粗格式的小标题")
if strong_titles:
print("识别的加粗小标题:", strong_titles)
print(f"找到 {len(number_titles)} 个数字编号的小标题")
if number_titles:
print("识别的编号小标题:", number_titles)
# 转换为markdown
md = markdownify.markdownify(str(content), heading_style="atx")
# 后处理:识别数字编号的小标题(在单独一行且符合标题模式)
lines = md.split('\n')
processed_lines = []
detected_subtitles = []
for i, line in enumerate(lines):
stripped = line.strip()
# 匹配数字开头的小标题:数字 + 空格/点/顿号 + 2-30个字符 + 可选的标点
if re.match(r'^\d+[\s\.、]+.{2,30}[?!。]?$', stripped):
# 确保不是列表项的一部分(前后有空行或是第一行)
prev_empty = (i == 0 or not lines[i-1].strip())
next_empty = (i == len(lines)-1 or not lines[i+1].strip())
if prev_empty or next_empty:
detected_subtitles.append(stripped)
# 转换为二级标题
processed_lines.append(f'## {stripped}')
continue
processed_lines.append(line)
md = '\n'.join(processed_lines)
print(f"后处理识别到 {len(detected_subtitles)} 个小标题")
if detected_subtitles:
print("识别的小标题:", detected_subtitles)
# 构建完整的markdown内容,包含标题和元信息
full_md = f"# {title}\n\n"
if author or publish_time:
full_md += "> "
if author:
full_md += f"作者: {author}"
if author and publish_time:
full_md += " | "
if publish_time:
full_md += f"发布时间: {publish_time}"
full_md += "\n\n"
full_md += "---\n\n"
full_md += md
# 保存markdown文件
open("article.md","w").write(full_md)
print(f"\n已保存到 article.md,内容长度: {len(full_md)} 字符")
# 保存图片信息到单独的文件
with open("images_info.txt", "w") as f:
f.write(f"文章URL: {url}\n")
f.write(f"文章标题: {title}\n")
if author:
f.write(f"作者: {author}\n")
if publish_time:
f.write(f"发布时间: {publish_time}\n")
f.write(f"共找到 {len(images)} 张图片\n\n")
for img_data in image_info:
f.write(f"=== 图片 {img_data['index']} ===\n")
f.write(f"URL: {img_data['data-src'] or img_data['src']}\n")
if img_data['alt']:
f.write(f"描述: {img_data['alt']}\n")
if img_data['data-w']:
f.write(f"宽度: {img_data['data-w']}\n")
if img_data['data-ratio']:
f.write(f"比例: {img_data['data-ratio']}\n")
f.write("\n")
print(f"已保存图片信息到 images_info.txt")
else:
print("未找到文章内容,保存 HTML 以供检查")
open("debug.html", "w").write(html)
print("已保存原始 HTML 到 debug.html")
```
评论
留下评论
邮箱仅用于识别,不会公开
$...$行内 ·$$...$$独立公式