发布于 2026-01-06 1 阅读
0

我用 Cypress 做 Xbox 网站爬虫,一点也不后悔。

我用 Cypress 做 Xbox 网站爬虫,一点也不后悔。

和许多人一样,我也想入手一台新款 Xbox。但除了极少数勤奋的网购达人之外,我至今为止都未能如愿,反而不断看到类似这样的图片:

好市多

那么,一个有进取心/走投无路的网络开发人员会怎么做呢?当然是自己构建警报系统啦!

网络爬虫本身就是一个相当简单的应用,通常也是这类应用的理想用例。但我希望为其添加一些视觉元素,以确保不会出现误报,而且我个人也更喜欢用户界面而不是裸代码(毕竟我在Stackery工作)。此外,过去一个月左右我一直在使用Cypress测试套件,非常喜欢它在前端测试方面的应用,因此我一直在寻找更多将其应用到我的项目中的方法。

现在,我应该说:我猜这并不是Cypress.io 的开发人员在构建基于浏览器的测试库时所设想的用例,但正如一句名言所说,“你可以发明一把锤子,但你无法阻止第一个用户用它来敲自己的脑袋¹ ”。

所以,事不宜迟,让我们捶胸顿足,赶紧去买一台 Xbox 吧!

设置:注册一个 Cypress 账户

Cypress 有一个非常棒的功能,允许你在其 Web 应用中查看自动化测试运行的视频。要使用此功能,你需要一个免费的开发者帐户:

  1. 前往Cypress 注册页面并创建账户
  2. 进入控制面板后,创建一个新项目。项目名称可以随意取,比如“Xbox股票抓取器”、“测试灾难”等等。我通常把项目名称和代码仓库名称一样,因为我的脑回路就是这样。
  3. 现在,你需要记下这些信息projectId以及记录key,因为稍后会用到。

为你的爬虫创建一个无服务器架构

由于商店库存变化频繁,我们需要定期运行爬虫程序——初始阶段每小时运行一次,当然您可以根据需要轻松调整运行频率。当然,我们希望实现这些运行的自动化,因为关键在于您还有自己的生活,不想频繁刷新网页。是不是只有我一个人觉得这很像一个理想的无服务器用例?不只是我这么觉得?我就知道!

我最初想用 Lambda 函数来运行整个程序,但经过几个小时的摸索,我发现这真的非常非常难,而且最终并不值得,因为CodeBuild 作业就能很好地完成这项工作。

我将使用 Stackery 构建我的技术栈,所以这些说明将围绕这个工作流程展开。这部分是可选的,因为你也可以在 AWS 控制台中完成这些操作,但我喜欢用更简单的方式,而 Stackery 的简易模式2就是无服务器架构。

  1. 如果您还没有 Stackery 帐户,请创建一个免费的 Stackery 帐户。
  2. 导航至 /stacks 目录,然后点击“添加 Stack”下拉箭头,选择“使用新仓库”。以下是我的操作界面:
    xbox-1

  3. 通常情况下,您需要在设计画布中逐个添加资源,但由于此堆栈主要基于 CodeBuild 作业和相关角色,因此可以更轻松地复制粘贴 AWS SAM 模板,如下所示:

xbox压缩

在编辑模式,单击“模板”,清除现有模板,然后粘贴以下内容:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  SendMessage:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-SendMessage
      Description: !Sub
        - Stack ${StackTagName} Environment ${EnvironmentTagName} Function ${ResourceName}
        - ResourceName: SendMessage
      CodeUri: src/SendMessage
      Handler: index.handler
      Runtime: nodejs12.x
      MemorySize: 3008
      Timeout: 30
      Tracing: Active
      Policies:
        - AWSXrayWriteOnlyAccess
        - SNSPublishMessagePolicy:
            TopicName: !GetAtt XboxAlert.TopicName
      Events:
        EventRule:
          Type: EventBridgeRule
          Properties:
            Pattern:
              source:
                - aws.codebuild
              detail-type:
                - CodeBuild Build State Change
              detail:
                build-status:
                  - SUCCEEDED
                  - FAILED
                project-name:
                  - cypress-xbox-scraper
          Metadata:
            StackeryName: TriggerMessage
      Environment:
        Variables:
          TOPIC_NAME: !GetAtt XboxAlert.TopicName
          TOPIC_ARN: !Ref XboxAlert
  CodeBuildIAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          Effect: Allow
          Principal:
            Service: codebuild.amazonaws.com
          Action: sts:AssumeRole
      RoleName: !Sub ${AWS::StackName}-CodeBuildIAMRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AdministratorAccess
  CypressScraper:
    Type: AWS::CodeBuild::Project
    Properties:
      Artifacts:
        Type: NO_ARTIFACTS
      Description: Cypress Xbox Scraper
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:2.0
        Type: LINUX_CONTAINER
        PrivilegedMode: true
      Name: cypress-xbox-scraper
      ServiceRole: !Ref CodeBuildIAMRole
      Source:
        BuildSpec: buildspec.yml
        Location: https://github.com/<github-user>/<repo-name>.git
        SourceIdentifier: BUILD_SCRIPTS_SRC
        Type: GITHUB
        Auth:
          Type: OAUTH
  CypressScraperTriggerIAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          Effect: Allow
          Principal:
            Service:
              - events.amazonaws.com
          Action: sts:AssumeRole
      Policies:
        - PolicyName: TriggerCypressScraperCodeBuild
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - codebuild:StartBuild
                  - codebuild:BatchGetBuilds
                Resource:
                  - !GetAtt CypressScraper.Arn
      RoleName: !Sub ${AWS::StackName}-CypressScraperTriggerRole
  TriggerScraper:
    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: rate(1 hour)
      State: ENABLED
      RoleArn: !GetAtt CypressScraperTriggerIAMRole.Arn
      Targets:
        - Arn: !GetAtt CypressScraper.Arn
          Id: cypress-xbox-scraper
          RoleArn: !GetAtt CypressScraperTriggerIAMRole.Arn
  XboxAlert:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: !Sub ${AWS::StackName}-XboxAlert
Parameters:
  StackTagName:
    Type: String
    Description: Stack Name (injected by Stackery at deployment time)
  EnvironmentTagName:
    Type: String
    Description: Environment Name (injected by Stackery at deployment time)
Enter fullscreen mode Exit fullscreen mode

我们来详细分析一下。对于刚接触无服务器架构的朋友,这是一个AWS SAM模板。虽然使用 Stackery 通常可以避免编写模板文件,但仍有几点需要注意,并且其中一行需要您输入自己的数据。

我们先来看第 55-74 行:

  CypressScraper:
    Type: AWS::CodeBuild::Project
    Properties:
      Artifacts:
        Type: NO_ARTIFACTS
      Description: Cypress Xbox Scraper
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:2.0
        Type: LINUX_CONTAINER
        PrivilegedMode: true
      Name: cypress-xbox-scraper
      ServiceRole: !Ref CodeBuildIAMRole
      Source:
        BuildSpec: buildspec.yml
        Location: https://github.com/<github-user>/<repo-name>.git
        SourceIdentifier: BUILD_SCRIPTS_SRC
        Type: GITHUB
        Auth:
          Type: OAUTH
Enter fullscreen mode Exit fullscreen mode

这是一个 CodeBuild 项目,它将用于在 AWS 的某个神奇服务器环境中,以 Linux 容器的形式运行 Cypress。您需要将第 70 行替换为您刚刚创建的 Git 仓库。这也意味着您可能需要向 AWS 验证您的 Git 提供商,稍后我会详细介绍。

第 101 行允许您更改消息发送频率。点击此处了解更多关于 AWS 调度表达式的信息

现在,如果您切换回可视化模式,您会发现一些资源已根据模板自动填充:

Xbox-3

其中包括:

  • TriggerScraper:每小时触发一次 Cypress CodeBuild 作业的 CloudWatch 事件规则
  • TriggerMessageSendMessage:当 CodeBuild 作业成功或失败时触发该函数的 EventBridge 规则
  • SendMessage:用于在 Xbox 重新到货时向社交媒体发送消息的 Lambda 函数
  • XboxAlert用于发送短信的 SNS 主题

您可以双击每个资源来查看其各自的设置。

瞧瞧:整个后端都搭建好了,而且你甚至都不用打开 AWS 控制台!

  1. 点击“提交...”按钮将其提交到您的 Git 仓库,然后点击堆栈名称下方的链接访问您的新仓库 URL,将堆栈克隆到本地,并在您喜欢的 VSCode(或其他文本编辑器,如果必须的话)中打开它。

Xbox-2

开始编写代码!

如您所见,Stackery 为您的函数创建了一些目录,以及一个您可以部署的 AWS SAM 模板。谢谢 Stackery!

首先,我们需要添加 Cypress:

  1. 从仓库根目录运行npm install cypress --save
  2. 安装完成后,运行./node_modules/.bin/cypress open

Cypress 会创建一个包含大量示例代码的目录。您可以删除该目录并cypress/integration/examples重新创建cypress/integration/scraper.spec.js。以下是该目录中将包含的内容:

// xbox-stock-alert/cypress/integration/scraper.spec.js

describe('Xbox out-of-stock scraper', () => {
  it('Checks to see if Xboxes are out of stock at Microsoft', () => {
    cy.visit('https://www.xbox.com/en-us/configure/8WJ714N3RBTL', {
      headers: {
        "Accept-Encoding": "gzip, deflate",
        "keepAlive": true
      }
    });
    cy.get('[aria-label="Checkout bundle"]')
      .should('be.disabled')
  });
});
Enter fullscreen mode Exit fullscreen mode

让我们来详细分析一下:

  1. Cypress 将访问一个特定的 URL——在本例中,它是 Xbox Series X 主机的产品页面。
  2. 添加这些头部信息后,页面就能正常加载,而不会出现可怕的ESOCKETTIMEDOUT 错误(我可是吃过亏才发现这一点的,所以你们就不用再经历一遍了!)
  3. Cypress 会查找包含“Checkout bundle”的元素aria-label,并检查它是否被禁用。如果被禁用,则测试结束,并被视为成功。如果未被禁用,则测试失败(但我们都知道它已经尽力了)。

那么,为什么是特定的“结账套装”元素呢?嗯,如果你在浏览器中打开 Xbox 页面并检查它,你会发现它实际上是 Xbox 有货时才会启用的结账按钮:

结账按钮

让我们把这破事儿自动化吧!

好了,我们已经编写好了测试,并且设置了一个定时器,让它每小时运行一次。现在我们需要添加一个 CodeBuild 作业来实际运行这个测试。我们还需要在我们的SendMessage函数中添加代码,以便在测试失败时通知我们,这意味着“结账”按钮已启用,我们离拥有新 Xbox 又近了一步。

还记得你很久以前记下的那张柏树projectId唱片key吗?现在它们就派上用场了。

在根目录下创建一个名为 `.md.txt` 的新文件,并添加以下内容buildspec.yml并保存

version: 0.2
phases:
  install:
    runtime-versions:
      nodejs: 10
  build:
    commands:
      - npm install && npm run cypress -- --headless --browser electron --record --key <your-record-key>
Enter fullscreen mode Exit fullscreen mode

打开cypress.json并替换为以下内容,然后保存:

{
  "baseUrl": "https://www.xbox.com/en-us/configure/8WJ714N3RBTL",
  "defaultCommandTimeout": 30000,
  "chromeWebSecurity": false,
  "projectId": "<your-projectId>"
}
Enter fullscreen mode Exit fullscreen mode

接下来,我们将添加测试失败时发送警报的功能代码。打开src/SendMessage/index.js并替换为以下内容:

// xbox-stock-alert/src/SendMessage/index.js

const AWS = require('aws-sdk');
const sns = new AWS.SNS({region: 'us-west-2'});

const message = 'Xbox alert! Click me now: https://www.xbox.com/en-us/configure/8WJ714N3RBTL';
const defaultMessage = 'No Xboxes available, try again later';

exports.handler = async (event) => {
  // Log the event argument for debugging and for use in local development
  console.log(JSON.stringify(event, undefined, 2));
  // If the CodeBuild job was successful, that means Xboxes are not in stock and no message needs to be sent
  if (event.detail['build-status'] === 'SUCCEEDED') {
    console.log(defaultMessage)
    return {
      statusCode: 200,
      body: defaultMessage
    };
  } else if (event.detail['build-status'] === 'FAILED') {
    // If the CodeBuild job failed, that means Xboxes are back in stock!
    console.log('Sending message: ', message);

    // Create SNS parameters
    const params = {
      Message: message, /* required */
      TopicArn: process.env.TOPIC_ARN,
      MessageAttributes: {
        'AWS.SNS.SMS.SMSType': {
          DataType: 'String',
          StringValue: 'Promotional'
        },
        'AWS.SNS.SMS.SenderID': {
          DataType: 'String',
          StringValue: 'XboxAlert'
        },
      },
    };

    try {
      let data = await sns.publish(params).promise();
      console.log('Message sent! Xbox purchase, commence!');
      return { 
        statusCode: 200,
        body: data
      };
    } catch (err) {
      console.log('Sending failed', err);
      throw err;
    }
  }
  return {};
};
Enter fullscreen mode Exit fullscreen mode

哦,对了,你最好把node_modules`and`添加package-lock.json到你的`<path>` 中.gitignore,除非你喜欢污染 Git 仓库。

是时候部署这辆狠角色了

请务必执行 `git add`、`git commit` 和 `git push` 命令来提交更改。部署时,AWS 需要访问您的 Git 提供商。如果您之前从未设置过访问令牌,请按照以下说明在您的账户中设置访问令牌。(这份文档对像我这样的新手也可能很有帮助。)

如果你像你这样既聪明又帅气的开发者一样,使用 Stackery 进行部署,那么你只需要在你的代码仓库根目录下运行以下命令:

stackery deploy
Enter fullscreen mode Exit fullscreen mode

这需要几分钟时间,在此期间,你可以畅想一下当新的 Xbox 连接到你的 4K 电视上时会有多么棒。

等待gif

完成了吗?好的!下一步:添加您的手机号码以接收短信提醒。

我可以要你的电话号码吗?

正如我上面提到的,你的堆栈中创建的资源之一是XboxAlert SNS 主题。它是在部署期间创建的,但目前它没有任何作用。让我们来改变这种情况。

  1. 打开 AWS 控制台,并导航至 SNS 控制面板。
  2. 在“主题”,您应该会看到您刚刚创建的主题,名称类似于“主题名称” xbox-stock-alert-<env>-XboxAlert。点击它的名称。
  3. 点击橙色的大“创建订阅”按钮
  4. 请按如下方式填写表格,包括您的手机号码,然后再次点击“创建订阅”

订阅

如果您之前没有在社交网络上使用过您的手机号码,则需要验证您的手机号码,然后就可以开始使用了!

测试时间

现在您仍然在 AWS 环境中,应该可以打开 CodeBuild 控制台并看到其中新建了一个项目:

Xbox 4

在设置好并置之后,您需要手动运行一次以确保一切正常。请选择您的项目并点击“开始构建”按钮。构建过程也需要一些时间,但您可以通过点击项目名称并选择最近一次的构建运行来查看 CloudWatch 日志。

没视频就等于没发生过

希望你的构建过程一切顺利(如果失败了,请联系我——我想我在构建过程中已经遇到过所有错误,或许可以帮上忙)。

但如何才能确定呢?你可以回到 Cypress.io 上的项目,看看最近几次运行的结果。如果一切顺利,你就能看到无头浏览器运行你的测试用例的视频了!

Xbox 5

如果有一天测试失败了🤞,你会立即收到手机通知,告诉你Xbox就在那里等着你。祝你好运!

笔记

1这句话其实是我编的,但我猜锤子的发明者可能说过类似的话。2
句话也是我编的,但这并不妨碍它的真实性。3
更好的方法是使用存储在AWS Systems Manager Parameter Store中的环境变量来存储记录键,但为了简洁起见,我的示例使用了硬编码。如果你要照搬我这个不太好的例子,请务必确保你的代码库是私有的🙏

后记

虽然可以扩展抓取程序规范以添加更多零售商,但我遇到了一些问题,例如沃尔玛的机器人检测器:

沃尔玛

我没能成功运行这些程序,总是出错,但也许其他人运气更好,可以分享他们的解决方案:

// xbox-stock-alert/cypress/integration/scraper.spec.js

describe('Xbox out-of-stock scraper - more retailers', () => {
  it('Checks to see if Xboxes are out of stock at GameStop', () => {
    cy.visit('https://www.gamestop.com/accessories/xbox-series-x/products/xbox-series-x/11108371.html?condition=New', {
      headers: {
        "Accept-Encoding": "gzip, deflate",
        "keepAlive": true
      }
    });
    cy.get('span.delivery-out-of-stock')
    cy.get('span.store-unavailable')
  });
  it('Checks to see if Xboxes are out of stock at Best Buy', () => {
    cy.visit('https://www.bestbuy.com/site/microsoft-xbox-series-x-1tb-console-black/6428324.p?skuId=6428324', {
      headers: {
        "Accept-Encoding": "gzip, deflate",
        "keepAlive": true
      }
    });
    cy.get('[data-sku-id="6428324"]')
      .should('be.disabled')
  });
  it('Checks to see if Xboxes are out of stock at Walmart', () => {
    cy.visit('https://www.walmart.com/ip/Xbox-Series-X/443574645', {
      headers: {
        "Accept-Encoding": "gzip, deflate",
        "keepAlive": true
      }
    });
    cy.get('.spin-button-children')
      .contains('Get in-stock alert');
  });
  it('Checks to see if Xboxes are out of stock at Costco', () => {
    cy.visit('https://www.costco.com/xbox-series-x-1tb-console-with-additional-controller.product.100691493.html', {
      headers: {
        "Accept-Encoding": "gzip, deflate",
        "keepAlive": true
      },
      pageLoadTimeout: 60000
    });
    cy.get('.oos-overlay')
  });
});
Enter fullscreen mode Exit fullscreen mode
文章来源:https://dev.to/annaspies/i-used-cypress-as-an-xbox-web-scraper-and-i-regret-nothing-1bn4