读取/处理word documents (docx)的R包有:
- officer
- docxtractr
- WordR
- ReporteRs(已作者已重写至officer)
- textreadr
我有个需求:
- 提取docx文件(源数据)中的文字/表格/图片
- 将指定的文件/表格/图片插入到模板docx中的指定位置
- 基于模板docx生成新的docx文件
因此我选择上述R包中的docxtractr包来提取源数据docx文件中的表格,选择officer包来提取源数据docx文件中的文字
剩下还有图片部分,上述所有R包并没有提供直接抓取图片的函数,但是按照docx文件的原理可以取巧一下
以OOXML保存的.docx可以直接解压缩,文档的文字/表格内容基本上存在
/word/document.xml
;而图片则是存在/word/media
目录中,按照图片顺序命名为imageX.png/imageX.jpeg等等
从上述原理可看出,如果docx文档比较简单(假如是纯文字),那么其实我们读取document.xml
文件即可解析出文字部分;如果文档结构比较复杂,那么还是用现有的R包比较省事
这样我们提取图片的方法也有了,以下重点记录下docxtractr包和officer包如何实现上述的需求。。。
数据准备
首先加载两个R包,并用其对应函数读取源数据docx文件
officer_doc <- officer::read_docx(path = "./data.docx")
docxtractr_docx <- docxtractr::read_docx(path = "./data.docx")
使用officer::read_docx
读取文件后,docx文件会默认解压缩到系统的临时目录下,因此我们用officer_doc$package_dir
来列出所有图片路径
list.files(paste0(officer_doc$package_dir, "\\word\\media"), full.names = T)
可以用officer::docx_summary()
查看docx文件中每行文字数据,生成一个数据框,大致上一行数据对应数据框的一行
summarydf <- docx_summary(officer_doc)
names(summarydf)
dim(summarydf)
使用docxtractr::docx_extract_tbl()
提取docx文件中的指定表格(按照顺序从1开始到N);其会将表格提取并转化为数据框,这是其相比其他R包的优势所在
docx_extract_tbl(docxtractr_docx, 1)
offier包基本操作
经过上述步骤,我们已经能将文字/表格/图片均提取出来了,接着则需要将这些内容分别加入到模板docx中,officer包提供了多个操作:
- Adding elements,添加段落/文字/图片/表格/目录等操作
- Cursor manipulation,控制光标位置以便能在指定位置插入对应的elements
- Remove content,清除内容,一般会搭配cursor来更新内容
- Replace content,替换内容,一般会搭配cursor来替换段落中的指定文字等
- Sections,章节,一般在结束时调用
以officer文档中一个demo文件作为源数据,我新建的一个docx文件作为模板,来演示以下功能
首先读入源数据docx文档,并设置图片路径
library(officer)
library(docxtractr)
library(flextable)
officer_doc <- officer::read_docx(path = "../Desktop/body_add_demo.docx")
docxtractr_docx <- docxtractr::read_docx(path = "../Desktop/body_add_demo.docx")
tempdir <- paste0(officer_doc$package_dir, "\\word\\media")
通过cursor_begin()
来初始化下模板docx,即将光标移至文档最开始的位置
doc_output <- officer::read_docx(path = "../Desktop/template.docx") %>%
# Begin docx
cursor_begin()
此时可以通过docx_show_chunk()
函数来查看光标所在的段落文字
docx_show_chunk(doc_output)
光标是以段落为单位进行移动的,因此如果想移至下一段落,则可以用cursor_forward()
,往上移则是cursor_backward()
,如果想移至指定段落,则可以用cursor_reach()
函数
doc_output <- doc_output %>% cursor_forward()
接着是增加/替换段落,一般来说我们移至指定段落后使用adding elements操作默认是在光标下方,比如body_add_par()
doc_output <- officer::read_docx(path = "../Desktop/template.docx") %>%
# Begin docx
cursor_begin() %>%
body_add_par(value = "加上这个", style = "Normal") %>%
docx_show_chunk()
如果更改body_add_par()
函数中的pos
参数,则可以将上述的添加变成替换(也可以将默认的下方变成上方)
doc_output <- officer::read_docx(path = "../Desktop/template.docx") %>%
# Begin docx
cursor_begin() %>%
body_add_par(value = "替换成这个", style = "Normal", pos = "on") %>%
docx_show_chunk()
如果不想替换整个段落,而是段落中的某个文字的话,则可以body_replace_all_text
函数,比如替换"文字XX"段落中的XX为YY(首先需要先reach定位到改段落,然后再替换。。)
doc_output <- officer::read_docx(path = "../Desktop/template.docx") %>%
# Begin docx
cursor_begin() %>% cursor_reach(keyword = "文字XX") %>%
body_replace_all_text(old_value = "XX", new_value = "YY", only_at_cursor = TRUE, fixed = TRUE)
如果你也跟我一样是使用中文的话,不出意外会报错,因为其在有中文的环境下对于词性的分割不是太理想,比如用docx_show_chunk
看下:
> docx_show_chunk(doc_output)
2 text nodes found at this cursor.
<w:t>: '文字X'
<w:t>: 'X
从上述可看出"文字XX"中的"XX"被分割成两部分,所以报错也难免了,解决方法可看:Replace text anywhere in the document, or at a cursor,简单的说就是如果我们想替换"XX",则在模板中将"XX"重新复制黏贴下,而不是用手动输入那种方式;更改后如下所示应该就正常了
doc_output <- officer::read_docx(path = "../Desktop/template.docx") %>%
# Begin docx
cursor_begin() %>% cursor_reach(keyword = "文字XX") %>%
body_replace_all_text(old_value = "XX", new_value = "YY", only_at_cursor = TRUE, fixed = TRUE) %>%
docx_show_chunk()
>2 text nodes found at this cursor.
<w:t>: '文字'
<w:t>: 'YY'
接着我们需要插入表格,officer包能将flextable包所生成的表格进行呈现输出,而且后者也是一个非常好用的R包表格生成工具
首先使用docxtractr包的
docx_extract_tbl()
函数抓取表格然后用flextable包的
qflextable()
转化生成表格最后用officer包的
body_add_flextable()
函数进行输出tb <- docx_extract_tbl(docxtractr_docx, 1) %>% qflextable()
doc_output <- officer::read_docx(path = "../Desktop/template.docx") %>% # Begin docx cursor_begin() %>% cursor_reach(keyword = "请在下方插入表格") %>% cursor_forward() %>% body_add_flextable(value = tb) %>% docx_show_chunk()
相比表格而言,插入图片就比较简单了,查到图片的临时路径,然后body_add_img()
函数调用即可
doc_output <- officer::read_docx(path = "../Desktop/template.docx") %>%
# Begin docx
cursor_begin() %>% cursor_reach(keyword = "请在下方插入图片") %>%
body_add_img(src = paste0(tempdir, "\\image1.png"), pos = "after", style = "centered") %>%
docx_show_chunk()
如果出现报错:错误: could not match any style named 'centered' in c('Normal')
,在说明在模板文件中没有叫做""centered"的style;解决办法很直接:在模板docx中新建一个叫做"centered"的style,并设置居中即可
doc_output <- officer::read_docx(path = "../Desktop/template.docx") %>%
# Begin docx
cursor_begin() %>% cursor_reach(keyword = "请在下方插入图片") %>%
body_add_img(src = paste0(tempdir, "\\image1.png"), pos = "after", style = "centered", width = 4, height = 3) %>%
docx_show_chunk()
最后我们将上述所有操作整合一下,使用print
输出到本地
doc_output <- officer::read_docx(path = "../Desktop/template.docx") %>%
# Begin docx
cursor_begin() %>%
cursor_reach(keyword = "文字XX") %>%
body_replace_all_text(old_value = "XX", new_value = "YY", only_at_cursor = TRUE, fixed = TRUE) %>%
cursor_reach(keyword = "请在下方插入表格") %>%
body_add_flextable(value = tb) %>%
cursor_reach(keyword = "请在下方插入图片") %>%
body_add_img(src = paste0(tempdir, "\\image1.png"), pos = "after", style = "centered", width = 4, height = 3)
print(doc_output, target = "../Desktop/demo.docx")
Summary
对于officer包其他功能,可以查阅其官方文档:https://davidgohel.github.io/officer/index.html,功能极其好用哈~
对于表格样式的修改,具体参考flextable包的官方文档:https://davidgohel.github.io/flextable/articles/overview.html,可选择的样式很齐全,基本满足常规的需求
对于其他处理docx文档的R包了解的并不深,大家可以去尝试下
参考资料
- https://www.r-bloggers.com/using-r-to-get-data-out-of-word-docs/
- http://www.sthda.com/english/wiki/create-and-format-word-documents-using-r-software-and-reporters-package
- Create and format Word documents using R software and Reporters package
本文出自于http://www.bioinfo-scrounger.com转载请注明出处