你所做的工作比你需要做的要多得多——我是说使用 Selenium。访问该页面时,我使用 Google Chrome 的开发工具记录了我的网络流量,我看到我的浏览器向 REST API 发出了 HTTP GET 请求,其响应为 JSON,包含您可能想要的所有葡萄酒/价格信息。因此,您不需要进行任何刮擦。只需使用所需的查询字符串参数和正确的标头来模仿 GET 请求即可。看起来 REST API 只关心user-agent
标题,这是微不足道的。
- 首先,访问URL在谷歌浏览器中。
- 按
F12
键打开 Google Chrome 开发工具菜单。
单击Network
tab.
- 单击圆形“录制”按钮。它应该变成红色,并且它会
开始在下面的日志中记录所有网络流量。单击
旁边的筛选按钮,然后单击
XHR
。这只会
在日志中显示 XHR (XmlHttpRequest) 请求。我们感兴趣的是
特别是这些请求,因为它是通过 XHR 请求的
通常会制作 API。现在应该是这样的:
![](https://i.stack.imgur.com/xVGGp.png)
- 在 Chrome 开发工具菜单仍然打开的情况下,右键单击(不是
左键单击)页面刷新按钮以显示下拉菜单。
然后,单击
Empty Cache and Hard Reload
.
![](https://i.stack.imgur.com/QB4I5.png)
这将清空浏览器对此页面的缓存,并强制您
浏览器刷新页面。当页面刷新时,您
应该开始看到流量日志中出现一些条目。
![](https://i.stack.imgur.com/u7XcQ.png)
现在日志中应该有一些 XHR 请求条目。我们不知道其中哪一项是我们真正感兴趣的,所以我们只是查看所有这些,直到找到一个看起来可能是正确的(或者,如果它包含我们的信息)正在寻找,例如个别葡萄酒的信息等)。我碰巧知道我们对开头的那个感兴趣explore?...
,所以让我们点击它。
- 单击该条目后,将在该条目的右侧打开一个面板
日志。单击
Headers
tab.
![](https://i.stack.imgur.com/IURu2.png)
此选项卡包含有关如何提出此请求的所有信息。在下面General
区,你可以看到Request URL
,这是我们发出请求的 REST API 端点的 URL。该 URL 可能会很长,因为它通常还包含查询字符串参数(这些参数是后面的键值对)explore?
, like country_code=DE
or currency_code=EUR
。它们之间的距离是&
)。查询字符串参数很重要,因为它们包含有关我们想要应用于查询的某些过滤器的信息。在我的代码示例中,我已将它们从 REST API 端点 URL 中删除,并将它们移至params
字典。此步骤不是必需的 - 您也可以将它们留在 URL 中,但我发现这种方式更容易阅读和修改。查询字符串参数也很重要,因为有时,某些 API 会期望请求中出现某些参数,或者它们会期望它们具有某些值 - 换句话说,某些 API 对它们的查询字符串参数非常挑剔,如果您删除它们或以 API 不期望的方式篡改它们,API 会说您的请求格式不正确。
In the General
区,你还可以看到Request Method
,在我们的例子中是GET
。这告诉我们,我们的浏览器发出了 HTTP GET 请求。并非所有 API 端点的工作方式都相同,有些端点需要 HTTP POST 等。
Status Code
告诉我们服务器发回的状态代码。200
意味着一切顺利。您可以了解更多有关HTTP 状态代码在这里.
让我们看一下Response Headers
区域。该区域包含服务器在发出请求后发回的所有响应标头。这些对于浏览器来说非常有用,例如设置 cookie 或了解如何解释服务器发回的数据。
The Request Headers
区域包含浏览器在发出请求时发送到服务器的所有标头。通常,最好复制所有这些键值对并将它们转换为 Python 字典headers
,因为这样您就可以确定您的 Python 脚本将发出与浏览器发出的完全相同的请求。然而,通常情况下,我喜欢尽可能地减少它。我知道许多 API 都非常关心user-agent
字段,所以通常我会保留那个字段,但有时他们也关心referer
。当您使用不同的 API 时,您必须通过反复试验找出 API 关心哪些请求标头。这个API恰好只关心user-agent
.
最后一个区域Query String Parameters
只是显示查询字符串参数的一种可爱方式Request URL
在一个人类友好的键值对列表中。有时从此处复制它们比从 URL 复制更有帮助。
- 现在,单击
Preview
选项卡,旁边Headers
tab.
![](https://i.stack.imgur.com/CP6KS.png)
The Preview
选项卡包含根据浏览器请求发送回的实际数据的精美打印预览。在我们的例子中,它包含服务器发回的 JSON 数据。您可以单击灰色小三角形来展开或折叠 JSON 结构的某些部分,以显示不同的数据。
![](https://i.stack.imgur.com/tmi87.png)
看看这个,我可以看出 JSON 响应是一个大字典,其中有一个键explore_vintage
,其值是另一个字典,该字典有一个键records
其值是一个字典列表,其中该列表中的每个字典代表一个 wine 对象。展开第一个记录(第 0 个)会显示有关列表中第一个葡萄酒的所有信息。您可以根据需要探索这些结构,看看您可以获得哪些类型的信息。
![](https://i.stack.imgur.com/PFm9E.png)
def main():
import requests
url = "https://www.vivino.com/api/explore/explore"
params = {
"country_code": "DE",
"currency_code": "EUR",
"grape_filter": "varietal",
"min_rating": "3.5",
"order_by": "ratings_average",
"order": "desc",
"page": "1",
"price_range_max": "30",
"price_range_min": "7",
"wine_type_ids[]": "1"
}
headers = {
"user-agent": "Mozilla/5.0"
}
response = requests.get(url, params=params, headers=headers)
response.raise_for_status()
records = response.json()["explore_vintage"]["records"]
for record in records:
name = record["vintage"]["name"]
price = record["price"]["amount"]
currency = record["price"]["currency"]["code"]
print(f"\"{name}\" - Price: {price} {currency}")
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
Output:
"Varvaglione Cosimo Varvaglione Collezione Privata Primitivo di Manduria 2015" - Price: 21.9 EUR
"Masseria Borgo dei Trulli Mirea Primitivo di Manduria 2019" - Price: 19.9 EUR
"Vigneti del Salento Vigne Vecchie Primitivo di Manduria 2016" - Price: 22.95 EUR
"Vigneti del Salento Vigne Vecchie Leggenda Primitivo di Manduria 2016" - Price: 17.87 EUR
"Varvaglione Papale Linea Oro Primitivo di Manduria 2016" - Price: 18.85 EUR
"Caballo Loco Grand Cru Apalta 2014" - Price: 27.9 EUR
"Luccarelli Il Bacca Old Vine Primitivo di Manduria 2016" - Price: 20.9 EUR
"Mottura Stilio Primitivo di Manduria 2018" - Price: 12.89 EUR
"Caballo Loco Grand Cru Maipo 2015" - Price: 24.81 EUR
"Lorusso Michele Solone Primitivo 2017" - Price: 21.39 EUR
"Château Purcari Negru de Purcari 2017" - Price: 29.8 EUR
"San Marzano 60 Sessantanni Limited Edition Old Vines Primitivo di Manduria 2016" - Price: 22.85 EUR
"San Marzano 60 Sessantanni Old Vines Primitivo di Manduria 2016" - Price: 20.9 EUR
"San Marzano 60 Sessantanni Old Vines Primitivo di Manduria 2017" - Price: 17.775 EUR
"Lenotti Amarone della Valpolicella Classico 2015" - Price: 27.95 EUR
"Zeni Cruino Rosso Veronese 2015" - Price: 22.9 EUR
"Masseria Pietrosa Palmenti Primitivo di Manduria Vigne Vecchie 2016" - Price: 25 EUR
"Ravazzi Prezioso 2016" - Price: 29.95 EUR
"Nino Negri Sfursat Carlo Negri 2017" - Price: 23.89 EUR
"Quinta do Paral Reserva Tinto 2017" - Price: 29.24 EUR
"Wildekrans Barrel Select Reserve Pinotage 2016" - Price: 29.9 EUR
"Caballo Loco Grand Cru Limarí 2016" - Price: 27.9 EUR
"San Marzano F Negroamaro 2018" - Price: 16.9 EUR
"Atlan & Artisan 8 Vents Mallorca 2018" - Price: 19 EUR
"Schneider Rooi Olifant Red 2017" - Price: 19.5 EUR
>>>
它似乎每页抓取二十五条记录/葡萄酒,但是改变了page
中的键值对params
查询字符串参数字典将从您想要的任何页面生成记录。我目前位于德国,这就是为什么我的country_code
and currency_code
are "DE"
and "EUR"
,但您应该能够更改它们以满足您的需要。
编辑 - 这里有一些您可能感兴趣的键值对,但我建议您熟悉浏览器的开发工具的工作原理,以便您可以自己发现 JSON 中的这些字段:
record["vintage"]["year"]
record["vintage"]["wine"]["region"]["name"]
record["vintage"]["wine"]["region"]["country"]["name"]
record["vintage"]["wine"]["taste"]["structure"]["acidity"]
record["vintage"]["wine"]["taste"]["structure"]["intensity"]
record["vintage"]["wine"]["taste"]["structure"]["sweetness"]
record["vintage"]["wine"]["taste"]["structure"]["tannin"]
record["vintage"]["wine"]["style"]["grapes"][0]["name"] # 0th grape information
record["vintage"]["wine"]["winery"]["name"]
不幸的是,JSON 不包含酒精含量。此信息硬编码在给定葡萄酒摘要页面的 HTML 中。您必须向每个葡萄酒摘要页面发出请求,并可能使用正则表达式从页面中提取酒精含量值。