You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby爬虫提取BBC GoodFood食谱准备时间的问题求助

精准提取BBC Good Food食谱卡片中的准备时间

我正在构建一个Ruby爬虫解析程序,目标网站为https://www.bbcgoodfood.com/search?q=banana。该网站的食谱卡片中,准备时间与难度信息使用相同类名且无ID,导致提取时会同时获取到无关内容。我仅需提取包含“mins”的准备时间文本,但当前代码会同时获取到难度(如Easy、Hard等),需要解决如何精准提取准备时间的问题。

相关HTML片段

<article class="card text-align-left card--horizontal card--inline card--with-borders" data-item-id="233531" data-item-name="Banana bread" data-variant="horizontal" data-index="1" data-component="Card" data-item-type="recipe" data-subscription-experience="default">  <div class="card__section card__media">    <div class="d-block p-relative">      <div class="p-absolute card__media-buttons pa-xs"></div>    </div>    <a class="link d-block p-relative card__image-container" href="/recipes/brilliant-banana-loaf" data-component="Link" aria-label="View Banana bread">      <div class="card__overlay-icon p-absolute d-grid justify-content-center align-content-center card__overlay-icon--horizontal"><i class="icon" style="width:24px;min-width:24px;height:24px;min-height:24px;animation-duration:1000ms"><svg class="icon__svg" aria-hidden="true" focusable="false" style="color:rgba(255, 255, 255, 1);fill:rgba(255, 255, 255, 1)"><use xlink:href="/static/icons/base/sprite-maps/media-f9e9817ead29e7dce81069945ff5bbe8.svg#play"></use></svg></i></div>      <div      class="image chromatic-ignore card__img image--fluid image--scaled-up">        <div class="image__container">          <picture class="image__picture" width="93" height="84.51190476190476">            <source type="image/webp" sizes="93px" srcset="https://images.immediate.co.uk/production/volatile/sites/30/2020/06/recipe-image-legacy-id-1273522_8-a6b9246.jpg?quality=90&amp;webp=true&amp;resize=93,85 93w,https://images.immediate.co.uk/production/volatile/sites/30/2020/06/recipe-image-legacy-id-1273522_8-a6b9246.jpg?quality=90&amp;webp=true&amp;resize=186,169 186w,https://images.immediate.co.uk/production/volatile/sites/30/2020/06/recipe-image-legacy-id-1273522_8-a6b9246.jpg?quality=90&amp;webp=true&amp;resize=279,254 279w,https://images.immediate.co.uk/production/volatile/sites/30/2020/06/recipe-image-legacy-id-1273522_8-a6b9246.jpg?quality=90&amp;webp=true&amp;resize=372,338 372w">            <source sizes="93px" srcset="https://images.immediate.co.uk/production/volatile/sites/30/2020/06/recipe-image-legacy-id-1273522_8-a6b9246.jpg?quality=90&amp;resize=93,85 93w,https://images.immediate.co.uk/production/volatile/sites/30/2020/06/recipe-image-legacy-id-1273522_8-a6b9246.jpg?quality=90&amp;resize=186,169 186w,https://images.immediate.co.uk/production/volatile/sites/30/2020/06/recipe-image-legacy-id-1273522_8-a6b9246.jpg?quality=90&amp;resize=279,254 279w,https://images.immediate.co.uk/production/volatile/sites/30/2020/06/recipe-image-legacy-id-1273522_8-a6b9246.jpg?quality=90&amp;resize=372,338 372w"><img class="image__img" src="https://images.immediate.co.uk/production/volatile/sites/30/2020/06/recipe-image-legacy-id-1273522_8-a6b9246.jpg?quality=90&amp;resize=93,85" alt="Sliced banana bread topped with drizzle and banana chips on a serving plate"            data-item-name="Sliced banana bread topped with drizzle and banana chips on a serving plate" title="Banana bread" style="aspect-ratio:1 / 0.9087301587301587;object-fit:cover" loading="lazy" width="93" height="84.51190476190476"></picture>        </div>  </div>  </a>  </div>  <div class="card__section card__content"><a class="link d-block" href="/recipes/brilliant-banana-loaf" data-component="Link"><h2 class="heading-4" style="color:inherit">Banana bread</h2></a>    <div class="card__rating rating">      <div class="rating__values rating__values--no-rate" aria-label="rating">        <div class="rating__stars d-flex"><i class="rating__icon d-inline-flex icon" style="width:15px;min-width:15px;height:15px;min-height:15px;animation-duration:1000ms"><svg class="icon__svg" aria-hidden="true" focusable="false" style="color:inherit;fill:inherit"><use xlink:href="/static/icons/base/sprite-maps/stars-f9e9817ead29e7dce81069945ff5bbe8.svg#star-fill"></use></svg></i>          <i          class="rating__icon d-inline-flex icon" style="width:15px;min-width:15px;height:15px;min-height:15px;animation-duration:1000ms">            <svg class="icon__svg" aria-hidden="true" focusable="false" style="color:inherit;fill:inherit">              <use xlink:href="/static/icons/base/sprite-maps/stars-f9e9817ead29e7dce81069945ff5bbe8.svg#star-fill"></use>            </svg>            </i><i class="rating__icon d-inline-flex icon" style="width:15px;min-width:15px;height:15px;min-height:15px;animation-duration:1000ms"><svg class="icon__svg" aria-hidden="true" focusable="false" style="color:inherit;fill:inherit"><use xlink:href="/static/icons/base/sprite-maps/stars-f9e9817ead29e7dce81069945ff5bbe8.svg#star-fill"></use></svg></i>            <i            class="rating__icon d-inline-flex icon" style="width:15px;min-width:15px;height:15px;min-height:15px;animation-duration:1000ms">              <svg class="icon__svg" aria-hidden="true" focusable="false" style="color:inherit;fill:inherit">                <use xlink:href="/static/icons/base/sprite-maps/stars-f9e9817ead29e7dce81069945ff5bbe8.svg#star-fill"></use>              </svg>              </i><i class="rating__icon d-inline-flex icon" style="width:15px;min-width:15px;height:15px;min-height:15px;animation-duration:1000ms"><svg class="icon__svg" aria-hidden="true" focusable="false" style="color:inherit;fill:inherit"><use xlink:href="/static/icons/base/sprite-maps/stars-f9e9817ead29e7dce81069945ff5bbe8.svg#star-fill"></use></svg></i></div>        <span        class="sr-only">A star rating of 4.5 out of 5.</span><span class="rating__count-text body-copy-small">1883 ratings</span></div>    </div>  </div>  <div class="card__section card__footer">    <ul style="height:22px" class="terms-icons-list d-flex card__footer-item terms-icons-list--single-row list list--horizontal">      <li class="d-flex pb-xs pr-md list-item">        <div class="icon-stack mr-xxs icon-stack--vertical-align" style="height:22px;width:22px">          <div class="icon-stack__icon" style="z-index:2"><i class="card__term-icon icon" style="width:22px;min-width:22px;height:22px;min-height:22px;animation-duration:1000ms"><svg class="icon__svg" aria-hidden="true" focusable="false" style="color:inherit;fill:inherit"><use xlink:href="/static/icons/base/sprite-maps/core-f9e9817ead29e7dce81069945ff5bbe8.svg#clock-hands"></use></svg></i></div>          <div          class="icon-stack__icon" style="z-index:1"><i class="card__term-icon-base icon" style="width:22px;min-width:22px;height:22px;min-height:22px;animation-duration:1000ms"><svg class="icon__svg" aria-hidden="true" focusable="false" style="color:inherit;fill:inherit"><use xlink:href="/static/icons/base/sprite-maps/shapes-f9e9817ead29e7dce81069945ff5bbe8.svg#circle"></use></svg></i></div>  </div><span class="terms-icons-list__text d-flex align-items-center" style="height:22px">1 hr 5 mins</span></li>  <li class="d-flex pb-xs list-item">    <div class="icon-stack mr-xxs icon-stack--vertical-align" style="height:22px;width:22px">      <div class="icon-stack__icon" style="z-index:2"><i class="card__term-icon icon" style="width:14px;min-width:14px;height:14px;min-height:14px;animation-duration:1000ms"><svg class="icon__svg" aria-hidden="true" focusable="false" style="color:inherit;fill:inherit"><use xlink:href="/static/icons/base/sprite-maps/food-f9e9817ead29e7dce81069945ff5bbe8.svg#chefs-hat"></use></svg></i></div>      <div      class="icon-stack__icon" style="z-index:1"><i class="card__term-icon-base icon" style="width:22px;min-width:22px;height:22px;min-height:22px;animation-duration:1000ms"><svg class="icon__svg" aria-hidden="true" focusable="false" style="color:inherit;fill:inherit"><use xlink:href="/static/icons/base/sprite-maps/shapes-f9e9817ead29e7dce81069945ff5bbe8.svg#circle"></use></svg></i></div>    </div><span class="terms-icons-list__text d-flex align-items-center" style="height:22px">Easy</span></li>  </ul>  </div></article>

当前Ruby代码

require 'nokogiri'
require 'open-uri'
require_relative "recipe"

class Scraper
  def initialize(ingredient)
    @ingredient = ingredient
  end

  def call
    html = URI.open("https://www.bbcgoodfood.com/search?q=#{@ingredient}").read
    doc = Nokogiri::HTML(html, nil, "utf-8")
    results = []
    doc.search("article.card.text-align-left.card--horizontal.card--inline.card--with-borders").first(1).each do |element|
      name = element.search("h2.heading-4").text.strip
      description = element.search(".region-clamper.line-clamper__content").text.strip
      prep_time = element.search(".terms-icons-list__text.d-flex.align-items-center").children.text.strip.split("Easy").first
     #to be continued...    
    end
    results
  end
end

解决方案

这里提供三种可靠的提取方式,根据网站结构稳定性选择:

方法1:通过关联图标定位(最精准)

观察HTML结构,准备时间对应的li里包含时钟图标(#clock-hands),可以利用这个特征精准定位:

prep_time = element.search('.list-item:has(.icon__svg[href*="#clock-hands"]) .terms-icons-list__text').text.strip

方法2:筛选包含"mins"的文本

直接获取所有目标元素后,筛选文本中包含"mins"的项:

prep_time = element.search('.terms-icons-list__text').find { |node| node.text.include?('mins') }&.text&.strip

方法3:按索引取第一个元素

如果网站中准备时间始终是列表的第一个项,也可以直接取第一个元素:

prep_time = element.search('.terms-icons-list__text').first.text.strip

修改后的完整代码

require 'nokogiri'
require 'open-uri'
require_relative "recipe"

class Scraper
  def initialize(ingredient)
    @ingredient = ingredient
  end

  def call
    html = URI.open("https://www.bbcgoodfood.com/search?q=#{@ingredient}").read
    doc = Nokogiri::HTML(html, nil, "utf-8")
    results = []
    doc.search("article.card.text-align-left.card--horizontal.card--inline.card--with-borders").first(1).each do |element|
      name = element.search("h2.heading-4").text.strip
      description = element.search(".region-clamper.line-clamper__content").text.strip
      # 使用方法1的精准定位
      prep_time = element.search('.list-item:has(.icon__svg[href*="#clock-hands"]) .terms-icons-list__text').text.strip
      results << { name: name, description: description, prep_time: prep_time }
    end
    results
  end
end

内容的提问来源于stack exchange,提问作者sas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 04:04:49