我如何从本网站获取这些信息?

-1 html python web-scraping

我找到了这个链接: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 以某种方式可抓取,我在论坛上随机找到它。用户是如何在那里获取所有这些文本数据的?

注意:我知道该网站是为儿童设计的,但我通过在那里销售限量版来赚钱。请不要做出严厉的判断。:)

use*_*432 7

您可以在不使用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,因为它只会看到“基本”模板 HTML。这通常是您Selenium用来模拟真实浏览会话的地方。

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

我建议的方法(这是我最喜欢的方法,在我看来,应该是您总是首先尝试的方法),只涉及弄清楚 Web API 潜在脚本正在向哪些 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. 点击“过滤器”按钮(图标为漏斗形),然后将过滤器选择从“全部”改为“XHR”(XMLHttpRequest或者 XHR资源是与服务器交互的对象。我们只想查看XHR资源,因为它们可能与 Web 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 通信——即返回我们想要抓取的实际信息的 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 https://catalog.roblox.com/v1/search/items?category=Collectibles&limit=60&sortType=4&subcategory=Collectibles (注意查询字符串参数)发出HTTP GET 请求(需要 cookie!), 响应是 JSON。它包含一个项目描述符列表,它看起来像这样:

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

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

所以,这是脚本。它首先创建一个requests.Session来跟踪 cookie。我们定义了一个字典params(它实际上只是我们的查询字符串)——您可以更改这些值以满足您的需要。它现在的编写方式是从“收藏品”类别中提取前 60 个项目。然后,我们使用正则表达式从 HTML 正文中获取 CSRF/XSRF 令牌。我们根据我们的 获取前 60 个项目的 id params,并生成最终 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())
Run Code Online (Sandbox Code Playgroud)

输出:

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
>>> 
Run Code Online (Sandbox Code Playgroud)