因此,我对网络爬虫这个话题还很陌生。我正在尝试查找以下页面的html代码包含的所有超链接: https://www.exito.com/mercado/lacteos-huevos-y-refrigerados/leches
这就是我尝试过的:
<div id="hotjob">
<hotjob-component :projects="{{ json_encode($Projects) }}"></hotjob-component>
</div>
结果仅包含6个链接,但是仅通过查看页面,您可以看到还有更多的超链接。
例如,第一张图片后面的代码类似:url <- "https://www.exito.com/mercado/lacteos-huevos-y-refrigerados/leches"
webpage <- read_html(url)
html_attr(html_nodes(webpage, "a"), "href")
...
我在做什么错了?
答案 0 :(得分:1)
由于html / xml解析器看不到网站的该部分,因此您将无法获得a
标签。这是因为,如果您选择网站的另一部分,它是网站的动态部分,因此会发生变化;网站的唯一“静态”部分是顶部标题,这就是为什么您只获得6个a
标签的原因:标题中有6个a
标签。
为此,我们需要模仿浏览器的行为(Firefox,Chrome等),进入网站(请注意,我们不是以html / xml解析器的形式进入网站,而是以“用户”),然后从那里阅读html / xml源代码。
为此,我们需要R包RSelenium
。确保install与docker
一起正确使用,因为如果没有它,下面的代码将无法正常工作。
安装RSelenium
和docker
后,从终端运行docker run -d -p 4445:4444 selenium/standalone-firefox:2.53.1
(如果在Linux上,则可以在终端上运行;如果在Windows上,则必须下载docker终端,在那里运行)。之后,您都准备好再现下面的代码。
我们需要从下图访问第5个div
标签:
如您所见,第5个div
标记内部有三个点(...
),表示内部有代码:这正是底部 all 的所有位置网站的名称(包括您所关注的a
标签)。如果我们尝试使用rvest
或xml2
访问此第5个标签,则找不到任何内容:
library(xml2)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
lnk <- "https://www.exito.com/mercado/lacteos-huevos-y-refrigerados/leches?page=2"
# Note how the 5th div element is empty and it should contain the lower
# part of the website
lnk %>%
read_html() %>%
xml_find_all("//div[@class='flex flex-grow-1 w-100 flex-column']") %>%
xml_children()
#> {xml_nodeset (6)}
#> [1] <div class=""></div>\n
#> [2] <div class=""></div>\n
#> [3] <div class=""></div>\n
#> [4] <div class=""></div>\n
#> [5] <div class=""></div>\n
#> [6] <div class=""></div>
请注意,第5个div
标记内部没有任何代码。一个简单的html / xml解析器将无法捕获它。
我们需要使用RSelenium
。正确安装所有组件后,我们需要设置一个“远程驱动程序”,将其打开并导航到该网站。所有这些步骤只是为了确保我们以浏览器的“正常”用户身份进入网站。这将确保我们可以访问进入网站时实际看到的渲染代码。以下是进入网站并构建链接的详细步骤。
# Make sure you install docker correctly: https://docs.ropensci.org/RSelenium/articles/docker.html
library(RSelenium)
# After installing docker and before running the code, make sure you run
# the rselenium docker image: docker run -d -p 4445:4444 selenium/standalone-firefox:2.53.1
# Now, set up your remote driver
remDr <- remoteDriver(
remoteServerAddr = "localhost",
port = 4445L,
browserName = "firefox"
)
# Initiate the driver
remDr$open(silent = TRUE)
# Navigate to the exito.com website
remDr$navigate(lnk)
prod_links <-
# Get the html source code
remDr$getPageSource()[[1]] %>%
read_html() %>%
# Find all a tags which have a certain class
# I searched for this tag manually on the website code and saw that all products
# had an a tag that shared the same class
xml_find_all("//a[@class='vtex-product-summary-2-x-clearLink h-100 flex flex-column']") %>%
# Extract the href attribute
xml_attr("href") %>%
paste0("https://www.exito.com", .)
prod_links
#> [1] "https://www.exito.com/leche-semidescremada-deslactosada-en-bolsa-x-900-ml-145711/p"
#> [2] "https://www.exito.com/leche-entera-en-bolsa-x-900-ml-145704/p"
#> [3] "https://www.exito.com/leche-entera-sixpack-x-1300-ml-cu-987433/p"
#> [4] "https://www.exito.com/leche-deslactosada-en-caja-x-1-litro-878473/p"
#> [5] "https://www.exito.com/leche-polvo-deslactos-semidesc-764522/p"
#> [6] "https://www.exito.com/leche-slight-sixpack-en-caja-x-1050-ml-cu-663528/p"
#> [7] "https://www.exito.com/leche-semidescremada-sixpack-en-caja-x-1050-ml-cu-663526/p"
#> [8] "https://www.exito.com/leche-descremada-sixpack-x-1300-ml-cu-563046/p"
#> [9] "https://www.exito.com/of-leche-deslact-pag-5-lleve-6-439057/p"
#> [10] "https://www.exito.com/sixpack-de-leche-descremada-x-1100-ml-cu-414454/p"
#> [11] "https://www.exito.com/leche-en-polvo-klim-fortificada-360g-239085/p"
#> [12] "https://www.exito.com/leche-deslactosada-descremada-en-caja-x-1-litro-238291/p"
#> [13] "https://www.exito.com/leche-deslactosada-en-caja-x-1-litro-157334/p"
#> [14] "https://www.exito.com/leche-entera-larga-vida-en-caja-x-1-litro-157332/p"
#> [15] "https://www.exito.com/leche-en-polvo-klim-fortificada-780g-138121/p"
#> [16] "https://www.exito.com/leche-entera-en-bolsa-x-1-litro-125079/p"
#> [17] "https://www.exito.com/leche-entera-en-bolsa-sixpack-x-11-litros-cu-59651/p"
#> [18] "https://www.exito.com/leche-deslactosada-descremada-sixpack-x-11-litros-cu-22049/p"
#> [19] "https://www.exito.com/leche-entera-en-polvo-instantanea-x-760-gr-835923/p"
#> [20] "https://www.exito.com/of-alpin-cja-cho-pag9-llev12/p"
希望这能回答您的问题
答案 1 :(得分:1)
点击页面上的 Mostrarmás时,您可以在网络标签中观察到的GraphQL query动态返回数据,包括网址。这就是为什么您的初始查询中没有内容的原因-尚未请求。
XHR以获得产品信息
开发工具的网络标签中的相关XHR:
url查询字符串的实际查询参数:
您可以删除大部分请求信息。您需要做的是extensions
参数。更具体地说,您需要提供sha256Hash
中与variables
键相关的persistedQuery
和base64编码的字符串值。
可以从至少一个主要控制设置的js文件中提取适当的哈希。您可以使用的示例文件是:
https://exitocol.vtexassets.com/_v/public/assets/v1/published/bundle/public/react/asset.min.js?v=1&files=vtex.store-resources@0.38.0,OrderFormContext,Mutations,Queries,PWAContext&files=exitocol.store-components@0.0.2,common,11,3,SearchBar&files=vtex.responsive-values@0.2.0,common,useResponsiveValues&files=vtex.slider@0.7.3,common,0,Dots,Slide,Slider,SliderContainer&files=exito.components@4.0.7,common,0,1,3,4&workspace=master
。
查询哈希可以从xhr请求到此uri的响应文本中进行正则表达式处理。对正则表达式的解释是here,并且第一个匹配就足够了:
要在R中使用stringr
进行申请,您将需要一些额外的转义符,例如\s
成为\\s
。
The Base64 encoded product query
您可以使用相应的库生成base64编码的字符串,例如caTools
软件包中似乎有一个base64encode
R函数。
编码后的字符串看起来像(取决于页面/结果批次):
eyJ3aXRoRmFjZXRzIjpmYWxzZSwiaGlkZVVuYXZhaWxhYmxlSXRlbXMiOmZhbHNlLCJza3VzRmlsdGVyIjoiQUxMX0FWQUlMQUJMRSIsInF1ZXJ5IjoiMTQ4IiwibWFwIjoicHJvZHVjdENsdXN0ZXJJZHMiLCJvcmRlckJ5IjoiT3JkZXJCeVRvcFNhbGVERVNDIiwiZnJvbSI6MjAsInRvIjozOX0=
已解码:
{"withFacets":false,"hideUnavailableItems":false,"skusFilter":"ALL_AVAILABLE","query":"148","map":"productClusterIds","orderBy":"OrderByTopSaleDESC","from":20,"to":39}
from
和to
参数是二十个批次的产品结果批次的偏移量。因此,您可以编写函数,以返回适当的sha256哈希并发送对产品信息的后续请求,并在其中使用适当的库对上面的字符串进行base64编码,然后更改from
和to
{{1 }} 按要求。可能还有其他人(有戏!)。
xhr响应:
响应为json,因此您可能需要一个json库(例如jsonlite)来处理结果(更新:似乎您不使用R和params
)。按照Python示例,您可以从嵌套在httr
中的词典列表中提取链接,其中result['data']['products']
是使用result
从xhr检索到from
的json对象。
示例:
使用R和Python的示例如下所示(注:我不太熟悉R)。上面的代码与语言无关。
请记住,当我提取网址时,返回的json有很多信息,包括产品标题,价格,图像信息等。
示例输出:
待办事项:
R(快速入门):
params
Py:
library(purrr)
library(stringr)
library(caTools)
library(httr)
get_links <- function(sha, start, end){
string = paste0('{"withFacets":false,"hideUnavailableItems":false,"skusFilter":"ALL_AVAILABLE","query":"148","map":"productClusterIds","orderBy":"OrderByTopSaleDESC","from":' , start , ',"to":' , end , '}')
base64encoded <- caTools::base64encode(string)
params = list(
'extensions' = paste0('{"persistedQuery":{"version":1,"sha256Hash":"' , sha , '","sender":"vtex.store-resources@0.x","provider":"vtex.search-graphql@0.x"},"variables":"' , base64encoded , '"}')
)
product_info <- content(httr::GET(url = 'https://www.exito.com/_v/segment/graphql/v1', query = params))$data$products
links <- map(product_info, ~{
.x %>% .$link
})
return(links)
}
start <- '0'
end <- '19'
sha <- httr::GET('https://exitocol.vtexassets.com/_v/public/assets/v1/published/bundle/public/react/asset.min.js?v=1&files=vtex.store-resources@0.38.0,OrderFormContext,Mutations,Queries,PWAContext&files=exitocol.store-components@0.0.2,common,11,3,SearchBar&files=vtex.responsive-values@0.2.0,common,useResponsiveValues&files=vtex.slider@0.7.3,common,0,Dots,Slide,Slider,SliderContainer&files=exito.components@4.0.7,common,0,1,3,4&workspace=master') %>%
content(., as = "text")%>% str_match(.,'query\\s+productSearch.*?hash:\\s+"(.*?)"')%>% .[[2]]
links <- get_links(sha, start, end)
print(links)