如何在Python中没有文本的情况下通过文本单击链接

2023-12-10

我正在尝试从 vivino.com 抓取 Wine 数据,并使用 selenium 使其自动化并抓取尽可能多的数据。我的代码如下所示:

import time 
from selenium import webdriver

browser = webdriver.Chrome('C:\Program Files (x86)\chromedriver.exe')

browser.get('https://www.vivino.com/explore?e=eJwFwbEOQDAUBdC_uaNoMN7NZhQLEXmqmiZaUk3x987xkVXRwLtAVcLLy7qE_tiN0Bz6FhcV7M4s0ZkkB86VUZIL9l4kmyjW4ORmbo0nTTPVDxlkGvg%3D&cart_item_source=nav-explore') # Vivino Website with 5 wines for now (simple example). Plan to scrape around 10,000 wines 

lenOfPage = browser.execute_script("window.scrollTo(0, document.body.scrollHeight);var lenOfPage=document.body.scrollHeight;return lenOfPage;")

match=False
while(match==False):
    lastCount = lenOfPage
    time.sleep(7)
    lenOfPage = browser.execute_script("window.scrollTo(0, document.body.scrollHeight);var lenOfPage=document.body.scrollHeight;return lenOfPage;")
    if lastCount==lenOfPage:
        match=True

这将打开一个包含 5 种葡萄酒的网站并向下滚动。现在我想一一点击葡萄酒的超链接来抓取有关其价格、酿酒葡萄种类等的信息。所以,基本上我的脚本会尝试向下滚动,允许在页面上显示尽可能多的葡萄酒,然后单击第一个超链接,获取附加信息并返回。然后,该过程将重复。我认为这不是一个有效的策略,但这就是我到目前为止所提出的。

我遇到的问题是 vivino 网站中的超链接。 href 链接附近没有允许我使用的文本按链接文本查找元素功能:

<a class="anchor__anchor--2QZvA" href="/weingut-r-a-pfaffl-austrian-cherry-zweigelt/w/1261542?year=2018&amp;price_id=23409078&amp;cart_item_source=direct-explore" target="_blank">

您能否建议如何点击超链接后没有文字的带有 Selenium 的葡萄酒?我在网络搜索过程中没有找到正确的答案。提前致谢


你所做的工作比你需要做的要多得多——我是说使用 Selenium。访问该页面时,我使用 Google Chrome 的开发工具记录了我的网络流量,我看到我的浏览器向 REST API 发出了 HTTP GET 请求,其响应为 JSON,包含您可能想要的所有葡萄酒/价格信息。因此,您不需要进行任何刮擦。只需使用所需的查询字符串参数和正确的标头来模仿 GET 请求即可。看起来 REST API 只关心user-agent标题,这是微不足道的。


  1. 首先,访问URL在谷歌浏览器中。
  2. F12键打开 Google Chrome 开发工具菜单。 单击Network tab.
  3. 单击圆形“录制”按钮。它应该变成红色,并且它会 开始在下面的日志中记录所有网络流量。单击 旁边的筛选按钮,然后单击XHR。这只会 在日志中显示 XHR (XmlHttpRequest) 请求。我们感兴趣的是 特别是这些请求,因为它是通过 XHR 请求的 通常会制作 API。现在应该是这样的:

  1. 在 Chrome 开发工具菜单仍然打开的情况下,右键单击(不是 左键单击)页面刷新按钮以显示下拉菜单。 然后,单击Empty Cache and Hard Reload.

这将清空浏览器对此页面的缓存,并强制您 浏览器刷新页面。当页面刷新时,您 应该开始看到流量日志中出现一些条目。

现在日志中应该有一些 XHR 请求条目。我们不知道其中哪一项是我们真正感兴趣的,所以我们只是查看所有这些,直到找到一个看起来可能是正确的(或者,如果它包含我们的信息)正在寻找,例如个别葡萄酒的信息等)。我碰巧知道我们对开头的那个感兴趣explore?...,所以让我们点击它。

  1. 单击该条目后,将在该条目的右侧打开一个面板 日志。单击Headers tab.

此选项卡包含有关如何提出此请求的所有信息。在下面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 复制更有帮助。

  1. 现在,单击Preview选项卡,旁边Headers tab.

The Preview选项卡包含根据浏览器请求发送回的实际数据的精美打印预览。在我们的例子中,它包含服务器发回的 JSON 数据。您可以单击灰色小三角形来展开或折叠 JSON 结构的某些部分,以显示不同的数据。

看看这个,我可以看出 JSON 响应是一个大字典,其中有一个键explore_vintage,其值是另一个字典,该字典有一个键records其值是一个字典列表,其中该列表中的每个字典代表一个 wine 对象。展开第一个记录(第 0 个)会显示有关列表中第一个葡萄酒的所有信息。您可以根据需要探索这些结构,看看您可以获得哪些类型的信息。


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 中。您必须向每个葡萄酒摘要页面发出请求,并可能使用正则表达式从页面中提取酒精含量值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在Python中没有文本的情况下通过文本单击链接 的相关文章

随机推荐

  • 通过带分隔符的字符串访问多维数组

    假设我有一个像这样的多维数组 我想通过传递像这样的字符串来访问数组元素 test1 test2 test3 到一个函数 该函数依次调用数组元素 我可以用eval 将字符串替换为 调用 array test2 test3 但我想知道是否有一种
  • 数据库设计 - 为用户存储积分的方法

    只是寻找一些有关如何为此进行数据库设计的建议 在我的网站上 用户可以通过执行不同的活动获得积分 目前 我有 3 项奖励积分的活动 但设计必须具有可扩展性 我也可以添加其他奖励积分的活动 所以今天 用户获得积分 1 当他添加一个新商店时 他获
  • 如何使用泛型 Go 实例化类型参数的非零指针?

    现在类型参数可用golang go master 我决定尝试一下 看来我遇到了在中找不到的限制类型 参数 提案 或者我一定错过了 我想编写一个函数 它返回带有接口类型约束的泛型类型值的切片 如果传递的类型是带有指针接收器的实现 我们如何实例
  • 如何取消从 Git Bash 克隆 Git 存储库?

    我目前正在使用 Git Bash 克隆一个拥有超过 100 000 次提交的大型存储库 克隆过程已经持续了半个多小时 还没有完成对象的接收 是否可以取消git clone来自 Git Bash 的命令 作为参考 我在 Windows 7 P
  • 在渲染 JS canvas 之前加载图像

    我正在编写一个简单的游戏来学习 JS 并且在此过程中我正在学习 HTML5 所以我需要在画布上绘制东西 这是代码 let paddle new Paddle GAME WIDTH GAME HEIGHT new InputHandler p
  • 应用程序窗口预计在应用程序启动结束时有一个根视图控制器

    当我的应用程序启动时出现上述错误 以下代码来自我的 AppDelegate h 文件 import
  • 如何导入数据文件进行UFT API测试?

    我正在开发一个项目 需要在运行时动态导入测试数据并运行与测试数据对应的API测试 我尝试使用 XMLload 直接从 xml 请求加载 这似乎运作良好 但就像 GUI 测试一样 我们导入测试数据 我希望 API 测试也一样 我可以动态地从外
  • SVG - 从中​​心重复缩放路径(脉动)

    我有一个中心点为 100 100 的 svg 图形
  • 从类或实例设置属性时的查找过程是什么?

    Python 简而言之描述了从类获取属性时的查找过程 例如cls name以及从实例获取属性时的查找过程 例如obj name 但我不确定何时设置属性 设置属性 请注意 属性查找步骤仅按照刚刚描述的方式发生 当您引用属性时 而不是当您绑定属
  • 使用vtd-xml解析xml文件

    如何在 java 或 C 中使用 vtd xml 解析如下所示的 xml 文件
  • IE11 预览版中未定义“WebForm_DoPostBackWithOptions”

    IE11来了 我刚刚安装了开发者预览版 但是 如果我运行一些 Web 应用程序并收到错误WebForm DoPostBackWithOptions未定义 当我玩自动回发时弹出错误DropDownList 而且之前IE10好像也有类似的问题
  • Postgres 自然排序依据

    我在 postgres 中的一列中遇到了排序问题 其中包含诸如版本之类的值 版本因字符而异 其值如下 无序 1 2 1 3 1 10 1 1 9 我如何按自然顺序排序 以便当我发布时SELECT version FROM TABLE A O
  • Oracle 10g 中的聚合字符串连接[重复]

    这个问题在这里已经有答案了 我看到上一个问题 其中表格包含 否 和 名称 列 以及其他与数字列分组的问题 但无法实现为我的案例提供的答案 我需要做同样的事情 但是使用非数字分组 源表是 tbl1 包含以下列 POD Name North R
  • iOS 7.0 未找到代码签名身份

    证书有效 xcode 5 iOS 7 代码签名错误 未找到代码签名身份 没有代码签名 匹配的身份 即证书和私钥对 在构建设置中指定的配置文件 iOS Team 找到配置文件 CodeSign 错误 代码签名是 SDK iOS 7 0 中的产
  • 在没有 stdarg 的情况下访问可变参数函数参数

    有没有办法使用指针访问函数中的可变参数 我尝试做类似的事情 void test int p int firstarg p 1 但这行不通 va start 和 va arg 如何访问这些参数 没有portable这样做的方法 并且没有充分的
  • Selenium:获取 chrome 时未正确关闭

    当我在 selenium 框架中重新打开 chrome 浏览器时 收到 chrome 未正确关闭的错误消息 在框架中 我使用以下代码在每个测试用例的开头打开浏览器实例 if browserType equalsIgnoreCase Chro
  • 为什么 git 认为整个文件已更改,而仅更改了其中的一部分

    我面临着一个奇怪的情况git 我刚刚编辑了一个长文件中的几行 但是当我去提交更改时 我看到 git 只是用红色突出显示了整个文件内容 怎么了 我正在使用 git 的 Windows GUI 编辑 我现在记得的一件事是我在上次提交中将 csp
  • Dart 空检查习惯用法或最佳实践是什么?

    我有以下形式的赋值和空检查 以避免在我的地图中进行双重查找 在 Dart 中是否有更好或更惯用的方法来做到这一点 bool isConnected a b List list return list outgoing a null list
  • 如何实现规则引擎?

    我有一个存储以下内容的数据库表 RuleID objectProperty ComparisonOperator TargetValue 1 age greater than 15 2 username equal some name 3
  • 如何在Python中没有文本的情况下通过文本单击链接

    我正在尝试从 vivino com 抓取 Wine 数据 并使用 selenium 使其自动化并抓取尽可能多的数据 我的代码如下所示 import time from selenium import webdriver browser we