【问题标题】:convert HTML Character Entity Encoding in R在 R 中转换 HTML 字符实体编码
【发布时间】:2011-02-20 21:15:23
【问题描述】:

R 中有没有办法转换 HTML 字符实体编码?

我想转换 HTML 字符实体,例如 & 至 & 或 > 到 >

对于 Perl,存在可以做到这一点的包 HTML::Entities,但我在 R 中找不到类似的东西。

我也尝试了iconv(),但没有得到令人满意的结果。也许还有一种使用XML包的方法,但我还没有想通。

【问题讨论】:

    标签: html r encoding character-encoding


    【解决方案1】:
    library(xml2)
    xml_text(read_html(charToRaw("& >")))
    

    给予:

    [1] "& >"
    

    【讨论】:

      【解决方案2】:

      使用 xml2 包取消转义 xml/html 值:

      unescape_xml <- function(str){
        xml2::xml_text(xml2::read_xml(paste0("<x>", str, "</x>")))
      }
      
      unescape_html <- function(str){
        xml2::xml_text(xml2::read_html(paste0("<x>", str, "</x>")))
      }
      

      例子:

      unescape_xml("3 &lt; x &amp; x &gt; 9")
      # [1] "3 < x & x > 9"
      unescape_html("&euro; 2.99")
      # [1] "€ 2.99"
      

      【讨论】:

        【解决方案3】:

        虽然solution by Jeroen 可以完成这项工作,但它的缺点是它不是矢量化的,因此如果应用于大量字符时会很慢。此外,它仅适用于长度为 1 的字符向量,而对于更长的字符向量,必须使用 sapply。

        为了证明这一点,我首先创建了一个大字符向量:

        set.seed(123)
        strings <- c("abcd", "&amp; &apos; &gt;", "&amp;", "&euro; &lt;")
        many_strings <- sample(strings, 10000, replace = TRUE)
        

        并应用函数:

        unescape_html <- function(str) {
          xml2::xml_text(xml2::read_html(paste0("<x>", str, "</x>")))
        }
        
        system.time(res <- sapply(many_strings, unescape_html, USE.NAMES = FALSE))
        ##    user  system elapsed 
        ##   2.327   0.000   2.326 
        head(res)
        ## [1] "& ' >" "€ <"   "& ' >" "€ <"   "€ <"   "abcd" 
        

        如果将字符向量中的所有字符串组合成一个大字符串,这样read_html() 和xml_text() 只需要使用一次,就会快得多。然后可以使用strsplit() 轻松地再次分隔字符串:

        unescape_html2 <- function(str){
          html <- paste0("<x>", paste0(str, collapse = "#_|"), "</x>")
          parsed <- xml2::xml_text(xml2::read_html(html))
          strsplit(parsed, "#_|", fixed = TRUE)[[1]]
        }
        
        system.time(res2 <- unescape_html2(many_strings))
        ##    user  system elapsed 
        ##   0.011   0.000   0.010 
        identical(res, res2)
        ## [1] TRUE
        

        当然,您需要注意用于组合str 中各种字符串的字符串(在我的示例中为"#_|")不会出现在str 中的任何位置。否则,当最后再次拆分大字符串时,您将引入错误。

        【讨论】:

        • 很好的答案!您介意解释一下&lt;x&gt; 的用途吗?如果那不是正确的地方,我很高兴提出一个新问题。
        • 我只是让 Jeroen 的回答更有效率,基本想法是他的,不是我的。如果您尝试没有&lt;x&gt; 和&lt;/x&gt; 的代码,您会注意到它失败并出现错误。原因是read_html() 可以与包含 HTML 代码的字符串或 html 文件的路径一起使用。如果字符不包含 &lt; 或 &gt;(即不包含单个 html 标记),则该函数假定它正在使用路径并尝试读取当然不存在的文件。
        【解决方案4】:

        更新:此答案已过时。请根据新的xml2 pkg检查answer below。


        尝试以下方式:

        # load XML package
        library(XML)
        
        # Convenience function to convert html codes
        html2txt <- function(str) {
              xpathApply(htmlParse(str, asText=TRUE),
                         "//body//text()", 
                         xmlValue)[[1]] 
        }
        
        # html encoded string
        ( x <- paste("i", "s", "n", "&", "a", "p", "o", "s", ";", "t", sep = "") )
        [1] "isn&apos;t"
        
        # converted string
        html2txt(x)
        [1] "isn't"
        

        更新:编辑了 html2txt() 函数,使其适用于更多情况

        【讨论】:

        【解决方案5】:

        根据Stibu's的回答,我去对函数进行基准测试。

        # first create large vector as in Stibu's answer
        set.seed(123)
        strings <- c("abcd", "&amp; &apos; &gt;", "&amp;", "&euro; &lt;")
        many_strings <- sample(strings, 10000, replace = TRUE)
        
        # then benchmark the functions by Stibu and Jeroen
        bench::mark(
          textutils::HTMLdecode(many_strings),
          map_chr(many_strings, unescape_html),
          unescape_html2(many_strings)
        )
        
        # A tibble: 3 x 13
          expression                                min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory time 
          <bch:expr>                           <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list> <lis>
        1 textutils::HTMLdecode(many_strings)  855.02ms 855.02ms     1.17   329.18MB    10.5      1     9   855.02ms <chr … <Rpro… <bch…
        2 map_chr(many_strings, unescape_html)    1.09s    1.09s     0.919    6.79MB     5.51     1     6      1.09s <chr … <Rpro… <bch…
        3 unescape_html2(many_strings)           4.85ms   5.13ms   195.     581.48KB     0       98     0   503.63ms <chr … <Rpro… <bch…
        # … with 1 more variable: gc <list>
        Warning message:
        Some expressions had a GC in every iteration; so filtering is disabled. 
        

        在这里,我通过 purrr::map_chr 运算符对 Jeroen 的 unescape_html 函数进行矢量化。到目前为止,这只是证实了 Stibu 的说法,即unescape_html2 确实快了很多倍!它甚至比textutils::HTMLdecode 函数还要快。

        但我也发现xml 版本可能更快。

        unescape_xml2 <- function(str){
          html <- paste0("<x>", paste0(str, collapse = "#_|"), "</x>")
          parsed <- xml2::xml_text(xml2::read_xml(html))
          strsplit(parsed, "#_|", fixed = TRUE)[[1]]
        }
        

        但是,这个函数在处理many_strings 对象时会失败(可能是因为read_xml 无法读取欧元符号。所以我必须尝试不同的基准测试方法。

        library(tidyverse)
        library(rvest)
        
        entity_html <- read_html("https://dev.w3.org/html5/html-author/charref")
        entity_mapping <- entity_html %>% 
          html_node(css = "table") %>% 
          html_table() %>% 
          rename(text = X1,
                 named = X2,
                 hex = X3, 
                 dec = X4,
                 desc = X5) %>% 
          as_tibble
        s2 <- entity_mapping %>% pull(dec) # dec can be replaced by hex or named
        
        bench::mark(
          textutils::HTMLdecode(s2),
          map_chr(s2, unescape_xml),
          map_chr(s2, unescape_html),
          unescape_xml2(s2),
          unescape_html2(s2)
        )
        
        # A tibble: 5 x 13
          expression                      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory   time   gc    
          <bch:expr>                 <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list>   <list> <list>
        1 textutils::HTMLdecode(s2)   191.7ms  194.9ms      5.16    64.1MB    10.3      3     6      582ms <chr … <Rprofm… <bch:… <tibb…
        2 map_chr(s2, unescape_xml)    73.8ms   80.9ms     11.9   1006.9KB     5.12     7     3      586ms <chr … <Rprofm… <bch:… <tibb…
        3 map_chr(s2, unescape_html)  162.4ms  163.7ms      5.83  1006.9KB     5.83     3     3      514ms <chr … <Rprofm… <bch:… <tibb…
        4 unescape_xml2(s2)           459.2µs    473µs   2034.      37.9KB     2.00  1017     1      500ms <chr … <Rprofm… <bch:… <tibb…
        5 unescape_html2(s2)            590µs  607.5µs   1591.      37.9KB     2.00   796     1      500ms <chr … <Rprofm… <bch:… <tibb…
        Warning message:
        Some expressions had a GC in every iteration; so filtering is disabled. 
        

        我们也可以试试hex的。

        > bench::mark(
        +   # gsubreplace_mapping(s2, entity_mapping),
        +   # gsubreplace_local(s2),
        +   textutils::HTMLdecode(s3),
        +   map_chr(s3, unescape_xml),
        +   map_chr(s3, unescape_html),
        +   unescape_xml2(s3),
        +   unescape_html2(s3)
        + )
        
        # A tibble: 5 x 13
          expression                      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory   time   gc    
          <bch:expr>                 <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list>   <list> <list>
        1 textutils::HTMLdecode(s3)   204.2ms  212.3ms      4.72    64.1MB     7.87     3     5      636ms <chr … <Rprofm… <bch:… <tibb…
        2 map_chr(s3, unescape_xml)    76.4ms   80.2ms     11.8   1006.9KB     5.04     7     3      595ms <chr … <Rprofm… <bch:… <tibb…
        3 map_chr(s3, unescape_html)  164.6ms  165.3ms      5.80  1006.9KB     5.80     3     3      518ms <chr … <Rprofm… <bch:… <tibb…
        4 unescape_xml2(s3)           487.4µs  500.5µs   1929.      74.5KB     2.00   965     1      500ms <chr … <Rprofm… <bch:… <tibb…
        5 unescape_html2(s3)          611.1µs  627.7µs   1574.      40.4KB     0      788     0      501ms <chr … <Rprofm… <bch:… <tibb…
        Warning message:
        Some expressions had a GC in every iteration; so filtering is disabled. 
        

        这里xml 版本比html 版本更快。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-12-29
          • 1970-01-01
          • 1970-01-01
          • 2019-06-16
          • 1970-01-01
          • 2012-07-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多