从动态网站抓取传单地图坐标

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

我正在尝试从包含

leaflet
地图(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帖子的启发,我还检查了网络选项卡,并能够找到边界框的坐标,但找不到两个标记的坐标。其他帖子谈论隐藏在脚本标签等中的信息,但这超出了我的能力。

任何有关抓取坐标的帮助将不胜感激!

r web-scraping leaflet openstreetmap rselenium
1个回答
0
投票

这是解决此问题的一种方法,请注意,它涉及一个在此示例中被截断的 API 密钥,因此您需要自己提取它。

我首先想出一些在开发的网络选项卡中使用的搜索字符串。工具。传单标记弹出窗口包括 Google 地图链接,这可能会很方便:

链接本身很可能是由 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(allow_non_interactive = T) |>
  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
© www.soinside.com 2019 - 2024. All rights reserved.