如何通过谷歌地图链接抓取餐厅信息并构建Restaurant对象?
谷歌地图餐厅信息抓取问题及解决方案
问题描述
我正尝试在Rails控制器中编写方法,给定谷歌地图链接后抓取餐厅基础信息并构建Restaurant对象,同时用Active Storage存储抓取到的图片。需要抓取的信息包括:
- 名称(Name)
- 地址(Address)
- 电话(Phone)
- 网站(Website)
- 菜系(Cuisine)
- 价格区间($的数量)
- 评分(星级数量)
- 营业时间(Hours)
目前仅尝试获取名称但未成功,现有方法代码如下:
def scrape_google google_link = params[:google_link] page = Nokogiri::HTML(URI.open(google_link)) @restaurant = Restaurant.new # Restaurant Name restaurant_name_element = page.at_css('h1.DUwDvf.fontHeadlineLarge') @restaurant.name = restaurant_name_element&.text&.strip if restaurant_name_element # Output scraped information to the console puts "Scraped Information:" puts "Name: #{@restaurant&.name}" render :scrape_google end
问题分析与解决思路
1. 名称抓取失败的核心原因
谷歌地图页面采用动态JavaScript渲染,直接用Nokogiri抓取静态HTML只能拿到初始空页面,无法获取JS执行后生成的实际元素,因此找不到目标CSS选择器对应的内容。
2. 解决方案步骤
(1)改用动态页面抓取工具
使用ferrum(基于Chrome DevTools的轻量工具)获取渲染后的完整页面,替代静态抓取方式:
- 先在Gemfile添加依赖:
gem 'ferrum',执行bundle install - 修改控制器方法如下:
def scrape_google google_link = params[:google_link] browser = Ferrum::Browser.new browser.goto(google_link) # 等待目标元素加载完成,避免抓取空内容 browser.wait_for_css('h1.DUwDvf.fontHeadlineLarge') page = Nokogiri::HTML(browser.body) @restaurant = Restaurant.new # 抓取名称 restaurant_name_element = page.at_css('h1.DUwDvf.fontHeadlineLarge') @restaurant.name = restaurant_name_element&.text&.strip if restaurant_name_element # 抓取地址 address_element = page.at_css('div.Io6YTe.fontBodyMedium') @restaurant.address = address_element&.text&.strip if address_element # 抓取电话 phone_element = page.at_css('button.LMNPb span.fontBodyMedium') @restaurant.phone = phone_element&.text&.strip if phone_element # 抓取网站 website_element = page.at_css('a.CsEnBe') @restaurant.website = website_element&.attribute('href')&.value if website_element # 抓取菜系 cuisine_element = page.at_css('div.kno-rdesc span.rQMQod') @restaurant.cuisine = cuisine_element&.text&.strip if cuisine_element # 抓取价格区间(统计$的数量) price_element = page.at_css('span.HlvSq') @restaurant.price_range = price_element&.text&.count('$') if price_element # 抓取评分(星级对应的数值) rating_element = page.at_css('div.F7nice span.ceNzKf') @restaurant.rating = rating_element&.text&.strip if rating_element # 抓取营业时间 hours_element = page.at_css('div.OqCZI') @restaurant.hours = hours_element&.text&.strip if hours_element # 抓取图片并存储到Active Storage image_element = page.at_css('img.THOPZb') if image_element image_url = image_element.attribute('src').value # 下载图片并附加到模型 @restaurant.image.attach(io: URI.open(image_url), filename: "#{@restaurant.name.parameterize}.jpg") end puts "Scraped Information:" puts "Name: #{@restaurant&.name}" puts "Address: #{@restaurant&.address}" browser.quit render :scrape_google end
(2)关键注意事项
- 谷歌地图的CSS选择器可能随时变更,需定期验证更新
- 频繁抓取可能触发反爬机制,建议添加请求间隔、使用代理IP
- 确保
Restaurant模型已配置Active Storage:has_one_attached :image - 优先考虑使用谷歌地图Places API获取结构化数据,合法合规且稳定性更高
内容的提问来源于stack exchange,提问作者BrownKid
相关产品推荐
相关产品推荐

