rvest 网页内容抓取问题/汽车交易网站
rvest web content scraping issue / car trading website
问题
我想购买网站(汽车销售平台)的特定部分。
坦率地说,CSS 太令人困惑了,我无法自己找出问题所在。
#### scraping the website www.otomoto.pl with used cars #####
baseURL_otomoto = "https://www.otomoto.pl/osobowe/?page="
i <- 1
for ( i in 1:7000 )
{
link = paste0(baseURL_otomoto,i)
out = read_html(link)
print(i)
print(link)
### building year
build_year = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[1]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
mileage = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[2]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
volume = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[3]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
fuel_type = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[4]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
price = html_nodes(out, xpath = '//div[@class="offer-item__price"]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
link = html_nodes(out, xpath = '//div[@class="offer-item__title"]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
offer_details = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
任何猜测可能是这种行为的原因?
PS#1.
如何立即将所分析网站上提供的所有 build_type、里程和 fuel_type 数据作为 data.frame 归属?使用 classes (xpath = '//div[@class=...) 在我的情况下不起作用
PS#2.
我想使用 f.i 获取实际报价的详细信息。
gear_type = html_nodes(out, xpath = '//*[@id="parameters"]/ul[1]/li[10]/div') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
参数
- in ul[a] 用于 (1:2) &
- in li[b] 是 b in (1:12)
不幸的是,这个概念失败了,因为生成的数据框是空的。猜猜为什么?
首先,了解 CSS 选择器和 XPath。您的选择器非常长且极其脆弱(仅仅两周后,其中一些对我根本不起作用)。例如,而不是:
html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[1]') %>%
html_text()
你可以这样写:
html_nodes(out, css="[data-code=year]") %>% html_text()
其次,阅读您使用的库的文档。 str_replace_all
pattern 可以是正则表达式,这样可以节省一次调用(使用 str_replace_all("[\n\r]", "")
而不是 str_replace_all("\n","") %>% str_replace_all("\r","")
)。 html_text
可以为您进行文本裁剪,也就是说根本不需要str_trim()
。
第三,如果你复制粘贴一些代码,退一步想想功能是否不是更好的解决方案;通常它会。在你的情况下,就我个人而言,我可能会跳过 str_replace_all
调用直到数据清理步骤,那时我会在 data.frame 上调用它们来保存整个报废数据。
要从您的数据创建 data.frame,请使用列名称和内容调用 data.frame()
函数,如下所示:
data.frame(build_year = build_year,
mileage = mileage,
volume = volume,
fuel_type = fuel_type,
price = price,
link = link,
offer_details = offer_details)
或者您可以仅使用一列初始化 data.frame,然后添加更多向量作为列:
output_df <- data.frame(build_year = html_nodes(out, css="[data-code=year]") %>% html_text(TRUE))
output_df$volume <- html_nodes(out, css="[data-code=engine_capacity]") %>%
html_text(TRUE)
最后,您应该注意 data.frame 列的长度必须相同,而您废弃的某些数据是可选的。在写这个答案的时候,我几乎没有没有引擎容量和报价描述的报价。您必须连续使用两个 html_nodes
调用(因为单个 CSS 选择器将无法匹配不存在的内容)。但即便如此,html_nodes
仍会悄悄丢弃丢失的数据。这可以通过管道 html_nodes
输出到 html_node
调用来解决:
current_df$volume = out %>% html_nodes("ul.offer-item__params") %>%
html_node("[data-code=engine_capacity]") %>%
html_text(TRUE)
我的循环内部方法的最终版本如下。只需确保在调用它之前初始化空 data.frame 并将当前迭代的输出与最终数据帧合并(例如使用 rbind
),否则每次迭代都会覆盖前一次的结果。或者您可以使用 do.call(rbind, lapply())
,这是用于此类任务的惯用 R。
附带说明一下,当抓取大量快速变化的数据时,请考虑解耦数据下载和数据处理步骤。想象一下,有一些您没有考虑到的极端情况会导致 R 终止。如果这种情况出现在您的迭代过程中,您将如何处理?您在一个页面上停留的时间越长,您介绍的重复项就越多(因为会出现更多的报价,而现有的报价会被推到更多页面上),您错过的报价也会越多(因为销售已经结束,报价永远消失)。
current_df <- data.frame(build_year = html_nodes(out, css="[data-code=year]") %>% html_text(TRUE))
current_df$mileage = html_nodes(out, css="[data-code=mileage]") %>%
html_text(TRUE)
current_df$volume = out %>% html_nodes("ul.offer-item__params") %>%
html_node("[data-code=engine_capacity]") %>%
html_text(TRUE)
current_df$fuel_type = html_nodes(out, css="[data-code=fuel_type]") %>%
html_text(TRUE)
current_df$price = out %>% html_nodes(xpath="//div[@class='offer-price']//span[contains(@class, 'number')]") %>%
html_text(TRUE)
current_df$link = out %>% html_nodes(css = "div.offer-item__title h2 > a") %>%
html_text(TRUE) %>%
str_replace_all("[\n\r]", "")
current_df$offer_details = out %>% html_nodes("div.offer-item__title") %>%
html_node("h3") %>%
html_text(TRUE)
问题
我想购买网站(汽车销售平台)的特定部分。
坦率地说,CSS 太令人困惑了,我无法自己找出问题所在。
#### scraping the website www.otomoto.pl with used cars #####
baseURL_otomoto = "https://www.otomoto.pl/osobowe/?page="
i <- 1
for ( i in 1:7000 )
{
link = paste0(baseURL_otomoto,i)
out = read_html(link)
print(i)
print(link)
### building year
build_year = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[1]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
mileage = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[2]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
volume = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[3]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
fuel_type = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[4]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
price = html_nodes(out, xpath = '//div[@class="offer-item__price"]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
link = html_nodes(out, xpath = '//div[@class="offer-item__title"]') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
offer_details = html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
任何猜测可能是这种行为的原因?
PS#1.
如何立即将所分析网站上提供的所有 build_type、里程和 fuel_type 数据作为 data.frame 归属?使用 classes (xpath = '//div[@class=...) 在我的情况下不起作用
PS#2.
我想使用 f.i 获取实际报价的详细信息。
gear_type = html_nodes(out, xpath = '//*[@id="parameters"]/ul[1]/li[10]/div') %>%
html_text() %>%
str_replace_all("\n","") %>%
str_replace_all("\r","") %>%
str_trim()
参数
- in ul[a] 用于 (1:2) &
- in li[b] 是 b in (1:12)
不幸的是,这个概念失败了,因为生成的数据框是空的。猜猜为什么?
首先,了解 CSS 选择器和 XPath。您的选择器非常长且极其脆弱(仅仅两周后,其中一些对我根本不起作用)。例如,而不是:
html_nodes(out, xpath = '//*[@id="body-container"]/div[2]/div[1]/div/div[6]/div[2]/article[1]/div[2]/div[3]/ul/li[1]') %>%
html_text()
你可以这样写:
html_nodes(out, css="[data-code=year]") %>% html_text()
其次,阅读您使用的库的文档。 str_replace_all
pattern 可以是正则表达式,这样可以节省一次调用(使用 str_replace_all("[\n\r]", "")
而不是 str_replace_all("\n","") %>% str_replace_all("\r","")
)。 html_text
可以为您进行文本裁剪,也就是说根本不需要str_trim()
。
第三,如果你复制粘贴一些代码,退一步想想功能是否不是更好的解决方案;通常它会。在你的情况下,就我个人而言,我可能会跳过 str_replace_all
调用直到数据清理步骤,那时我会在 data.frame 上调用它们来保存整个报废数据。
要从您的数据创建 data.frame,请使用列名称和内容调用 data.frame()
函数,如下所示:
data.frame(build_year = build_year,
mileage = mileage,
volume = volume,
fuel_type = fuel_type,
price = price,
link = link,
offer_details = offer_details)
或者您可以仅使用一列初始化 data.frame,然后添加更多向量作为列:
output_df <- data.frame(build_year = html_nodes(out, css="[data-code=year]") %>% html_text(TRUE))
output_df$volume <- html_nodes(out, css="[data-code=engine_capacity]") %>%
html_text(TRUE)
最后,您应该注意 data.frame 列的长度必须相同,而您废弃的某些数据是可选的。在写这个答案的时候,我几乎没有没有引擎容量和报价描述的报价。您必须连续使用两个 html_nodes
调用(因为单个 CSS 选择器将无法匹配不存在的内容)。但即便如此,html_nodes
仍会悄悄丢弃丢失的数据。这可以通过管道 html_nodes
输出到 html_node
调用来解决:
current_df$volume = out %>% html_nodes("ul.offer-item__params") %>%
html_node("[data-code=engine_capacity]") %>%
html_text(TRUE)
我的循环内部方法的最终版本如下。只需确保在调用它之前初始化空 data.frame 并将当前迭代的输出与最终数据帧合并(例如使用 rbind
),否则每次迭代都会覆盖前一次的结果。或者您可以使用 do.call(rbind, lapply())
,这是用于此类任务的惯用 R。
附带说明一下,当抓取大量快速变化的数据时,请考虑解耦数据下载和数据处理步骤。想象一下,有一些您没有考虑到的极端情况会导致 R 终止。如果这种情况出现在您的迭代过程中,您将如何处理?您在一个页面上停留的时间越长,您介绍的重复项就越多(因为会出现更多的报价,而现有的报价会被推到更多页面上),您错过的报价也会越多(因为销售已经结束,报价永远消失)。
current_df <- data.frame(build_year = html_nodes(out, css="[data-code=year]") %>% html_text(TRUE))
current_df$mileage = html_nodes(out, css="[data-code=mileage]") %>%
html_text(TRUE)
current_df$volume = out %>% html_nodes("ul.offer-item__params") %>%
html_node("[data-code=engine_capacity]") %>%
html_text(TRUE)
current_df$fuel_type = html_nodes(out, css="[data-code=fuel_type]") %>%
html_text(TRUE)
current_df$price = out %>% html_nodes(xpath="//div[@class='offer-price']//span[contains(@class, 'number')]") %>%
html_text(TRUE)
current_df$link = out %>% html_nodes(css = "div.offer-item__title h2 > a") %>%
html_text(TRUE) %>%
str_replace_all("[\n\r]", "")
current_df$offer_details = out %>% html_nodes("div.offer-item__title") %>%
html_node("h3") %>%
html_text(TRUE)