スマレジの画像ファイル取得

パンのカタログに使用したデータはスマレジのバックアップデータから取得しました。データソースを一元化することで、実際とカタログの差を最小限度に縮めます。画像自体はこちらからアップロードしたものですが、商品名と価格と画像の整合性を保持するのにスマレジデータを利用すのが一番確実な方法でしょう。

まず、自分のデータベースに取り込むためのsqlスクリプトを作成します。

.mode csv
select productId,categoryId,productCode,productName,price,image,icon from product;

これを次のようなコマンドを実行すれば商品名や価格等の記載されているproduct.csvを得ることができます。

sqlite3 pos/pos.sqlite < products.sql > datas/product.csv 

このCSVファイルを取り込むプログラムを作成してデータベースに追加すればよいのですが、スマレジの有料会員になればデータへのAPIが公開されているようです。ただオープンしたばかりのパン屋さんでは体力が充分ではないため、すべてを自前で賄うことにしています。

image、iconフィールドはスマレジに表示される商品のイメージとアイコンへのURLです。お店の造作やチラシ配りの手伝いはしますが、私には商品知識はまったくないため、商品名と画像の一致は嬉しいデータです。ホームページから他所様への直リンクはマズいので、一旦ダウンロードさせてもらってから利用することにしました。

GO言語でproduct.csvを置いた場所にdownload_img.goというファイルを作成しました。まずは、csvファイルを読み込んで処理する部分のメイン処理です。

package main

import (
    "log"
    "flag"
    "os"
    "os/exec"
    "io"
    "encoding/csv"
    "path/filepath"
    "strings"
)

func main() {
    log.Println("Start")
    flag.Parse()

    args := flag.Args()
    if len(args) == 0 {
        return
    }
    fname := args[0]
    _, err := os.Stat(fname)

    if err != nil {
        log.Println("File:",err.Error())
        return
    }

    getDatas(fname)

    log.Println("End")
}

実行時の引数は先程作成したproduct.csvファイルの場所を指定します。

GO言語の場合(でなくても)、スクリプトが完全に出来上がっていないとエラーで動作しません。次にgetDatas関数を作成します。

func getDatas(fname string) {
    file1, err := os.Open(fname)
    if err != nil {
        return
    }
    defer file1.Close()

    reader := csv.NewReader(file1)
    reader.LazyQuotes = true

    for {
        rec, err := reader.Read()
        if err == io.EOF {
            break
        }
        if len(rec) > 5 {
            img_url := rec[5]

            if img_url != "" && strings.Contains(img_url, "https") {
                ExecDownload(img_url)
            }
            icon_url := rec[6]
            if icon_url != "" && strings.Contains(icon_url, "https") {
                ExecDownload(icon_url)
            }
        }
    }
}

csvのreaderを作成してEOFを読み込むまでループすれば順次処理することができます。読み込んだCSVデータの配列からimageファイル、iconファイルのURLをそれぞれ得ています。このURLにアクセスしてダウンロードします。

func ExecDownload(url string) {

    filename := filepath.Base(url)

    img, err := exec.Command("curl", url).Output()
    if err != nil {
        log.Println("Exec:", err.Error())
    }
    SaveImage(img, filename)
    cmd := exec.Command("sleep", "5s")
    cmd.Start()
    log.Println(filename)
    cmd.Wait()
}

URLアクセスは安直にcurlを呼び出すだけです。結果はimg変数に返されます。これをファイルとして保存していますが、次のアクセスまで5秒待機させています。これは、ダウンロードとファイル保存を同期させて動かすのが難しいことと、連続してアクセスするとサイトへのアタックと間違えてブロックされないようにするためです。何でも早ければいいと言うわけではありませんね。

func SaveImage(img []byte, fname string) {
    file, err := os.Create("./smaregi/" + fname)

    if err != nil {
        log.Println("SaveImage", err.Error())
        return
    }
    defer file.Close()

    _, err = file.Write(img)

    if err != nil {
        log.Println("SaveImage:Write:", err.Error())
    }
}

ファイルは後でまとめやすいように、実行ディレクトリにsmaregiディレクトリを作成してください。上記までに作成したプログラムは次のように実行します。

go run download_img.go product.csv

Palpanではそれほど商品数も多くないので、5秒間隔で動いてもせいぜい10分から15分でダウンロード完了です。