我如何使用 NodeJS 获取一台 Nintendo Switch
你是不是也错过了热门的节日礼物?我也是……直到我用NodeJS给我的手机发送了购买提醒!
今年假期,我看到一款任天堂Switch套装特价,非常适合我哥哥和他的家人。售价299美元,但可惜的是,等我准备去买的时候已经售罄了,第三方黄牛把价格炒到了500多美元。真可惜。
不过,我注意到亚马逊的库存偶尔会以 299 美元的价格出售;但我一直没能幸运地在合适的时机查看。
那么软件工程师会怎么做呢?编写一个脚本来监控商品的库存情况,并在商品可以购买时向我的手机发送消息。而且,它真的奏效了!
工作原理。
剧本由三个相互配合的部分组成:
- 一个
AmazonPriceChecker通过产品 ID 获取亚马逊产品网页,并使用 JSDOM 查找当前价格的脚本。 - 这只是简单地通过我的个人 Telegram 机器人
TelegramBot发出一个简单的请求来提醒我的手机。 - 还有一个主服务器文件,它将所有功能连接起来,运行检查循环,检查价格阈值,并利用机器人向我的手机发出警报。
设置
这个项目所需的依赖项非常少。
- 如果您尚未安装NodeJS,则需要先安装它。
-
您还需要 TypeScript。我建议全局安装:
npm install -g typescript -
接下来你需要用到 `.`
jsdom和 `node-fetch.`。你可以package.json在项目目录中创建一个类似于下面的文件并运行npm install:{ "name": "rgthree-pricewatch", "description": "Watch prices.", "version": "0.0.1", "private": true, "dependencies": { "jsdom": "^16.4.0", "node-fetch": "^2.6.1" }, "devDependencies": { "@types/jsdom": "11.0.4", "@types/node": "^12.12.2", "@types/node-fetch": "^2.5.7" } }package.json
亚马逊价格查询器
我们只需要通过产品 ID 获取亚马逊产品的网页,然后使用 JSDOM 在 DOM 中查找当前价格,如果找到,则将其与产品本身的 URL 一起返回。
需要注意的是,我们是通过服务器获取网页的。我们会覆盖 User-Agent 参数,使其看起来像一个浏览器,但返回的响应将是原始 HTML,可能与我们在使用亚马逊服务时看到的标记有所不同,因为 JavaScript 很可能会在返回原始 HTML 后对页面进行大量修改。
因此,为了找到抓取价格的方法,我们将使用浏览器中的查看源代码功能来查看我们的脚本将看到的内容,而不是使用开发者工具。
幸运的是,不难发现亚马逊将价格信息封装在一个 id 为 的元素中priceblock_ourprice。(至少对于任天堂产品来说是这样;其他产品的加价可能有所不同。)
整体来看,我们的AmazonPriceChecker样子是这样的:
import fetch from 'node-fetch';
import {JSDOM} from 'jsdom';
/**
* Given a product id, will try to find the current price of the item on the
* Amazon page.
*/
export class AmazonPriceChecker {
private readonly url: string;
constructor(id: string) {
this.url = `https://www.amazon.com/gp/product/${id}`;
}
/**
* Returns a payload of the url for the product and it's current price, if
* found. If the price cannot be determined, it will be `NaN`.
*/
async check() {
try {
const response = await this.fetchProductPage();
const body = await response.text();
const doc = new JSDOM(body).window.document;
let price = Number(doc.querySelector('#priceblock_ourprice')?.textContent?.trim().replace('$',''));
return {url: this.url, price};
} catch(e) {
throw new Error(e);
}
}
private async fetchProductPage() {
return await fetch(this.url, {
method: 'get',
headers: {
'accept-language': 'en-US',
'Accept': 'text/html,application/xhtml+xml',
// Make our request look like a browser.
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.11 Safari/537.36',
}
});
}
}
我们的 TelegramBot
接下来,我们需要向手机发送提醒。我之前写过一篇关于如何创建个人 Telegram 机器人的文章,链接如下:
我们只需要机器人的 API 密钥和机器人所属的聊天 ID,然后就可以 ping 通聊天对象了。
import fetch from 'node-fetch';
/**
* Super simple telegram wrapper that sends messages to a bot specific chat.
*/
export class TelegramBot {
constructor(private botApiKey: string, private chatId: string) {}
async sendMessage(text: string) {
return await fetch(`https://api.telegram.org/bot${this.botApiKey}/sendMessage?chat_id=${this.chatId}&text=${encodeURIComponent(text)}`);
}
}
注意:您不必使用Telegram 来接收提醒。您可以修改此处的代码,例如发送电子邮件,或以其他方式触发 IFTTT 等。选择权在您手中,我选择使用 Telegram 是因为我已经设置了一个个人机器人来提醒我的手机 :)
将所有内容联系起来
现在我们有了这两个独立的部分,我们将把它们整合到主服务器文件中,并在其中循环每两分钟检查一次。
import {AmazonPriceChecker} from './amazon_watcher';
import {TelegramBot} from './telegrambot';
const TELEGRAM_API_KEY = 'YOUR_API_KEY';
const TELEGRAM_CHAT_ID = 'YOUR_API_KEYCHAT_ID';
// The Amazon product id. The XXX in
// https://www.amazon.com/dp/XXX or https://www.amazon.com/gp/product/XXX
const AMAZON_PRODUCT_ID = 'B08KB652Q2';
const TARGET_PRICE = 300;
const MS_MINUTES = 1000 * 60;
const BASE_TIMEOUT = MS_MINUTES * 2;
const telegram = new TelegramBot(TELEGRAM_API_KEY, TELEGRAM_CHAT_ID);
const priceChecker = new AmazonPriceChecker(AMAZON_PRODUCT_ID);
/**
* Checks the price with `priceChecker`, issues a message with `telegram` if
* it meets our threshold, and schedules another check.
*/
async function check() {
let timeout = BASE_TIMEOUT;
try {
const {url, price} = await priceChecker.check();
if (price) {
if (price <= TARGET_PRICE) {
telegram.sendMessage(`Price is: ${price}. Checking again in ${timeout / MS_MINUTES} minutes. ${url}`);
}
} else {
// If we can't parse the price, maybe something's wrong. We'll slow down
// our interval a bit.
timeout += MS_MINUTES * 5;
telegram.sendMessage(`Could not parse price. Trying again in ${timeout / MS_MINUTES}. ${url}`);
}
} catch(e) {
timeout += MS_MINUTES * 5;
telegram.sendMessage(`There was an error fetching the price. Will check again in ${timeout / MS_MINUTES} minutes.`);
console.error(e);
}
// Rinse & repeat.
setTimeout(() => { check(); }, timeout);
}
// Start it!
check();
console.log('Checker Started. Stop with Ctrl + C.');
启动它
首先,运行 TypeScript 编译器,它会根据我们规范化的 TypeScript 文件生成 JavaScript 文件:
tsc
然后使用Node.js运行我们的服务器文件:
node server.js
就这样!大约 4 小时后,我的手机收到了提醒,我可以直接打开亚马逊的产品页面购买 Switch,这很好,因为 4 分钟后我再次查看时,它的价格又涨回了 500 美元!
提高!
这只是我个人觉得可行的一个基础版本。我们可以以此为基础,添加更多功能,用于其他网店、不同产品,甚至可以把它变成一个完整的“机器人”,真正帮你购买产品等等。你可以把它看作是我们未来发展方向的起点。也许明年假日季就能用上了。
文章来源:https://dev.to/rg Three/how-i-got-a-nintendo-switch-using-nodejs-2eab