PDF:如何将一个列列表转换为多列数据框? -小组内子小组中的人员列表到多列

问题描述 投票:0回答:2

我有将近15个包含人员列表的PDF。这些PDF只有一列的宽度,因此它是一个纯列表。但是以某种方式,这些列表是嵌套的(子组在组内的子组内...)。除了列表中每个人的第一个数字(这对我的分析非常重要)之外,没有任何数字数据,并且类似的订单信息。

我需要从PDF中提取此列表并将其转换为常规数据框。

这里是一个PDF的结构示例:

TERRITORY ONE
1. GROUP ONE
1. Name Surname
2. Name Surname
3. Name Surname
4. Name Surname
2. GROUP TWO
1. Name Surname
2. Name Surname
3. Name Surname
4. Name Surname
TERRITORY TWO
(...)

这是第一个PDF:http://bocyl.jcyl.es/boletines/1983/04/02/pdf/BOCYL-D-02041983-1.pdf


!!!我发现这些文档也存储在网页中,因此采用HTML格式:http://bocyl.jcyl.es/html/1983/04/02/html/BOCYL-D-02041983-1.do也许从其中获取内容而不是从PDF中获取内容更容易?


您可以想象得到(区域2、3、4 ...,以及随后的子组1、2、3、4等)。每个PDF最多增加600行,而最新的PDF则更多。

我需要创建一个遵循此示例结构的数据框:

   PERSON    |    TERRITORY  |  GROUP  | POSITION IN LIST
Name Surname | TERRITORY ONE | GROUP 1 |         1
(...)
Name Surname | TERRITORY ONE | GROUP 2 |         4
(...)
Name Surname | TERRITORY TWO | GROUP 1 |         3

一行应该是一个人

[POSITION IN LIST应该指的是Name Surname在给定年份中出现的顺序(每个PDF代表一年),在TERRITORY中,在GROUP中。

请考虑类似排名,在该排名中人的顺序很重要。PDF1(第1年)中很少有人会再次出现在PDF2(第2年)中,然后又出现在PDF3(第3年)中,依此类推。因此,所有这一切的目的之一是要知道有多少人重复在此列表中年复一年。

而且,重要的是,分析中要知道每年重复的那个人的位置,得出这个人的发展情况,或者知道这个人在X年后是否消失,等等,这一点很重要

PS:对不起,我的英语不是我的第一语言:(

r list pdf text-mining
2个回答
0
投票
我可能会把它留给您作为练习@pbstckvrflw!

[这是很多工作,但幸运的是我很喜欢这样做,并且在我学习的同时学习了东西。

但是

请注意这种规模的任务通常不适合SO问题,最好尝试自己尝试解决方案,然后针对发现的问题提出非常具体的问题。

我希望您能仔细阅读我编写的代码,并尝试了解每一步所发生的事情。您可能需要学习的主要知识是关于map及其如何将功能应用于列表中的每个项目。我在这里广泛使用map,因为我们正在使用嵌套列表。还有一些很好的正则表达式。

它离完美的代码还很远,可能会有错误或效率低下。如果将其中一些分解为可重复的功能,则更好。并且它生成了几个中间对象,这有点杂乱,但这就是事实。为了清楚起见,部分代码位于块中,部分原因是我没有更多的时间将块集成到更流畅的工作流程中,而不会造成事情破裂的过多风险。

library(rvest) library(stringr) library(purrr) library(dplyr) library(tibble) library(conflicted) conflict_prefer("pluck", "purrr") # you should add any further URLs to this vector urls <- c("http://bocyl.jcyl.es/html/1983/04/02/html/BOCYL-D-02041983-1.do") # scrape text from the relevant part of the webpage # (assume that any additional URLs have the same structure) text <- urls %>% map(., ~ {xml2::read_html(.) %>% rvest::html_nodes("#presentDocumentos p:not([class])") %>% html_text}) # extract a manageable name from the URL and use it to name each text names(text) <- urls %>% str_extract_all(., pattern = "(?<=/)BOCYL.*(?=\\.do$)") # do any manual fixes for errors in source data text1 <- text %>% map(., ~ str_replace_all(., "PARTIDO COMUNISTA DE ESPAÑA PARTIDO COMUNISTA DE CASTILLA- LEON", "2. PARTIDO COMUNISTA DE ESPAÑA PARTIDO COMUNISTA DE CASTILLA- LEON")) text2 <- text1 %>% map(., ~ str_replace_all(., "(\\.)*(\\s)*$", "") %>% str_replace_all(., "(\\s)+", " ") %>% str_replace_all(., "^Suplente.*", "") %>% str_c(., collapse = ";") %>% str_split(., pattern = "JUNTA ELECTORAL DE ") %>% map(., ~ tail(., -1) %>% str_split(., pattern = ";(?=\\d{1,2}\\.\\s([:upper:]|\\s){2,})") %>% set_names(str_to_title(map(., 1))) %>% map(., ~ tail(., -1)) ) ) text3 <- text2 %>% map(., ~ map(., ~ map(., ~ str_split(., pattern = ";(?=\\d{1,2}\\.\\s)") %>% set_names(map(., 1) %>% str_extract(., pattern = "(?<=\\d{1,2}\\.\\s)[:upper:].*")) %>% map(., ~ tail(., -1) %>% enframe(., name = "list_position", value = "person_name") %>% mutate_at( vars("person_name"), ~ str_extract_all(., pattern = "(?<=\\d{1,2}\\.\\s)[:alpha:]+.*")))))) text4 <- text3 %>% map(., ~ map(., ~ map(., ~ map_df(., c, .id = "political_group")))) text5 <- text4 %>% map(., ~ map(., ~ map_df(., c, .id = "territory"))) # EXAMPLE # To look at just the data frame produced from the first web page supplied # (with columns rearranged as desired): data_frame1 <- text5 %>% pluck(1, 1) %>% select(person_name, everything()) data_frame1

我已将最新代码推送到the GitHub repo I made。如果您对问题的答案满意,请在接受的答案中打勾。


0
投票
我想整理一下并简化代码,并添加更多注释,但想在准备好后立即答复。

不幸的是,由于在阅读PDF和创建dput文本块时出现问题,我无法提供代表,但您应该可以修改以下内容以开始使用:

library(here) library(pdftools) library(purrr) library(stringr) # download.file( # url = "http://bocyl.jcyl.es/boletines/1983/04/02/pdf/BOCYL-D-02041983-1.pdf", # destfile = here("pdfs", "BOCYL-D-02041983-1.pdf"), # mode = "wb" # ) pdfs <- list.files(path = here("pdfs"), pattern = "pdf$") text <- map(pdfs, ~ pdftools::pdf_text(here("pdfs", .))) # be better to combine these into a single piped function (mappable) # we need to combine the text into a single block before editing and splitting text1_combined <- str_c(text[[1]], collapse = "") text1_split <- str_split(text1_combined, "JUNTA") # remove header text text1_split <- text1_split[[1]] %>% tail(-1) # repair text lost from str_split text1_list <- map(text1_split, ~ paste0("JUNTA", .)) # extract territory name and use it to name each sub-list text1_list <- text1_list %>% set_names(., nm = str_extract(., "^([:upper:]|\\s)+(?=\r)")) text1_trimmed <- map(text1_list, ~ str_replace(., "^([:upper:]|\\s)+\r\n", "")) text1_trim_tidy <- map(text1_trimmed, ~ str_replace_all(., "\r\n(?=[:digit:])", ",")) text1_trim_tidy <- map(text1_trim_tidy, ~ str_replace_all(., "\r\n", " ")) text1_trim_tidy <- map(text1_trim_tidy, ~ str_replace_all(., "\\s+$", "")) text1_by_party <- map(text1_trim_tidy, ~ str_split(., ",(?=[:digit:]+\\.\\s[:upper:]{2,})")) # clear up intermediate objects # rm(text1_combined) # rm(text1_split) # rm(text1_list) # rm(text1_trimmed) # rm(text1_trim_tidy)

希望我对代码进行更多整理后,将对其进行编辑或添加其他答案。我制作了一个github存储库here供进一步参考。

© www.soinside.com 2019 - 2024. All rights reserved.