ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从字符串中提取 Document 的 page_content:手动拆还是用工具?

从字符串中提取 Document 的 page_content:手动拆还是用工具?

从字符串中提取 Document 的 page_content:手动拆还是用工具?

背景

继上一题的手拆字符串之后,又来了一个升级版:

字符串内容如下(为了阅读方便,我做了换行处理):

“[Document(metadata={‘pk’: 12, ‘page’: 2}, page_content=‘这里是第一段正文内容…’), Document(metadata={‘pk’: 27, ‘page’: 2}, page_content=‘这里是第二段正文内容…’), Document(metadata={‘pk’: 14, ‘page’: 3}, page_content=‘这里是第三段正文内容…’)]”

需求是:从这个字符串中提取每个 Document 的 page_content(正文内容),然后拼接成一个完整的字符串。

这道题的核心考点是:给定一个包含对象的字符串,如何安全地提取其中的特定字段(page_content)。

思路一:纯手动拆分(正则表达式)

既然字符串有固定格式:Document(metadata={‘pk’: 数字, ‘page’: 数字}, page_content=‘这里是正文…’)

我们可以直接用正则表达式提取 page_content 后面的内容:

import re raw_str = "[Document(metadata={'pk': 12, 'page': 2}, page_content='这里是第一段正文内容...'), Document(metadata={'pk': 27, 'page': 2}, page_content='这里是第二段正文内容...'), Document(metadata={'pk': 14, 'page': 3}, page_content='这里是第三段正文内容...')]" # 提取 page_content 后面的内容 contents = re.findall(r"page_content='(.*?)'", raw_str) # 拼接成完整字符串 result = "".join(contents) print(result) # 输出: 这里是第一段正文内容...这里是第二段正文内容...这里是第三段正文内容...

优点

  • 代码短,思路直接
  • 不需要额外库(re 是标准库)

缺点

  • 极其脆弱:如果正文里包含单引号,正则就会失效
  • 不通用:换一种对象结构就得重写正则
  • 如果 page_content 的值包含换行或特殊字符,代码立刻报错

思路二:用 ast.literal_eval 解析(更安全)

如果我们能把字符串转成 Python 对象,然后直接访问 page_content 属性,那就安全得多。

但问题来了:ast.literal_eval 不认识 Document 这个类名,所以不能直接解析。

解决思路:把 Document(…) 替换成字典 {…}

import re import ast raw_str = "[Document(metadata={'pk': 12, 'page': 2}, page_content='这里是第一段正文内容...'), Document(metadata={'pk': 27, 'page': 2}, page_content='这里是第二段正文内容...'), Document(metadata={'pk': 14, 'page': 3}, page_content='这里是第三段正文内容...')]" # 提取 metadata 和 page_content pattern = r"Document\(metadata=({.*?}), page_content='(.*?)'\)" matches = re.findall(pattern, raw_str) contents = [] for metadata_str, content in matches: contents.append(content) # content 就是 page_content 的值 result = "".join(contents) print(result) # 输出: 这里是第一段正文内容...这里是第二段正文内容...这里是第三段正文内容...

优点

  • 解析的是 Python 字面量,安全可靠
  • 能处理稍微复杂的格式变化(只要 metadata 部分合法)

缺点

  • 代码比纯正则稍复杂
  • 如果 page_content 里有单引号,仍然会出问题

思路三:针对 page_content 包含特殊字符的终极方案

如果 page_content 里面可能包含单引号、换行等特殊字符,上面的正则就失效了。

这时候需要更精确的匹配策略:

import re import ast raw_str = "[Document(metadata={'pk': 12, 'page': 2}, page_content='第一段正文...'), Document(metadata={'pk': 27, 'page': 2}, page_content='第二段正文...')]" # 用正则提取 metadata 部分,再单独提取 page_content pattern = r"Document\(metadata=({.*?}), page_content='(.*?)'\)" matches = re.findall(pattern, raw_str) contents = [] for metadata_str, content in matches: # 如果 content 里有转义字符,用 ast.literal_eval 安全还原 try: # 把 content 当作 Python 字符串字面量解析 safe_content = ast.literal_eval(f"'{content}'") contents.append(safe_content) except: contents.append(content) result = "".join(contents)

思路四:如果题目给的是对象列表(最理想情况)

如果题目不是给你一个字符串,而是给你一个已经存在的 documents 列表,那就直接操作对象即可:

contents = [doc.page_content for doc in documents] result = "".join(contents)

这是最简单、最直接、最安全的方式。

这道题在考试中的正确答法

  1. 如果题目明确说"这是一个字符串":用 ast.literal_eval 配合 re.findall 提取 page_content 字段,然后拼接。
  2. 如果题目说"有一个 documents 列表":直接用列表推导式 [doc.page_content for doc in documents]。
  3. 如果 page_content 可能包含特殊字符(如引号、换行),用 ast.literal_eval 做二次安全解析。

总结一句话

手动拆分是"能做到",但正确做法是"安全解析"。考试看的是你对工具的理解,不是你折腾字符串的能力。

三种方式对比

方式代码量安全性通用性推荐度
纯正则手拆极低极低不推荐
ast.literal_eval + 正则推荐
直接操作对象列表极高极高最推荐

彩蛋:如果 page_content 包含换行或特殊字符怎么办?

如果正文内容可能包含换行或特殊字符,上面的方法仍然可以处理,因为 ast.literal_eval 能安全解析 Python 字符串字面量。

但如果正文内容非常长,或者包含各种特殊符号,最稳妥的方式是在字符串生成时就做好序列化,而不是在解析时临时处理。

返回列表