R语言 从动态网站中抓取传单Map坐标

balp4ylt  于 2024-01-03  发布在  其他
关注(0)|答案(1)|浏览(96)

我试图从一个包含leafletMap(osm数据)的网站上抓取标记坐标。我一直在网上搜索答案,由于网站的动态特性,对解析的html的简单查询似乎是不够的。因此我一直在使用RSelenium。在查看html并使用ChatGPT之后,我已经走了这么远:

library(RSelenium)
library(rvest)
library(xml2)

remote_driver <- rsDriver(browser = "firefox",
                      chromever =  NULL,
                      verbose = FALSE,
                      port = 4445L)
remDr <- remote_driver$client

remDr$navigate("https://www.hejfish.com/d/1356-strobl-wasser-fliegenstrecke-traun")

# Scroll to the end of the page to trigger marker loading
remDr$executeScript("window.scrollTo(0, document.body.scrollHeight);")

map_markers <- remDr$findElements(using = "css", value = ".MapIcons__marker_icon___vDTMo")

字符串
ChatGPT建议我像这样提取坐标:

coordinates <- lapply(map_markers, function(marker) {
  lat <- marker$getElementAttribute("data-lat")$value
  lon <- marker$getElementAttribute("data-lon")$value
  c(lat = as.numeric(lat), lon = as.numeric(lon))
})


不幸的是,这不起作用:Error: attempt to apply non-function。我假设提取的元素中没有名为"data-lat""data-lon"的属性。然而,通过检查网站的html,我找不到任何看起来甚至模糊不清的标记代码中的坐标。受this帖子的启发,我还检查了网络选项卡,并能够找到边界框的坐标,但不是为这两个标记。其他posts谈到隐藏在脚本标记中的信息,等等。但这超出了我的能力。
任何帮助刮坐标将不胜感激!

thigvfpy

thigvfpy1#

这里有一种方法来解决这个问题,注意,它涉及一个API键,在这个例子中它被截断了,所以您需要自己提取它。
我会先在dev.tools的网络标签中使用一些搜索字符串。传单标记弹出窗口包括GoogleMap链接,这可能很方便:
x1c 0d1x的数据
链接本身很可能是由JavaScript生成的,但坐标值可能是按原样传输的;在这里太具体可能不起作用,所以让我们只搜索纬度48.235585。此外,确保所有请求都被捕获,即在需要时刷新页面。这将引导我们到api.hejfish.com/fisher/areas/1356/map API端点:



我们可以尝试打开该URL,但即使在同一个浏览器会话中,我们也会遇到错误403 - Zugriff verweigert。当检查请求头时,会设置几个会话cookie和额外的头,最明显的是X-Api-Key。只是为了检查我们是否可以自己复制该请求,让我们将其复制为cURL命令:

我们可以通过命令行使用它,或者尝试通过https://curlconverter.com/r/或类似的工具将其转换为R或Python,但让我们看看httr2::curl_translate()是否可以处理这个问题:

library(httr2)

# read copied cURL command from clipboard, ends up as a vector of lines;
# paste it back into a single string and feed to curl_translate():
clipr::read_clip() |>
  paste0(collapse = "\n") |> 
  curl_translate()
#> request("https://api.hejfish.com/fisher/areas/1356/map") %>% 
#>   req_headers(
#>     authority = "api.hejfish.com",
#>     accept = "application/json",
#>     `accept-language` = "en-GB,en;q=0.9,et-EE;q=0.8,et;q=0.7,en-US;q=0.6",
#>     origin = "https://www.hejfish.com",
#>     referer = "https://www.hejfish.com/",
#>     `sec-ch-ua` = "\"Not_A Brand\";v=8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120",
#>     `sec-ch-ua-mobile` = "?0",
#>     `sec-ch-ua-platform` = "\"Windows\"",
#>     `sec-fetch-dest` = "empty",
#>     `sec-fetch-mode` = "cors",
#>     `sec-fetch-site` = "same-site",
#>     `user-agent` = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
#>     `x-api-key` = "4O...",
#>     `x-locale` = "de_DE",
#>     `x-requested-with` = "XMLHttpRequest",
#>   ) %>% 
#>   req_perform()

字符串
看上去很结实!使用完整的API键,它实际上完成状态 200 OK。我们可以这样保留它,或者可能稍微修剪一下,为了处理JSON响应,我们可以使用httr2::resp_body_json()

library(httr2)
map_ <- 
  request("https://api.hejfish.com/fisher/areas/1356/map") |>
  req_headers(`x-api-key` = "4O...") |>
  req_perform() |>
  resp_body_json(simplifyVector = TRUE)

map_$data$locations
#>        lat      lng
#> 1 48.23559 14.29808
#> 2 48.24744 14.32493

相关问题