R语言解析XML-TEI P5构建兼容stylo等包的文本分析语料库方法
TEI P5文档转计量语料库的可落地操作流程
现有两种实现路径的问题根源
- 路径1(xml2与XML包混用):两套包的解析对象不互通,把
read_xml返回的xml2类对象直接传给XML包的xmlParse,会导致解析逻辑混乱,无法准确筛选正文节点,残留大量标注内容;且未按TEI P5的固定结构定位文本节点,默认抓取了teiHeader元数据、注释、校勘异文等非正文内容。 - 路径2(直接用stylo读取原始TEI):stylo内置的XML解析逻辑是通用标签剔除规则,没有针对TEI P5的结构做适配,只会删除标签本身,不会筛选哪些内容属于有效正文,会把出版说明、目录、脚注等非正文内容全部计入语料;若文件编码未强制指定,极易出现德语变音字符乱码。
标准化操作步骤
第一步:批量提取TEI P5的有效无标注纯文本
全程使用xml2包处理,不与XML包混⽤,通过XPath规则定位正文节点,强制指定UTF-8编码,保证ä、ü、ß等特殊字符完整保留。
首先明确TEI P5的固定结构边界:
- 保留范围:
<tei:body>节点下的核心阅读正文 - 剔除范围:
<tei:teiHeader>(元数据)、<tei:note>(注释)、<tei:app>(校勘异文)、<tei:front>(前言/目录)、<tei:back>(尾注/附录)下的所有内容
对应可直接运行的提取代码:
library(xml2) library(dplyr) # 注册TEI标准命名空间,避免XPath定位失败 tei_ns <- c(tei = "http://www.tei-c.org/ns/1.0") # 批量匹配目录下所有TEI XML文件 tei_files <- list.files( path = "存放TEI文档的本地路径", pattern = ".*\\.xml$", full.names = TRUE ) # 单文档文本提取函数 extract_tei_core_text <- function(file_path) { # 读取文档,强制指定UTF-8编码 tei_doc <- read_xml(file_path, encoding = "UTF-8") # 按TEI规则筛选有效正文文本 core_text <- tei_doc %>% xml_find_all( xpath = "//tei:body//text()[ not(ancestor::tei:teiHeader) and not(ancestor::tei:note) and not(ancestor::tei:app) and not(ancestor::tei:front) and not(ancestor::tei:back) ]", ns = tei_ns ) %>% xml_text() %>% paste(collapse = " ") # 生成文档唯一ID doc_id <- basename(file_path) %>% gsub("\\.xml$", "", .) return(data.frame(doc_id = doc_id, text = core_text)) } # 批量处理所有文档,生成结构化的语料数据框 corpus_df <- bind_rows(lapply(tei_files, extract_tei_core_text))
第二步:适配三类计量分析包的语料结构
基于上一步生成的结构化语料数据框,可无缝对接三个分析包的原生格式,不需要额外重复解析XML内容:
- 适配quanteda包:直接调用corpus构造函数读入数据框,quanteda原生支持两列结构(doc_id、text)的输入,UTF-8编码下德语变音字符可直接识别,无乱码。
library(quanteda) q_corpus <- corpus(corpus_df, docid_field = "doc_id", text_field = "text")
- 适配tm包:通过DataframeSource把数据框转成tm认可的语料格式,指定德语语言标识即可直接用tm的清洗、分词功能。
library(tm) tm_corpus <- VCorpus( DataframeSource(corpus_df), readerControl = list(language = "de_DE", encoding = "UTF-8") )
- 适配stylo包:因为已经提取了无标注纯文本,不需要stylo再做XML解析,把文本存成本地txt文件后,把加载参数里的
markup.type改成plain.txt即可,不会残留标注内容。
library(stylo) # 先把纯文本写入本地目录供stylo读取 dir.create("stylo_input_corpus", showWarnings = FALSE) apply(corpus_df, 1, function(single_doc) { writeLines( single_doc["text"], con = file.path("stylo_input_corpus", paste0(single_doc["doc_id"], ".txt")), useBytes = TRUE ) }) # 加载生成的纯文本语料 stylo_corpus <- load.corpus.and.parse( files = "all", corpus.dir = "stylo_input_corpus", markup.type = "plain.txt", corpus.lang = "German", encoding = "UTF-8", preserve.case = FALSE )
关键注意事项
- 禁止混用xml2和XML包的解析逻辑:两个包的对象结构完全独立,交叉传参会导致解析异常、内容提取不全。
- 编码设置必须显式指定
encoding = "UTF-8",不要依赖系统默认编码,否则德语ä、ü、ö等变音字符会出现乱码。 - 若需要保留部分特殊内容(比如作者原注),可直接修改XPath里的节点筛选规则,去掉对应的not判断条件即可。
内容的提问来源于stack exchange,提问作者psh
相关产品推荐
相关产品推荐

