安利一个好东西 doc2x,可以把 pdf 直接转成 markdown,扫描的 pdf 也行
流程大概这个样子,doc2xpdf 识别转 markdown 然后导入思源,稍作排版,太舒服了,简单的格式排版之后堪比原书的排版
doc2x 的网址:https://doc2x.com
一个手机号免费转 500 页,如果有邀请码有五天的 pro 会员可以转 1000 页,后面邀请别人可以获得 5 天的 pro 会员时常,就是一天能转 1000 页的。不过这个年头谁还没有几个手机号是吧。。。
下图是一本概率论的 pdf 转 markdown 之后导入思源笔记之后的排版效果
doc2x 的公式识别贼准,还能识别图片,比如下图这种
目前发现有几个小缺点:
- 有些表格可能会用 html 的表格标记标记出来
- 标题的层级容易识别错误,基本所有标题识别出来都是二级标题
- 对于排版格式很花哨的标题识别效果不是特别好,比如标题前面有个手指头指着那种。。。。。每次看得眼花缭乱的,咱也不知道这种排版的好处在哪里,咱也不敢问。
不过这两个小问题都很简单
第一个就抽取出来交给 gpt 让他帮你修改一下
第二个就正则匹配一下各级标题给一下各级标题的排版就好,我用的是 vscode,里面支持正则匹配然后替换,这样就不用写代码了 hhhh,没试过其他编辑器不过应该也可以的把。,比如下图的把章节修改为一级标题。
下面是一段我写的抽取<table></table> 表格然后转化为 markdown 表格的代码,大家有需要的话自己拿去改一改哈。
import re
import asyncio
from tqdm import tqdm
from openai import AsyncOpenAI
import openai
# 确保你有 OpenAI 的 API 密钥
openai.api_key = "sk-xxxxxxxxxxxxx"
openai.base_url = "https://xxxxxxxxxxx"
openai.default_headers = {"x-foo": "true"}
client = AsyncOpenAI(api_key=openai.api_key, base_url=openai.base_url, default_headers=openai.default_headers)
MAX_WORKERS = 60
MAX_RETRIES = 3 # 最大重试次数
bar = tqdm(total=0)
async def get_markdown(data):
data = data.replace("```markdown", "")
data = data.replace("```", "")
return data
async def chat_with_gpt4(semaphore, messages):
async with semaphore:
try:
completion = await client.chat.completions.create(
model="gpt-4o",
messages=messages,
temperature=0.6
)
answer = completion.choices[0].message.content
return answer
except Exception as e:
print(f"GLM-4-Air 调用错误: {e}")
return None
async def process_table_async(semaphore, table_html, retry_count=0):
prompt = """
你需要将我所给你的内容转换为markdown表格,只用输出markdown文本,不用做任何解释
"""
try:
answer_text = await chat_with_gpt4(
semaphore,
[
{"role": "system", "content": prompt},
{"role": "user", "content": table_html}
]
)
markdown_table = await get_markdown(answer_text)
if not markdown_table and retry_count < MAX_RETRIES:
return await process_table_async(semaphore, table_html, retry_count + 1)
bar.update(1)
return markdown_table
except Exception as e:
print(e)
if retry_count < MAX_RETRIES:
return await process_table_async(semaphore, table_html, retry_count + 1)
else:
return None
def replace_table_contents(markdown_content):
table_pattern = re.compile(r'(<table.*?>.*?</table>)', re.DOTALL)
semaphore = asyncio.Semaphore(MAX_WORKERS)
async def process_all_tables(tables):
tasks = [process_table_async(semaphore, table) for table in tables]
return await asyncio.gather(*tasks)
tables = table_pattern.findall(markdown_content)
bar.total = len(tables)
processed_tables = asyncio.run(process_all_tables(tables))
for original, processed in zip(tables, processed_tables):
if processed:
markdown_content = markdown_content.replace(original, processed)
return markdown_content
if __name__ == "__main__":
markdown_file_name = "MD概率论与数理统计 第五版\概率论与数理统计 第五版.md"
out_put_file_name = "MD概率论与数理统计 第五版\概率论与数理统计 第五版out.md"
with open(markdown_file_name,"r",encoding="utf-8") as f:
markdown_content = f.read()
# 处理 HTML 内容
processed_content = replace_table_contents(markdown_content)
with open(out_put_file_name,"w",encoding="utf-8") as f:
f.write(processed_content)
最后,doc2x 里面还有一个 pdf 翻译的功能,虽然我不咋用得到,但是真的好强呀,比如下面几个图
到处原文和译文的
直接导出译文的
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于