使用 R 抓取网站的 Power BI 仪表盘

5
我一直在尝试使用R爬取本地政府的Power BI仪表板,但似乎这是不可能的。我从Microsoft网站上读到,无法爬取Power BI仪表板,但我正在查阅几个论坛,显示这是可能的,但我卡在了一个循环中。
我正在尝试从此仪表板中爬取“邮政编码”选项卡数据。

https://app.powerbigov.us/view?r=eyJrIjoiZDFmN2ViMGEtNzQzMC00ZDU3LTkwZjUtOWU1N2RiZmJlOTYyIiwidCI6IjNiMTg1MTYzLTZjYTMtNDA2NS04NDAwLWNhNzJiM2Y3OWU2ZCJ9&pageName=ReportSectionb438b98829599a9276e2&pageName=ReportSectionb438b98829599a9276e2

我尝试了下面给出的代码中的几种“技巧”。
scc_webpage <- xml2::read_html("https://app.powerbigov.us/view?r=eyJrIjoiZDFmN2ViMGEtNzQzMC00ZDU3LTkwZjUtOWU1N2RiZmJlOTYyIiwidCI6IjNiMTg1MTYzLTZjYTMtNDA2NS04NDAwLWNhNzJiM2Y3OWU2ZCJ9&pageName=ReportSectionb438b98829599a9276e2&pageName=ReportSectionb438b98829599a9276e2")


# Attempt using xpath
scc_webpage %>% 
  rvest::html_nodes(xpath = '//*[@id="pvExplorationHost"]/div/div/exploration/div/explore-canvas-modern/div/div[2]/div/div[2]/div[2]/visual-container-repeat/visual-container-group/transform/div/div[2]/visual-container-modern[1]/transform/div/div[3]/div/visual-modern/div/div/div[2]/div[1]/div[4]/div/div/div[1]/div[1]') %>% 
  rvest::html_text()

# Attempt using div.<class>
scc_webpage %>% 
  rvest::html_nodes("div.pivotTableCellWrap cell-interactive tablixAlignRight ") %>% 
  rvest::html_text()

# Attempt using xpathSapply
query = '//*[@id="pvExplorationHost"]/div/div/exploration/div/explore-canvas-modern/div/div[2]/div/div[2]/div[2]/visual-container-repeat/visual-container-group/transform/div/div[2]/visual-container-modern[1]/transform/div/div[3]/div/visual-modern/div/div/div[2]/div[1]/div[4]/div/div/div[1]/div[1]'
XML::xpathSApply(xml, query, xmlValue)

scc_webpage %>% 
  html_nodes("ui-view")

但是当我使用xpath获取
类和id时,总是得到一个输出,显示为character(0),甚至在尝试通过html_nodes进行操作时也会出现{xml_nodeset (0)}。奇怪的是,当我执行以下操作时,它不会显示tableau数据的整个html:
scc_webpage %>% 
  html_nodes("div")

这将是输出结果,留下我需要的空白块:
{xml_nodeset (2)}
[1] <div id="pbi-loading"><svg version="1.1" class="pulsing-svg-item" xmlns="http://www.w3.org/2000/svg" xmlns:xlink ...
[2] <div id="pbiAppPlaceHolder">\r\n        <ui-view></ui-view><root></root>\n</div>

我猜问题可能是因为数字在一系列嵌套的
属性中?
我试图获取的主要数据是表格中显示的邮政编码确诊病例% 总病例死亡人数% 总死亡人数的数字。
如果这在R中可行,或者可能使用Selenium在Python中完成,任何帮助都将不胜感激!
1个回答

7

问题在于您想要分析的网站依赖于JavaScript来运行和获取内容。在这种情况下,httr::GET 对您没有帮助。
然而,由于手动操作也不是一个选择,我们可以使用Selenium。

以下代码可以实现您想要的功能:

library(dplyr)
library(purrr)
library(readr)

library(wdman)
library(RSelenium)
library(xml2)
library(selectr)

# using wdman to start a selenium server
selServ <- selenium(
  port = 4444L,
  version = 'latest',
  chromever = '84.0.4147.30', # set this to a chrome version that's available on your machine
)

# using RSelenium to start chrome on the selenium server
remDr <- remoteDriver(
  remoteServerAddr = 'localhost',
  port = 4444L,
  browserName = 'chrome'
)

# open a new Tab on Chrome
remDr$open()

# navigate to the site you wish to analyze
report_url <- "https://app.powerbigov.us/view?r=eyJrIjoiZDFmN2ViMGEtNzQzMC00ZDU3LTkwZjUtOWU1N2RiZmJlOTYyIiwidCI6IjNiMTg1MTYzLTZjYTMtNDA2NS04NDAwLWNhNzJiM2Y3OWU2ZCJ9&pageName=ReportSectionb438b98829599a9276e2&pageName=ReportSectionb438b98829599a9276e2"
remDr$navigate(report_url)

# find and click the button leading to the Zip Code data
zipCodeBtn <- remDr$findElement('.//button[descendant::span[text()="Zip Code"]]', using="xpath")
zipCodeBtn$clickElement()

# fetch the site source in XML
zipcode_data_table <- read_html(remDr$getPageSource()[[1]]) %>%
  querySelector("div.pivotTable")

现在我们已经把页面源代码读入了R中,这可能是你在开始爬取任务时所想到的。
从这里开始,一切都很顺利,只需将该xml转换为可用的表格即可:

col_headers <- zipcode_data_table %>%
  querySelectorAll("div.columnHeaders div.pivotTableCellWrap") %>%
  map_chr(xml_text)

rownames <- zipcode_data_table %>%
  querySelectorAll("div.rowHeaders div.pivotTableCellWrap") %>%
  map_chr(xml_text)

zipcode_data <- zipcode_data_table %>%
  querySelectorAll("div.bodyCells div.pivotTableCellWrap") %>%
  map(xml_parent) %>%
  unique() %>%
  map(~ .x %>% querySelectorAll("div.pivotTableCellWrap") %>% map_chr(xml_text)) %>%
  setNames(col_headers) %>%
  bind_cols()

# tadaa
df_final <- tibble(zipcode = rownames, zipcode_data) %>%
  type_convert(trim_ws = T, na = c(""))

生成的数据框如下所示:
> df_final
# A tibble: 15 x 5
   zipcode `Confirmed Cases ` `% of Total Cases ` `Deaths ` `% of Total Deaths `
   <chr>                <dbl> <chr>                   <dbl> <chr>               
 1 63301                 1549 17.53%                     40 28.99%              
 2 63366                 1364 15.44%                     38 27.54%              
 3 63303                 1160 13.13%                     21 15.22%              
 4 63385                 1091 12.35%                     12 8.70%               
 5 63304                 1046 11.84%                      3 2.17%               
 6 63368                  896 10.14%                     12 8.70%               
 7 63367                  882 9.98%                       9 6.52%               
 8                        534 6.04%                       1 0.72%               
 9 63348                  105 1.19%                       0 0.00%               
10 63341                   84 0.95%                       1 0.72%               
11 63332                   64 0.72%                       0 0.00%               
12 63373                   25 0.28%                       1 0.72%               
13 63386                   17 0.19%                       0 0.00%               
14 63357                   13 0.15%                       0 0.00%               
15 63376                    5 0.06%                       0 0.00%

1
你可以选择使用其他浏览器,包括 Firefox 或无头选项来完成这个任务。wdman 也许可以帮助你下载所需的所有二进制文件。不过这需要你自己去研究,因为我只有使用 Chrome 的经验。 - alex_jwb90
1
@Doughey - 对于端口问题,请使用以下命令:install.packages('netstat'),然后port = free_port()应该可以工作,这样您就不必每次都输入新的端口。 - Kendal
2
1)如果API原始数据可用,您将不需要使用Selenium。2)或者,像OP一样,您可以检查呈现的仪表板以找到感兴趣的部分并进行抓取(提示:进入页面检查器的元素选项卡,按Ctrl + F查找您要在页面上隔离的文本,然后右键单击该元素并选择“复制选择器”。您可以将其放入R的querySelector("<HERE>")中,就像上面一样,在您的R代码中提取那个确切的元素)。对于页面底部的KPI,这将起作用... - alex_jwb90
1
2.5) 对于像条形图标签这样的值,您可能希望稍微抽象一下特定选择器,并使用 querySelectorAll 获取所有感兴趣的节点。您可以使用 xpath 和 css 选择器来执行类似于 read_html(remDr$getPageSource()[[1]]) %>% xml_find_first("//div[contains(@aria-label,\"Vaccine doses administered by Week\")]") %>% querySelectorAll("g.labelGraphicsContext > text.label") %>% xml_text() 的操作,该操作利用屏幕阅读器标签找到正确的图表,然后深入挖掘以获取标签文本节点的值。 - alex_jwb90
1
如果有人再次遇到这个问题,我认为值得一提的是,如果不滚动获取页面源代码,将会产生有限数量的匹配项(我大约获得了100个),这并不能涵盖大型表格。请查看我的解决方案:https://dev59.com/BsTsa4cB1Zd3GeqPJNtd#73130938 - dcsuka
显示剩余14条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接