用 JavaScript 破解我的蜜月之旅🦒

2025-06-08

用 JavaScript 破解我的蜜月之旅🦒

想看更多类似的精彩内容吗?订阅我的新闻邮件,请访问:alec.coffee/signup

当我妻子在 Instagram 上看到这个帖子时,她立刻被迷住了:

肯尼亚蜜月即将到来,我们开始预订房间。我咨询了几年前去过肯尼亚的阿姨,她住在这里,预订起来很顺利。但当我们得知这家酒店提前一两年就订满了时,我们感到很惊讶。

突然爆红肯定有原因。稍微查了一下,发现这家店最近上了艾伦秀

该死,艾伦。

起初,我们查看了他们的网站,看看我们去肯尼亚的日期是否有空,但没找到。之后我们给庄园发了邮件,结果还是没收到,被告知我们被列入了“候补名单”。可能是因为要和候补名单上的其他人竞争,而且我们的行程只有几个月了,我和妻子的希望变得渺茫。

寻找解决方案

他们用来显示房间可用性的网站是只读的,没有预订房间的功能。

尽我所能点击只读元素

打电话和发邮件是联系他们的唯一方式,过程缓慢而艰巨。我以为等到有空位时,他们的网站会更新,然后才会开始联系候补名单上的人。这样,即使有人落选他们仍然会有预订。

假设

我接下来想的是,如果我们在房间空出来那天联系他们,很可能就能绕过候补名单。但每小时都查看网站可不是什么好玩的事。

我穿上程序员的裤子,心想这应该能派上用场,一个老派的网络抓取工具,爵士乐手。我每30分钟访问一次网站,然后给我和妻子的手机发短信,这样我们就可以给他们打电话了。这个90年代的肯尼亚网站不太可能有防机器人程序的保护措施。

看起来像是一张简单的表格,结果就是一张简单的表格:

// Example of a unbooked day HTML node

<td
  width="25"
  unselectable="on"
  ab="0"
  style="border-top: none; "
  name="WB15:Salas Camp:Keekorok Honeymoon
  Tent-Tent 1:0*:1:11e8485f8b9898cc8de0ac1f6b165406:0"
  id="WB15:07:28:2019"
  darkness="0"
  onmousedown="mouseDownFunction(arguments[0]);"
  onmouseup="cMouseUp(arguments[0]);"
  onmouseover="mouseOverFunction(arguments[0]);"
  class="overbooking calIndicator0"
>
  1
</td>
Enter fullscreen mode Exit fullscreen mode

这就是我需要找到的,如果节点文本是1,则它可用。

在研究了简单的 HTML 结构之后,我开始编写 Node.js 服务来废弃它。我偶然发现了一个 NPM 模块crawler,它提供了我所需的一切。

const Crawler = require("crawler");

const startCrawler = async () => {
  return new Promise(resolve => {
    const c = new Crawler({
      maxConnections: 10,
      callback: (error, res, done) => {
        if (error) {
          console.log(error);
          throw new Error(
            `Error with sending request to website! ${JSON.stringify(error)}`
          );
        }
        const $ = res.$;
        // get the table of bookings
        const results = $("#tblCalendar tbody tr").slice(12, 17);
        done();
        // return the results
        resolve(results);
      }
    });
    // hit giraffe manors website
    c.queue(
      "http://thesafaricollection.resrequest.com/reservation.php?20+2019-02-08" +
        "+RS12:RS14:RS16:WB656:RS2274+15:20:30:25++WB5++n/a++true+true+0+0"
    );
  });
};
Enter fullscreen mode Exit fullscreen mode

这需要一些调试,但现在我可以使用 Giraffe Manors 网站的 HTML 来玩了。

接下来,我使用名为cheerio的 NPM 包搜索结果

const parseResults = async () => {
  let availability = false;

  // get HMTL
  const results = await startCrawler();

  for (let x = 0; x < results.length; x++) {
    // Feb 13th - Feb 20th
    const validDates = cheerio(results[x]).find("td").slice(7, 14);
    // See if any of the dates are not booked
    for (let y = 0; y < validDates.length; y++) {
      if (parseInt(validDates[y].children[0].data, 10) === 1) {
        availability = true;
      }
    }
  }
  ...
Enter fullscreen mode Exit fullscreen mode

现在到了最有意思的部分,当房间显示可用时,我会给我妻子发短信。我用的是Twilio,不过还有很多其他服务。这需要注册一个免费账户,我知道我最多只会发几条短信。

  ...
  // send text message if availability
  if (availability) {
    // Your Account Sid and Auth Token from twilio.com/console
    const accountSid = process.env.ACCOUND_SID;
    const authToken = process.env.AUTH_TOKEN;
    const twilio = require("twilio");
    const client = twilio(accountSid, authToken);

    client.messages
      .create({
        body: "Giraffe manor is available for our dates!",
        from: process.env.SMS_FROM,
        to: process.env.SMS_TO
      })
      .then(message => console.log(`Sent a text! ${message.sid}`))
      .done();
    return;
  }
  console.log("No availability!");
}
Enter fullscreen mode Exit fullscreen mode

经过几个未预订日期的测试,它成功了!现在将其设置为每5分钟运行一次(何乐而不为呢?)。

const schedule = require("node-schedule");

schedule.scheduleJob("*/5 * * * *", () => {
  console.log("Running availability checker!");
  try {
    main();
  } catch (e) {
    console.log(`Error! ${JSON.stringify(e)}`);
  }
});
Enter fullscreen mode Exit fullscreen mode

为了托管和运行代码,我选择了Heroku,因为我有使用经验,而且知道它的免费套餐能满足我的需求。我不知道他们的免费套餐是如何支持后台服务作业的,不过不管怎样,我还是会选择 Heroku。

几周后(我居然忘了它还在运营),我妻子的手机收到了短信!我们立刻给他们打电话,然后就收到了!就像我们希望的那样,好像绕过了候补名单。她收到了一连串的短信,还用完了我在 Twilio 上的免费套餐,因为我没有在找到空房时写一个停止方法。

我特别喜欢这样做,因为在我的生活中我并不经常用代码来解决问题,但我认为对于这样的图片来说这是值得的:

我和泽娜长颈鹿庄园照片

这是我运用编程技能解决“现实”问题的一个例子。我很想听听你解决了什么问题,请在这里评论。

代码

喜欢这篇文章吗?不妨请我喝杯咖啡,支持我继续写下去。

想接收季度更新邮件吗?订阅我的新闻通讯

鏂囩珷鏉ユ簮锛�https://dev.to/aleccool213/hacking-my-honeymoon-with-javascript-11fg
PREV
使用 DigitalOcean 上的 Dokku 创建你自己的 Heroku ⚡ 简介
NEXT
开发过程中应避免的 5 件事