Scrapy:无法抓取 App Store 评论页面

Luc*_*ães 1 python app-store scrapy web-scraping ios

大家好,我在从应用商店获取此页面的数据时遇到了一些问题: 应用商店评论https://apps.apple.com/us/app/mathy-cool-math-learner-games/id1476596747#see-all /评论

我想首先检索一个字符串,该字符串显示用户对应用程序的评分。它们位于 class = "we-star-rating ember-view we-customer-review__rating we-star-rating--large" 的图形标签内,并且是属性@aria-label 的名称。

应用评论页面

这是我的代码:

from scrapy import Selector
import requests

html = requests.get('https://apps.apple.com/us/app/mathy-cool-math-learner-games/id1476596747#see-all/reviews').content

sel = Selector(text = html)

sel.xpath('//figure[@class="we-star-rating ember-view we-customer-review__rating we-star-rating--large"]/@aria-label').extract()

Run Code Online (Sandbox Code Playgroud)

但它只返回前 3 场比赛:

['5 out of 5', '5 out of 5', '5 out of 5']
Run Code Online (Sandbox Code Playgroud)

我想要的是从该页面的所有评论中检索所有可用的评分。

有人可以给我一个线索吗?

Aar*_*onS 7

问题

排名前三的评论作为 HTML 的一部分加载,但其余的则由 javascript 加载。这就是为什么你只得到前三个结果。

我不完全确定这是否是您使用scrapy 的全部代码。我很想知道你为什么选择scrapy的那部分。

因此,处理 javascript 是现代网站抓取网页的重要组成部分。我不完全确定您是否主要使用scrapy 来抓取网页。不过有几个选项可以使用scrapy处理javascript。

关于动态网页抓取的信息

首先知道,如今的网站使用 javascript 来调用 HTTP 请求(称为 AJAX 请求(异步 Javascript 和 XHTML))即时获取信息。这会向 API/服务器发出 post 或 get HTTP 请求,并且该 HTTP 响应会返回信息。在这种情况下,他们已将 3 个结果预加载到 HTML 中,但要求在使用 javascript 加载页面时提供其余评论。

一般来说,有两种方法可以处理面向 javascript 的网站。

  1. 重新设计 HTTP 请求 - 这是获取所需数据的最有效方式。您想模仿 javascript 正在调用的这些 HTTP 请求。如果您可以这样做并且有时需要您发布标题、参数和 cookie,那么您就可以获得所需的数据。
  2. 使用某种形式的浏览器自动化。Selenium 是首选的软件包,尽管最初从未打算以这种方式使用。如果使用更大的数据集,它会变得缓慢、低效且脆弱。

解决方案

对于您的特定网站,您可以重新设计 HTTP 请求以获取您想要的信息。这是理想的情况。

但我怎么知道的?你可以在 chrome 中做的一件事就是关闭 javascript。您必须检查页面并转到设置(单击页面最右侧的三个点 -> 更多工具 -> 设置)。无需 JavaScript 即可刷新页面。您会看到只有三个评论可供查看。

使用 ChromeDev 工具了解正在发生的事情非常有用。如果在右键单击并检查页面时转到网络选项卡,您将看到服务器发出的所有请求和响应。转到 XHR 选项卡,您将找到包含所需数据的请求。这里有一堆请求和响应。

见下图,我检查了页面,进入网络并刷新了页面。这会记录浏览器请求和响应的活动。

网络工具

您可以看到大约有 6 个请求,5 个 GET 请求和 1 个 POST 请求。如果单击每个请求,您将在右侧看到一个弹出框,其中包含请求数据、预览和响应。

在此处输入图片说明

在这里,我点击了第一个请求,我点击了预览,你可以看到是否点击了一些评论。

我可以在该数据的 HTTP 请求中看到偏移量为 10,这意味着它正在抓取接下来的 10 个请求。

在此处输入图片说明

因此,我将更改该偏移量,以查看是否可以获得前 10 条和第二条 10 条(此页面上有 20 条评论)。

无需手动输入参数和标题等...您可以将请求复制到 CURL 中。然后可以使用类似的站点将其转换curl.trillworks.com为漂亮的 python 格式。

在此处输入图片说明

现在值得查看预览数据,因为您将不得不使用请求来处理它。您最终会得到一个 JSON 对象,您可以从 HTTP get 请求的接受部分得知这一点是application/json

因此,已将此请求复制到 curl.trillworks.com。我们有以下内容。

编码示例

import requests

headers = {
    'Accept': 'application/json',
    'Referer': 'https://apps.apple.com/us/app/mathy-cool-math-learner-games/id1476596747',
    'Authorization': 'Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IldlYlBsYXlLaWQifQ.eyJpc3MiOiJBTVBXZWJQbGF5IiwiaWF0IjoxNTk2NTc1NTY4LCJleHAiOjE2MTIxMjc1Njh9.jnEuBNEVWhKGqI10W6dfhJFtYJtd74Nbu1NueZrPgYjU2K34LwXPQClcus8S9Jit5ayK5MOr0bIpcDx821RI4Q',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
}

params = (
    ('l', 'en-US'),
    ('offset', '1'),
    ('platform', 'web'),
    ('additionalPlatforms', 'appletv,ipad,iphone,mac'),
)

response = requests.get('https://amp-api.apps.apple.com/v1/catalog/us/apps/1476596747/reviews', headers=headers params=params)
response.json()
Run Code Online (Sandbox Code Playgroud)

我在上面提到过,有时您需要标题和参数。你可以在这里使用 request get 方法,看看是什么让你得到数据。在这种情况下,您需要参数和标题。情况并非总是如此,因此您应该始终做一个request.get()没有任何东西的简单,然后构建它。该json()方法将 json 对象格式化为 Python 字典,以便我们可以轻松访问数据。

现在,当我说查看预览时,会为我们提供访问数据所需的键和值。有时数据可以嵌套得很深。在这种情况下不是,所以我们现在考虑循环遍历这个字典来获取我们想要的所有数据。我们必须发出两个 HTTP 请求,一个偏移量为 0,另一个偏移量为 10,以获得完整的 20 星评级。

最终代码示例

import requests

headers = {
    'Accept': 'application/json',
    'Referer': 'https://apps.apple.com/us/app/mathy-cool-math-learner-games/id1476596747',
    'Authorization': 'Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IldlYlBsYXlLaWQifQ.eyJpc3MiOiJBTVBXZWJQbGF5IiwiaWF0IjoxNTk2NTc1NTY4LCJleHAiOjE2MTIxMjc1Njh9.jnEuBNEVWhKGqI10W6dfhJFtYJtd74Nbu1NueZrPgYjU2K34LwXPQClcus8S9Jit5ayK5MOr0bIpcDx821RI4Q',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
}
for i in range(0,20,10):
    
    params = (
        ('l', 'en-US'),
        ('offset', f'{i}'),
        ('platform', 'web'),
        ('additionalPlatforms', 'appletv,ipad,iphone,mac'),
    )
    response = requests.get('https://amp-api.apps.apple.com/v1/catalog/us/apps/1476596747/reviews', params=params)
    data =  response.json()['data']
    for a in data:
        print(a['attributes']['rating'])
Run Code Online (Sandbox Code Playgroud)

输出

5
5
5
5
5
...
Run Code Online (Sandbox Code Playgroud)

代码说明

  1. 我们使用发出请求所需的标头(我设法在没有它的情况下获取数据,但是在尝试获取所有数据时,似乎需要标头,但您可以使用它)

  2. 我们正在循环所需的参数,在这种情况下,我们希望偏移量为 0, 10。所以我们range(0,20,10)用来得到它。对于每个参数,我们使用标头和那些特定参数发出 HTTP get 请求。

  3. 我们使用以下命令将响应转换为 Python 字典 response.json()

  4. 如果你输出这个,我们可以看到很多数据,我们需要在data键内,如下所示。如果你打印这个,你会得到下面的输出。

    data = response.json()['data'] 打印(数据)

输出

{'id': '5632394152',
 'type': 'user-reviews',
 'attributes': {'review': "I really like it. I have been using a memory trainer, but this one is a little bit more fun. I had to learn my skills the hard way so to speak, but it is really fun! I can't wait to buy it again! ",
  'rating': 5,
  'title': 'Cow force than',
  'date': '2020-03-08T11:37:29Z',
  'userName': 'MY VICELER',
  'isEdited': False}}
Run Code Online (Sandbox Code Playgroud)
  1. 所以你可以看到实际上我们想要的数据在attributes密钥后面,然后是rating密钥。因此我们想循环response.json()['data'])并访问一个['attribute']['value'],它给了我们输出。