$shibayu36->blog;

エンジニアをしています。プログラミングや読書のことなどについて書いています。

puppeteerを使ってBOOTHの注文ごとの未発送商品を一覧化する

最近妻が通販のためBOOTHを使っている。それでそろそろ発送するかと思っていたところ、BOOTHの注文一覧では、購入者名や金額は表示されているものの、購入した商品が表示されていない。購入した商品を確認するためには注文詳細を一つずつ見るしかない。これだと梱包するためには、一つ一つ注文詳細を開いて商品を見て、また一覧に戻って...と何度も繰り返す必要があり、死にたい気持ちになった。

梱包を楽にするためには、「注文者名 -> 未発送の商品一覧」というフォーマットで全ての注文をリストアップ出来ると良い。最近はpuppeteerというツールを使うと、Headless Chromeを使ってスクレイピング出来て便利ということを聞いていたので、これを利用してリストアップをしてみた。実装は以下のとおり。

https://github.com/shibayu36/booth-tools/blob/master/get-booth-orders.js

// Usage
// PIXIV_ID=... PIXIV_PASSWORD=... node get-booth-orders.js

const puppeteer = require('puppeteer');
const fs = require('fs');

// 注文リスト1ページから購入情報を抜き出す
async function getOrders(browser, page) {
  let orderPage = await browser.newPage();

  // 商品リンクを抜き出す
  let orderLinks = await page.evaluate(() => {
    return Array.from(document.querySelectorAll('a.link-ext')).map(a => a.href);
  });

  // それぞれの商品詳細から名前、商品リストを抜き出す
  let rows = [];
  for (let link of orderLinks) {
    await orderPage.goto(link);
    let row = await orderPage.evaluate(() => {
      let panel = document.querySelectorAll('.manage-panel')[0];
      let name = panel.querySelector('.manage-order-shipment-summaries .u-tpg-slight-body').textContent.match(/(.+)\s+様/)[1];
      let productContainers = panel.querySelectorAll('.manage-order-content');
      let products = Array.from(productContainers).map(c => c.querySelector('.u-tpg-slight-body').innerText.trim() + " : " + c.querySelector('.u-text-right b').innerText);
      return name + "\t" + products.sort().reverse().join("\t");
    });
    console.log(row);
    rows.push(row);
  }

  await orderPage.close();
  return rows;
}

(async () => {

  let browser = await puppeteer.launch();
  let page = await browser.newPage();

  // loginする
  await page.goto('https://accounts.pixiv.net/login?view_type=popup&source=booth&return_to=https%3A%2F%2Faccounts.pixiv.net%2Fpopup.html%23https%3A%2F%2Fbooth.pm%2Fusers%2Fsign_in');
  await page.type('#LoginComponent input[autocomplete="username"]', process.env.PIXIV_ID);
  await page.type('#LoginComponent input[autocomplete="current-password"]', process.env.PIXIV_PASSWORD);
  await page.click('#LoginComponent button[type="submit"]');
  await page.goto('https://booth.pm/users/sign_in');
  console.log("finish to login");

  // 未発送ページへ
  await page.goto('https://manage.booth.pm/orders?state=paid');

  // 未発送ページを全部たどって購入状況を全てたどる
  let results = [];
  while (true) {
    console.log("start : " + page.url());
    let rows = await getOrders(browser, page);
    results = results.concat(rows);

    let nextLink = await page.$('a[rel="next"]');
    if (nextLink) {
      nextLink.click();
      await page.waitForNavigation({timeout: 60000, waitUntil: "domcontentloaded"});
    }
    else {
      break;
    }
  }

  fs.writeFileSync('result.tsv', results.join("\n"));

  browser.close();

  console.log('done');
})();

これを使ってPIXIV_ID=... PIXIV_PASSWORD=... node get-booth-orders.jsと実行すると、

  • 1. 自動でBOOTHにログインして
  • 2. 未発送の注文一覧を開き
  • 3. 注文詳細を一つずつ見ながら未発送商品を抽出し
  • 4. 最終的にresult.tsvに、(注文者名)\t(商品名1)\t(商品名2)\t...のリストを出力する

ということを自動でやってくれる。ページャも自動でたどるので、未発送の注文を全て辿れる。

あとはresult.tsvをGoogle Spreadsheetにでも貼っておけば、誰に何を送ればよいか一目でわかり、梱包するだけとなる。便利!

しかし、こんなことをしたくないので、BOOTHの注文一覧に未発送商品も表示してほしい...