CSDN文章保存为MD文档(一)

免责声明

文章仅做经验分享用途,利用本文章所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任,一旦造成后果请自行承担!!!

import os
import re
import sys
import requests
sys.path.append("")
from os.path import join, exists
from urllib.request import urlretrieve
from bs4 import BeautifulSoup, Tag, NavigableString, Comment

class Parser(object):
    def __init__(self, html,markdown_dir):
        self.html = html
        self.soup = BeautifulSoup(html, 'html.parser')
        self.outputs = []
        self.fig_dir = markdown_dir
        self.pre = False
        self.equ_inline = False

        if not exists(self.fig_dir):
            os.makedirs(self.fig_dir)
        self.recursive(self.soup)

    def remove_comment(self, soup):
        if not hasattr(soup, 'children'): return
        for c in soup.children:
            if isinstance(c, Comment):
                c.extract()
            self.remove_comment(c)

    def recursive(self, soup):
        if isinstance(soup, Comment): return
        elif isinstance(soup, NavigableString):
            for key, val in special_characters.items():
                soup.string = soup.string.replace(key, val)
            self.outputs.append(soup.string)
        elif isinstance(soup, Tag):
            tag = soup.name
            if tag in ['h1', 'h2', 'h3', 'h4', 'h5']:
                n = int(tag[1])
                soup.contents.insert(0, NavigableString('\n' + '#'*n + ' '))
                soup.contents.append(NavigableString('\n'))
            elif tag == 'a' and 'href' in soup.attrs:
                soup.contents.insert(0, NavigableString('['))
                soup.contents.append(NavigableString("]({})".format(soup.attrs['href'])))
            elif tag in ['b', 'strong']:
                soup.contents.insert(0, NavigableString('**'))
                soup.contents.append(NavigableString('**'))
            elif tag in ['em']:
                soup.contents.insert(0, NavigableString('*'))
                soup.contents.append(NavigableString('*'))
            elif tag == 'pre':
                self.pre = True
            elif tag in ['code', 'tt']:
                if self.pre:
                    if not 'class' in soup.attrs:
                        language = 'bash' 
                    else:
                        for name in ['cpp', 'bash', 'python', 'java']:
                            if name in ' '.join(list(soup.attrs['class'])): # <code class="prism language-cpp">
                                language = name
                    soup.contents.insert(0, NavigableString('\n```{}\n'.format(language)))
                    soup.contents.append(NavigableString('```\n'))
                    self.pre = False  # assume the contents of <pre> contain only one <code>
                else:
                    soup.contents.insert(0, NavigableString('`'))
                    soup.contents.append(NavigableString('`'))
            elif tag == 'p':
                if soup.parent.name != 'li':
                    # print(soup.parent)
                    soup.contents.insert(0, NavigableString('\n'))
            elif tag == 'span':
                if 'class' in soup.attrs:
                    if ('katex--inline' in soup.attrs['class'] or
                       'katex--display' in soup.attrs['class']): ## inline math
                        self.equ_inline = True if 'katex--inline' in soup.attrs['class'] else False
                        math_start_sign = '$' if self.equ_inline else '\n\n$$'
                        math_end_sign = '$' if self.equ_inline else '$$\n\n'
                        equation = soup.find_all('annotation', {'encoding': 'application/x-tex'})[0].string
                        equation = math_start_sign + str(equation) + math_end_sign
                        self.outputs.append(equation)
                        self.equ_inline = False
                        return
            elif tag in ['ol', 'ul']:
                soup.contents.insert(0, NavigableString('\n'))
                soup.contents.append(NavigableString('\n'))
            elif tag in ['li']:
                soup.contents.insert(0, NavigableString('+ '))
            elif tag == 'img':
                src = soup.attrs['src']
                # pattern = r'.*\.png'
                pattern = r'(.*\..*\?)|(.*\.(png|jpeg|jpg))'
                result_tuple = re.findall(pattern, src)[0]
                if result_tuple[0]:
                    img_file = result_tuple[0].split('/')[-1].rstrip('?')
                else:
                    img_file = result_tuple[1].split('/')[-1].rstrip('?')
                img_file = join(self.fig_dir, img_file)
                urlretrieve(src, img_file)
                code = '![{}]({})'.format(img_file, img_file)
                self.outputs.append('\n' + code + '\n')
                return
        if not hasattr(soup, 'children'): return
        for child in soup.children:
            self.recursive(child)

def html2md(url, md_file, with_title=False):
    response = requests.get(url,headers = header)
    soup = BeautifulSoup(response.content, 'html.parser', from_encoding="utf-8")
    html = ""
    for child in soup.find_all('svg'):
        child.extract()
    if with_title:
        for c in soup.find_all('div', {'class': 'article-title-box'}):
            html += str(c)
    for c in soup.find_all('div', {'id': 'content_views'}):
        html += str(c)

    parser = Parser(html,markdown_dir)
    with open(md_file, 'w',encoding="utf-8") as f:
        f.write('{}\n'.format(''.join(parser.outputs)))
        
def download_csdn_single_page(article_url, md_dir, with_title=True, pdf_dir='pdf', to_pdf=False):
    response = requests.get(article_url,headers = header)
    soup = BeautifulSoup(response.content, 'html.parser', from_encoding="utf-8")
    title = soup.find_all('h1', {'class': 'title-article'})[0].string  ## 使用 html 的 title 作为 md 文件名
    title = title.replace('*', '').strip().split()
    md_file = md_dir+'/'+title[0] + '.md'
    print('正在保存 Markdown File To {}'.format(md_file))
    html2md(article_url, md_file, with_title=with_title)

header = {
        "Accept": "application/json, text/plain, */*",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,und;q=0.7",
        "Connection": "keep-alive",
        "Content-Type": "application/json;charset=UTF-8",
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) Safari/537.36",
        "x-requested-with": "XMLHttpRequest"
        }

special_characters = {
    "&lt;": "<", "&gt;": ">", "&nbsp": " ",
    "&#8203": "",
}

if __name__ == '__main__':
    article_url = input(str("输入需要保存的csdn博客链接:")) #csdn博客链接
    markdown_dir = './' #保存文件夹
    download_csdn_single_page(article_url,markdown_dir)


结语

没有人规定,一朵花一定要成长为向日葵或者玫瑰。

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

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

相关文章

Element中el-table组件右侧空白隐藏-滚动条

开发情况&#xff1a; 固定table高度时&#xff0c;出现滚动条&#xff0c;我们希望隐藏滚动条&#xff0c;或修改滚动条样式&#xff0c;出现table右边出现15px 的固定留白。 代码示例 <el-table class"controlTable" header-row-class-name"controlHead…

C语言二十一弹 --打印空心正方形

C语言实现打印空心正方形 思路&#xff1a;观察图中空心正方形&#xff0c;可知首行列和尾行列被黑色外框包裹&#xff0c;其它均为空。所以按观察打印即可。 总代码 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h>int main() {int n 0;while (scanf("%d&q…

关于数据摆渡 你关心的5个问题都在这儿!

数据摆渡&#xff0c;这个词语的概念源自于网络隔离和数据交换的场景和需求。不管是物理隔离、协议隔离、应用隔离还是逻辑隔离&#xff0c;最终目的都是为了保护内部核心数据的安全。而隔离之后&#xff0c;又必然会存在文件交换的需求。 传统的跨网数据摆渡方式经历了从人工U…

手把手教你通过CODESYS V3进行PLC编程(二)

教程背景 在上一期教程中&#xff0c;我们已经完成了控制器设备的连接和配置。接下来的教程将继续以宏集MC-Prime为例&#xff0c;假设控制器已经配置并连接到开发者的PC上&#xff0c;为您演示如何为控制器安装合适的CODESYS V3版本并创建第一个程序。 一、安装CODESYS &…

调用飞浆情感评分模型

# 跑模型并保存 import paddlehub as hub # 加载模型 senta hub.Module(name"senta_lstm")# dataframe格式转为list格式 text articles[标题内容].drop_duplicates().to_list() # 情感评分 results_data senta.sentiment_classify(data{text:text}) results_df p…

解决 requests 库下载文件问题的技术解析

每次都以为自己即将战胜bug&#xff0c;这是一场永无休止的游戏。在编程的世界中&#xff0c;bug就像狡猾的敌人&#xff0c;时隐时现&#xff0c;让人防不胜防。 今天&#xff0c;我要分享的是如何解决requests库下载文件问题的技术解析。这是一个让我头痛已久的bug&#xff0…

FastAPI通过SSE进行流式输出

服务端推送 在服务器推送技术中&#xff0c;服务器在消息可用后立即主动向客户端发送消息。其中&#xff0c;有两种类型的服务器推送&#xff1a;SSE和 WebSocket。 SSE&#xff08;Server-Send Events&#xff09; SSE 是一种在基于浏览器的 Web 应用程序中仅从服务器向客户…

【高级网络程序设计】Week2-1 Sockets

一、The Basics 1. Sockets 定义An abstraction of a network interface应用 use the Socket API to create connections to remote computers send data(bytes) receive data(bytes) 2. Java network programming the java network libraryimport java.net.*;similar to…

pgsql常用命令总结

pgsql常用命令及相关总结 命令 命令登录 psql -U postgres -h 127.0.0.1 -p 5432 -d vism查看所有数据库&#xff1a;\l 进入某一数据库&#xff1a;\c 数据库名字 查看数据库表&#xff1a;\dt 列出某一张表格的结构&#xff1a;\d 表名 查看某个表的所有数据&#xff1a;s…

学习笔记记录

目录 windows php一句话木马 日志清理 DOS命令 查看用户的SID 最高权限 常见的cmd命令 反弹shell PHPMYadmin mysql注入语句 wmic linux crontab创建隐藏后门 linux日志文件 knockd服务 ssh登录 ssh隧道 本地转发 远程转发 动态转发 /proc: Centos 8 更…

CentOS 7 使用cJSON 库

什么是JSON JSON是一种轻量级的数据交换格式&#xff0c;可读性强、编写简单。键值对组合编写规则&#xff0c;键名使用双引号包裹&#xff0c;冒号&#xff1a;分隔符后面紧跟着数值&#xff0c;有两种常用的数据类型是对象和数组。 对象&#xff1a;使用花括号{}包裹起来的…

【Rxjava详解】(三)更好实践异步请求

本文为更像demo的总结&#xff0c;在实际开发中&#xff0c;利用rxjava采取异步请求在一些简单的单纯请求数据上面&#xff0c;会显得没有必要&#xff0c;但rxjava提供的思路&#xff0c;在后期不论是增加功能&#xff0c;还是说整体代码的工整度&#xff0c;都能感受到开发的…

补充:自动化测试高级应用之python多线程的使用-新方法

前段时间在网上学习多线程跑用例的时,发现一种更简洁,优雅的使用多线程的方法,在此分享给大家。 阅读本文前,请先阅读前面写的多线程跑用例的文章:【精选】第七章 第四节 自动化测试高级应用之python多线程的使用_add_test_img-CSDN博客 本文新的方法,对原有的run_al…

接口传参数list的时候,items里面放个​​​​​​​list

item里面放个list 先定义一个 list&#xff0c;循环add加入参数

java之switch case的用法

java之switch case的用法 Java中的switch语句是一种多路选择结构&#xff0c;它允许一个变量在其值的多个可能选项之间进行选择。这种结构可以替代一系列嵌套的if-else语句&#xff0c;使代码更清晰和简洁。 下面是switch语句的基本语法&#xff1a; switch (expression) { …

android keylayout键值适配

1、通过getevent打印查看当前keyevent数字对应事件和物理码 2、dumpsys input 查看输入事件对应的 KeyLayoutFile: /system/usr/keylayout/Vendor_6080_Product_8060.kl 3、通过物理码修改键值映射&#xff0c;修改/system/usr/keylayout/目录下的文件

CuratorFramework的blockUntilConnected方法

CuratorFramework是一个ZooKeeper客户端库&#xff0c;它提供了一些用于处理ZooKeeper连接和节点操作的高级API。其中&#xff0c;blockUntilConnected方法是一个阻塞方法&#xff0c;它会一直阻塞直到客户端成功连接到ZooKeeper服务器。 具体来说&#xff0c;blockUntilConne…

(三)、基于 LangChain 实现大模型应用程序开发 | 模型链 Chains

&#x1f604; 为什么我们需要Chains &#xff1f; 链允许我们将多个组件组合在一起&#xff0c;以创建一个单一的、连贯的应用程序。链&#xff08;Chains&#xff09;通常将一个LLM&#xff08;大语言模型&#xff09;与提示结合在一起&#xff0c;使用这个构建块&#xff0…

永久免费!N个excel表一键合并成一个表(excel表格合并技巧)

您是否还在用手工复制粘贴来将多个EXCEL或表的数据合并到一个表里&#xff1f;那就太麻烦&#xff0c;效率太低了&#xff0c;用金鸣表格文字识别的“表格合并”功能&#xff0c;可免费将N个excel文件或N个excel表一键合并到一个表里面&#xff0c;而且这个功能永久免费&#x…

【C++】特殊类设计 {不能被拷贝的类;只能在堆上创建的类;只能在栈上创建的类;不能被继承的类;单例模式:懒汉模式,饿汉模式}

一、不能被拷贝的类 设计思路&#xff1a; 拷贝只会发生在两个场景中&#xff1a;拷贝构造和赋值重载&#xff0c;因此想要让一个类禁止拷贝&#xff0c;只需让该类不能调用拷贝构造以及赋值重载即可。 C98方案&#xff1a; 将拷贝构造与赋值重载只声明不定义&#xff0c;并…