【问题标题】:How do I get this information out of this website?我如何从本网站获取这些信息?
【发布时间】:2020-07-17 19:47:47
【问题描述】:

我找到了这个链接: https://search.roblox.com/catalog/json?Category=2&Subcategory=2&SortType=4&Direction=2

原文是: https://www.roblox.com/catalog/?Category=2&Subcategory=2&SortType=4

我正在尝试使用 Python 抓取整个目录中所有商品的价格,但我似乎无法找到商品的价格。每当我转到下一页时,URL 都不会改变。我已尝试检查网站本身,但找不到任何东西。

第一个 URL 不知何故可刮,我在论坛上随机找到它。用户是如何获得所有这些文本数据的?

注意:我知道该网站是为儿童准备的,但我通过在那里销售限量商品来赚钱。请不要苛刻的判断。 :)

【问题讨论】:

  • 网站使用 ReactJs。这使得很难通过传统方式刮掉它。
  • 这很可悲。有什么我可以做的吗?
  • 我之前没有做过 React 页面的网页抓取,所以我不能提供太多建议。看看这个帖子,这可能会带你到正确的方向:stackoverflow.com/questions/57689198/…

标签: python html web-scraping


【解决方案1】:

您可以在没有BeautifulSoupSelenium 的情况下抓取所有项目信息 - 您只需要requests。话虽这么说,它不是超级直截了当,所以我会尝试分解它:

当您访问一个 URL 时,您的浏览器会向外部资源发出许多请求。这些资源托管在服务器上(或者,现在,在几个不同的服务器上),它们构成了浏览器正确呈现网页所需的所有文件/数据。仅举几例,这些资源可以是图像、图标、脚本、HTML 文件、CSS 文件、字体、音频等。仅供参考,在我的浏览器中加载 www.google.com 会向各种资源发出 36 个请求。

您发出请求的第一个资源将始终是实际网页本身,即类似 HTML 的文件。然后,浏览器通过查看该 HTML 来确定它需要向哪些其他资源发出请求。

例如,假设网页包含一个包含我们要抓取的数据的表格。我们应该问自己的第一件事是“那张桌子是怎么出现在那个页面上的?”。我的意思是,有不同的方式可以用元素/html标签填充网页。这是一种这样的方式:

  • 服务器从我们的浏览器接收到page.html的请求 资源
  • 该资源包含一个表,而该表需要数据,因此 服务器与数据库通信以检索数据 表
  • 服务器获取该表数据并将其烘焙到 HTML 中
  • 最后,服务器将 HTML 文件提供给您
  • 您收到的是带有烘焙表格数据的 HTML。没有办法 您可以与前面提到的数据库进行通信 - 这很好,可取
  • 您的浏览器呈现 HTML

当像这样抓取页面时,使用BeautifulSoup 是标准程序。您知道您要查找的数据已写入 HTML,因此BeautifulSoup 将能够看到它。

这是另一种可以用元素填充网页的方式:

  • 服务器从我们的浏览器接收到page.html的请求 资源
  • 该资源需要另一个资源 - 一个脚本,它的工作是 在以后的某个时间点用数据填充表格
  • 服务器向您提供该 HTML 文件(它不包含实际的 表数据)

当我说“稍后的时间点”时,该时间间隔可以忽略不计,对于使用实际浏览器查看页面的实际人类来说实际上是不明显的。然而,服务器只为我们提供了一个“基本”的 HTML。它只是一个空模板,它依赖于脚本来填充它的表。该脚本向 Web API 发出请求,Web API 用实际的表数据回复。所有这些都需要有限的时间,并且只有在脚本资源加载后才能开始。

当像这样抓取页面时,你不能使用BeautifulSoup,因为它只会看到“bare-bones”模板 HTML。这通常是您使用Selenium 模拟真实浏览会话的地方。

要返回您的 roblox 页面,此页面是第二种类型。

我建议的方法(这是我最喜欢的方法,在我看来,应该是您始终首先尝试的方法),只需找出潜在的 Web API 脚本正在发出请求,然后模仿请求得到你想要的数据。这是我最喜欢的方法的原因是因为这些 Web API 通常提供 JSON,这很容易解析。它超级干净,因为您只需要一个第三方模块 (requests)。

第一步是记录浏览器对资源的所有流量/请求。我将使用 Google Chrome,但其他现代浏览器可能具有类似功能:

  1. 打开谷歌浏览器并导航到目标页面 (https://www.roblox.com/catalog/?Category=2&Subcategory=2&SortType=4)
  2. 按 F12 打开 Chrome 开发者工具菜单
  3. 点击“网络”标签
  4. 点击“过滤器”按钮(图标为漏斗形),然后 将过滤器选择从“All”更改为“XHR”(XMLHttpRequestXHR 资源是与服务器交互的对象。我们想 只查看 XHR 资源,因为它们可能与 网络 API)
  5. 单击圆形“录制”按钮(或按 CTRL + E)启用 日志记录 - 启用后图标应变为红色
  6. 按 CTRL + R 刷新页面并开始记录流量
  7. 刷新页面后,您应该会看到资源日志开始 填上。这是我们浏览器请求的所有资源的列表 - 我们只会看到 XHR 对象,因为我们设置了过滤器(如果 您很好奇,您可以将过滤器切换回“全部”以查看 对资源的所有请求的列表)

单击列表中的一项。右侧应打开一个面板,其中包含多个选项卡。单击“标头”选项卡以查看请求 URL、请求和响应标头以及任何 cookie(查看“Cookies”选项卡以获得更漂亮的视图)。如果请求 URL 包含任何查询字符串参数,您还可以在此选项卡中以更漂亮的格式查看它们。这是它的样子(对不起,大图):

此选项卡告诉我们关于模仿我们的请求的所有信息。它告诉我们应该在哪里提出请求,以及应该如何制定我们的请求才能被接受。 Web API 将拒绝格式错误的请求 - 并非所有 Web API 都关心相同的标头字段。例如,一些 Web API 非常关心“User-Agent”标头,但在我们的例子中,这个字段不是必需的。我知道的唯一原因是因为我复制并粘贴了请求标头,直到 Web API 不再拒绝我的请求 - 在我的解决方案中,我将使用最低限度来发出有效请求。

但是,我们实际上需要弄清楚这些 XHR 对象中的哪一个负责与正确的 Web API 对话——返回我们想要抓取的实际信息的那个。从列表中选择任何 XHR 对象,然后单击“预览”选项卡以查看 Web API 返回的数据的解析版本。假设是 Web API 将 JSON 返回给我们 - 您可能需要先展开和折叠树结构一段时间才能找到您要查找的内容,但是一旦您这样做了,您就会知道这个 XHR 对象是要求我们需要模仿。我碰巧知道我们感兴趣的数据在名为“details”的 XHR 对象中。以下是“预览”选项卡中扩展 JSON 的一部分:

如您所见,我们从该 Web API (https://catalog.roblox.com/v1/catalog/items/details) 获得的响应包含我们想要抓取的所有有趣数据!

这就是事情变得有点深奥的地方,并且特定于这个特定的网页(到目前为止,您可以使用所有东西通过 Web API 从其他页面抓取内容)。当您访问 https://www.roblox.com/catalog/?Category=2&Subcategory=2&SortType=4 时会发生以下情况:

  • 您的浏览器会获取一些持续存在的 cookie,并生成 CSRF/XSRF 令牌并将其烘焙到页面的 HTML 中
  • 最终,XHR 对象之一(以 "items?") 向 Web API 发出 HTTP GET 请求(需要 cookie!) https://catalog.roblox.com/v1/search/items?category=Collectibles&limit=60&sortType=4&subcategory=Collectibles (注意查询字符串参数) 响应是 JSON。它包含一个项目描述符列表,如下所示:

然后,一段时间后,另一个 XHR 对象(“详细信息”)向 Web API https://catalog.roblox.com/v1/catalog/items/details 发出 HTTP POST 请求(请参阅第一个和第二个屏幕截图)。这个请求只有在它包含正确的 cookie 和前面提到的 CSRF/XSRF 令牌时才会被 Web API 接受。此外,此请求还需要一个包含我们要抓取其信息的资产 ID 的有效负载 - 未能提供此信息也会导致拒绝。

所以,这有点棘手。一个 XHR 对象的请求取决于另一个 XHR 对象的响应。

所以,这是脚本。它首先创建一个requests.Session 来跟踪cookie。我们定义了一个字典params(实际上只是我们的查询字符串)——您可以更改这些值以满足您的需要。它现在的编写方式是从“收藏品”类别中提取前 60 个项目。然后,我们使用正则表达式从 HTML 正文中获取 CSRF/XSRF 令牌。我们根据 params 获取前 60 个项目的 id,并生成最终 Web API 请求将接受的字典/有效负载。我们发出最终请求,创建项目列表(字典),并打印查询的第一项的键和值。

def get_csrf_token(session):

    import re

    url = "https://www.roblox.com/catalog/"

    response = session.get(url)
    response.raise_for_status()

    token_pattern = "setToken\\('(?P<csrf_token>[^\\)]+)'\\)"

    match = re.search(token_pattern, response.text)
    assert match
    return match.group("csrf_token")

def get_assets(session, params):

    url = "https://catalog.roblox.com/v1/search/items"

    response = session.get(url, params=params, headers={})
    response.raise_for_status()

    return {"items": [{**d, "key": f"{d['itemType']}_{d['id']}"} for d in response.json()["data"]]}

def get_items(session, csrf_token, assets):

    import json

    url = "https://catalog.roblox.com/v1/catalog/items/details"

    headers = {
        "Content-Type": "application/json;charset=UTF-8",
        "X-CSRF-TOKEN": csrf_token
    }

    response = session.post(url, data=json.dumps(assets), headers=headers)
    response.raise_for_status()

    items = response.json()["data"]
    return items

def main():

    import requests

    session = requests.Session()

    params = {
        "category": "Collectibles",
        "limit": "60",
        "sortType": "4",
        "subcategory": "Collectibles"
    }

    csrf_token = get_csrf_token(session)
    assets = get_assets(session, params)
    items = get_items(session, csrf_token, assets)

    first_item = items[0]

    for key, value in first_item.items():
        print(f"{key}: {value}")
    
    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())

输出:

id: 76692143
itemType: Asset
assetType: 8
name: Chaos Canyon Sugar Egg
description: This highly collectible commemorative egg recalls that *other* classic ROBLOX level, the one that was never quite as popular as Crossroads.
productId: 11837951
genres: ['All']
itemStatus: []
itemRestrictions: ['Limited']
creatorType: User
creatorTargetId: 1
creatorName: ROBLOX
lowestPrice: 400
purchaseCount: 7714
favoriteCount: 2781
>>> 

【讨论】:

  • 哇!这是一个非常详细的答案!谢谢你,我真的很感谢你为这个答案付出的所有时间和精力。这绝对有助于我的抓取和理解。再次感谢您!
【解决方案2】:

您可以使用 selenium 来控制浏览器。 https://www.selenium.dev/ 它可以为您提供项目和子项目(以及更多)的竞争。您可以按我认为在 firefox 上的 alt,然后按 developer -> Inspector 并将鼠标悬停在网页的 Item 上。它向您显示响应的 html 文本 python绑定:selenium-python.readthedocs.io

【讨论】:

    猜你喜欢
    • 2011-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多