安利一个好东西 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 翻译的功能,虽然我不咋用得到,但是真的好强呀,比如下面几个图
到处原文和译文的
直接导出译文的
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于