Python爬取Deliveroo多站点商品名称与价格问题求解
Deliveroo站点商品爬取方案
单站点商品名、价格提取
你之前卡在数据提取环节的核心问题是写死了完整的class属性值——Deliveroo前端类名末尾带随机生成的哈希串,会随版本迭代变动,硬写全值很容易匹配失败,同时不需要手动逐层追溯嵌套节点,直接按特征匹配目标标签即可。
提取逻辑:
- 商品卡片统一以
UIMenuItemCard为类名前缀,用前缀匹配定位所有商品容器 - 商品名存放在卡片内类名前缀为
ccl-的p标签中 - 价格存放在卡片内带欧元符号
€的span标签中,通过文本特征匹配避免抓错无关标签
可直接运行的单站点提取代码:
import requests from bs4 import BeautifulSoup import pandas as pd url = "https://deliveroo.it/en/menu/jesolo/jesolo/burger-king-jesolo" # 加请求头模拟正常浏览器访问,避免被反爬拦截 headers = { "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" } resp = requests.get(url, headers=headers) soup = BeautifulSoup(resp.text, "html.parser") product_list = [] for card in soup.find_all("div", class_=lambda x: x and x.startswith("UIMenuItemCard")): name_tag = card.find("p", class_=lambda x: x and x.startswith("ccl-")) price_tag = card.find("span", string=lambda t: t and "€" in t) if name_tag and price_tag: product_list.append({ "name": name_tag.get_text(strip=True), "price": price_tag.get_text(strip=True).replace("€", "").strip() }) df_single = pd.DataFrame(product_list) print(df_single)
运行后输出的结构化数据和你给出的示例格式完全一致。
多站点批量爬取+结果合并+站点间空行分隔
你原有批量代码仅返回最后一个站点数据的核心原因:db_name、db_price列表的初始化放在了for循环内部,每次遍历新URL时都会清空之前存储的爬取结果,循环结束后自然只保留最后一个站点的数据。另外你原代码中的URL被手动拆成了多行,会导致请求地址无效。
实现逻辑:
- 将存储全量数据的列表移到循环外部初始化,避免每次遍历重置数据
- 每爬完一个非末尾站点的所有商品,往结果集中插入一条空记录,实现站点间空行分隔
- 爬取完成后一次性转换为DataFrame,无需拆分name、price分别建表再合并
修正后的可运行代码:
import time import requests from bs4 import BeautifulSoup import pandas as pd siti = [ "https://deliveroo.it/it/menu/roma/piazza-venezia/burger-king-via-nazionale", "https://deliveroo.it/en/menu/jesolo/jesolo/burger-king-jesolo", "https://deliveroo.it/it/menu/roma/via-tiburtina/burger-king-via-tiburtina" ] headers = { "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" } all_products = [] for idx, url in enumerate(siti): resp = requests.get(url, headers=headers) soup = BeautifulSoup(resp.text, "html.parser") # 提取当前站点所有商品 for card in soup.find_all("div", class_=lambda x: x and x.startswith("UIMenuItemCard")): name_tag = card.find("p", class_=lambda x: x and x.startswith("ccl-")) price_tag = card.find("span", string=lambda t: t and "€" in t) if name_tag and price_tag: all_products.append({ "name": name_tag.get_text(strip=True), "price": price_tag.get_text(strip=True).replace("€", "").strip() }) # 非最后一个站点,爬完后插入空行做分隔 if idx != len(siti) - 1: all_products.append({"name": "", "price": ""}) # 加2秒延迟,避免请求过频被反爬拦截 time.sleep(2) df_all = pd.DataFrame(all_products) # 导出为csv文件,用utf-8-sig编码避免乱码 df_all.to_csv("deliveroo_all_products.csv", index=False, encoding="utf-8-sig") print(df_all)
注意事项
- 不要硬编码完整的class属性值,Deliveroo前端发版会更新类名后的哈希后缀,前缀匹配的方式稳定性更高
- 如果出现部分商品匹配不到的情况,可以先打印
soup.prettify()确认返回的页面源码中是否包含目标商品,大概率是被反爬拦截,调整请求头、增加请求延迟即可解决
内容的提问来源于stack exchange,提问作者Davide
相关产品推荐
相关产品推荐

