ダブル11に乗じて、京東商品を書いて自動注文します


プロジェクトのアドレスはstarを求めます
今、商店は1年商品を売らないで、双11は1年の商品を売ってみんなが知っている事実で、総じて蚊の足の価格を調整しなければなりません.
もともと11組のパソコンに乗ってZ 370のボードUセットを買おうと思っていたのですが、京東の8700 kはずっと品切れの状態で、ここ数日品切れになり、価格が3999に上昇して我慢できず、下板Uセットを見たほうがお得でしたが、一部のボードUセットは自動注文に対応していないので、gayhubは爬虫類が入荷自動注文を監視できるかどうかを調べて、ちょうどこの兄たちのjd-autobuy Pythonスクリプトがあったので、それからGoの、インタフェースがすでにそろっていることを見て、nodeバージョンの助興に来ます
今回使用したhttpライブラリはaxiosで、クライアントとサービス側をサポートしています.総じて文法は簡潔ですが、その前にsuperagentライブラリがあります.見ても悪くありませんが、superagentはresponseで多く処理されています.
vueでaxiosを使っているので、今回はサービス側の能力を試してみたいと思います.相変わらずいいですね.
先にrequest headerを書いて、結局サービス側で、ブラウザがあなたにUser-Agentを処理することを手伝っていないので、自分でブラウザに行って要求してからheaderを手に入れます
const defaultInfo = {
    header: {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
        'Content-Type': 'text/plain;charset=utf-8',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.6,en;q=0.4,en-US;q=0.2',
        'Connection': 'keep-alive',
    },
}

headerは私达を手に入れてブラウザに伪装してQRコードのピクチャーを要求することができて、京东のスキャンのピクチャーの住所のhttps://qr.m.jd.com/show、余分な技巧がなくて、直接axiosでgetの要求をします
async function requestScan() {
    const result = await request({
        method: 'get',
        url: 'https://qr.m.jd.com/show',
        headers: defaultInfo.header,
        params: {
            appid: 133,
            size: 147,
            t: new Date().getTime()
        },
        responseType: 'arraybuffer'
    })
}

パラメータappid sizetはバッグをつかむことで手に入れることができます.ここで私のresponseType用のarraybufferに注意してください.デフォルト値はjson です.bufferは主にローカルで画像を書き込むのに便利です.resを処理します.
defaultInfo.cookies = cookieParser(result.headers['set-cookie'])
defaultInfo.cookieData = result.headers['set-cookie'];
const image_file = result.data;
await writeFile('qr.png', image_file)
async function writeFile(fileName, file) {
    return await new Promise((resolve, reject) => {
        fs.writeFile(fileName, file, 'binary', err => {
            opn('qr.png')
            resolve()
        })
    })
}

このステップのcookieはすでに手に入れて、ここで私は2つのステップの処理をして、1歩は自分で書いたcookieParserでパラメータを解析して、主にその中のwlfstk_smdlを手に入れて、次に使って、それから直接writeFileでピクチャーを書いて、書いた後にopnを利用してピクチャーを開けて、sindresorhus大神のopnライブラリはやはり使いやすくて、プログラムを指定してピクチャーを開けることができて、ファイルなど
スキャンする前にスキャンのステータスを監視します
async function listenScan() {

    let flag = true
    let ticket

    while (flag) {
        const callback = {}
        let name;
        callback[name = ('jQuery' + getRandomInt(100000, 999999))] = data => {
            console.log(`   ${data.msg || '    ,    '}`)
            if (data.code === 200) {
                flag = false;
                ticket = data.ticket
            }
        }

        const result = await request({
            method: 'get',
            url: 'https://qr.m.jd.com/check',
            headers: Object.assign({
                Host: 'qr.m.jd.com',
                Referer: 'https://passport.jd.com/new/login.aspx',
                Cookie: defaultInfo.cookieData.join(';')
            }, defaultInfo.header),
            params: {
                callback: name,
                appid: 133,
                token: defaultInfo.cookies['wlfstk_smdl'],
                _: new Date().getTime()
            },
        })

        eval('callback.' + result.data);
        await sleep(1000)
    }

    return ticket
}

最初はタイマーをつけてポーリングしたいと思っていました.「いいか悪いか」は、私が1秒もしないうちに聞いてみました.ここではasync/awaitの強力な機能を使ってsleepを実現し、settimoutの使用よりも優雅で非同期の処理も自在に操作できます.
function sleep(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
        }, ms)
    })
}

ここではheaderを組み合わせて、さっき手に入れたクッキーを持ってきて、hostrefererを加えて、私たちがどこからどこへ行くかを示します.パラメータの中のtokenは前にクッキーを解析して手に入れたwlfstk_です.smdl、このインタフェースは約束すべきjQuery jsonp(京東はjsonpを見ても多い)なので、私はここでcallbackを使ってjsonpの実行をシミュレートして、戻ってきたcodeとmsgを見て、codeが200の時にスキャンコードが成功したことを説明して、この時msgはなくて、カスタマイズの下で、その他の状態はmsgがあって、直接出力すればOKで、スキャンコードが成功して私たちはticketを手に入れなければなりません.これは文字通りわかりますが、お兄さんは入場券を手に入れました.そしてチケットを注文するときも必要です.貯金してください.
この时、あなたの携帯で京东を开けて开いたQRコードの画像をスキャンして、确认してスキャンに成功して、入场券で登录します
async function login(ticket) {
    const result = await request({
        method: 'get',
        url: 'https://passport.jd.com/uc/qrCodeTicketValidation',
        headers: Object.assign({
            Host: 'passport.jd.com',
            Referer: 'https://passport.jd.com/uc/login?ltype=logout',
            Cookie: defaultInfo.cookieData.join('')
        }, defaultInfo.header),
        params: {
            t: ticket
        },
    })

    defaultInfo.header['p3p'] = result.headers['p3p']
    return defaultInfo.cookieData = result.headers['set-cookie']
}

この一歩は何も言わないで、入場券があって、当然登録に成功したはずで、p 3 pパラメータを手に入れてクッキーを更新するという合法的な身分が誕生しました.
アイデンティティがあればget商品ページに行けますが、このステップでは3つのリクエストの情報をつづる必要があります.
商品ページのhtmlを入手
function goodInfo(goodId) {

    const stockLink = `http://item.jd.com/${goodId}.html`

    return request({
        method: 'get',
        url: stockLink,
        headers: Object.assign(defaultInfo.header, {
            cookie: defaultInfo.cookieData.join('')
        }),
        responseType: 'arraybuffer'
    })
}

商品の値段をもらう
async function goodPrice(stockId) {
    const callback = {}
    let name;
    let price;

    callback[name = ('jQuery' + getRandomInt(100000, 999999))] = data => {
        price = data
    }

    const result = await request({
        method: 'get',
        url: 'http://p.3.cn/prices/mgets',
        headers: Object.assign(defaultInfo.header, {
            cookie: defaultInfo.cookieData.join('')
        }),
        params: {
            type: 1,
            pduid: new Date().getTime(),
            skuIds: 'J_' + stockId,
            callback: name,
        },
    })

    eval('callback.' + result.data)

    return price
}

商品を受け取った状態
async function goodStatus(goodId, areaId) {
    const callback = {}
    let name;
    let status

    callback[name = ('jQuery' + getRandomInt(100000, 999999))] = data => {
        status = data[goodId]
    }

    const result = await request({
        method: 'get',
        url: 'http://c0.3.cn/stocks',
        headers: Object.assign(defaultInfo.header, {
            cookie: defaultInfo.cookieData.join('')
        }),
        params: {
            type: 'getstocks',
            area: areaId,
            skuIds: goodId,
            callback: name,
        },
        responseType: 'arraybuffer'
    })

    const data = iconv.decode(result.data, 'gb2312')
    eval('callback.' + data)

    return status
}

最後にオール一波で連れ去る
async function runGoodSearch() {

    let flag = true

    while (flag) {
        const all = await Promise.all([goodPrice(defaultInfo.goodId), goodStatus(defaultInfo.goodId, defaultInfo.areaId), goodInfo(defaultInfo.goodId)])

        const body = $.load(iconv.decode(all[2].data, 'gb2312'))
        outData.name = body('div.sku-name').text().trim()
        const cartLink = body('a#InitCartUrl').attr('href')
        outData.cartLink = cartLink ? 'http:' + cartLink : '     '
        outData.price = all[0][0].p
        outData.stockStatus = all[1]['StockStateName']
        outData.time = formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')

        console.log()
        console.log(`       ------------------------------`)
        console.log(`     :${outData.time}`)
        console.log(`      :${outData.name}`)
        console.log(`     :${outData.price}`)
        console.log(`     :${outData.stockStatus}`)
        console.log(`       :${outData.link}`)
        console.log(`       :${outData.cartLink}`)

        const statusCode = all[1]['StockState']
        //        
        // 33     34   
        if (+statusCode === 33) {
            flag = false
        } else {
            await sleep(defaultInfo.time)
        }
    }
}

ここでdomを解析するには、$はNode版jQueryと呼ばれるcheerioですが、直接解析すると文字化けしてしまい、先にトランスコードし、トランスコード神器がiconv-liteに登場し、残りはjQuery操作で、長い間jQueryを書いていませんが、書くとこんなにスムーズです
defaultInfoのgoodIdは商品のidですが、解析コマンドラインのパラメータが得られるのは、どこで見られるのでしょうか、図
areaIdは地域情報に対応していますが、都市ごとに在庫が異なります
京東のショッピングの流れのショッピングカートは先に行って、それから注文して支払いを始めて、品物があったら私達はショッピングカートに参加します
async function addCart() {
    console.log()
    console.log('          ')

    const result = await request({
        method: 'get',
        url: outData.cartLink,
        headers: Object.assign(defaultInfo.header, {
            cookie: defaultInfo.cookieData.join('')
        }),
    })

    const body = $.load(result.data)

    const addCartResult = body('h3.ftx-02')

    if (addCartResult) {
        console.log(`   ${addCartResult.text()}`)
    } else {
        console.log('          ')
    }
}

何も言うことがなくて、加入してから注文します.
async function buy() {
    const orderInfo = await request({
        method: 'get',
        url: 'http://trade.jd.com/shopping/order/getOrderInfo.action',
        headers: Object.assign(defaultInfo.header, {
            cookie: defaultInfo.cookieData.join('')
        }),
        params: {
            rid: new Date().getTime(),
        },
        responseType: 'arraybuffer'
    })

    const body = $.load(orderInfo.data)
    const payment = body('span#sumPayPriceId').text().trim()
    const sendAddr = body('span#sendAddr').text().trim()
    const sendMobile = body('span#sendMobile').text().trim()

    console.log()
    console.log(`       ------------------------------`)
    console.log(`        :${payment}`)
    console.log(`   ${sendAddr}`)
    console.log(`   ${sendMobile}`)
    console.log()

    console.log('       ')

    const result = await request({
        method: 'post',
        url: 'http://trade.jd.com/shopping/order/submitOrder.action',
        headers: Object.assign(defaultInfo.header, {
            cookie: defaultInfo.cookieData.join('')
        }),
        params: {
            'overseaPurchaseCookies': '',
            'submitOrderParam.btSupport': '1',
            'submitOrderParam.ignorePriceChange': '0',
            'submitOrderParam.sopNotPutInvoice': 'false',
            'submitOrderParam.trackID': defaultInfo.ticket,
            'submitOrderParam.eid': defaultInfo.eid,
            'submitOrderParam.fp': defaultInfo.fp,
        },
    })

    if (result.data.success) {
        console.log(`       ,   ${result.data.orderId}`)
        console.log('           ,        ')
    } else {
        console.log(`       ,${result.data.message}`)
    }
}

実はここはposthttp://trade.jd.com/shopping/...これでいいです.前のリクエストは注文ページで注文の情報を見せてください.ここには2つの注意点があります.
  • 商品の数量京東注文はショッピングカートという商品を全部注文します.数量にかかわらず、例えば、あなたのショッピングカートにはすでにこの商品があります.では、前の流れが終わったら、ショッピングカートには今この商品が2つあります.注文後は2つ注文しました.もちろん、ここで数量を変更することができますが、私は
  • と書いていません.
  • 注文のパラメータ上で注文の要求は3つの見知らぬパラメータsubmitOrderParam.trackID submitOrderParam.eid submitOrderParam.fpに気づくことができて、trackIDの前に手に入れたことがあって、ここで直接使えばいいのに、eidとfpはどこから来たのですか?答えはログインページですが、ここにはrequestが戻ってきたページで手に入れたdom要素がピットがありません.ブラウザでしか来られません.これもやりやすいです.Nodeにはphantomjsがありますが、ここではChrome出品のpuppeteer
  • を使いました.
    puppeteerの使用も簡単で、ノードベースのheadless Chromeツールです
    puppeteer.launch().then(async browser => {
        console.log('        ,      ')
        const page = await browser.newPage();
        await page.goto('https://passport.jd.com/new/login.aspx');
        await sleep(1000)
        console.log('         ,      ')
        const inputs = await page.evaluate(res => {
            const result = document.querySelectorAll('input')
            const data = {}
    
            for (let v of result) {
                switch (v.getAttribute('id')) {
                    case 'token':
                        data.token = v.value
                        break
                    case 'uuid':
                        data.uuid = v.value
                        break
                    case 'eid':
                        data.eid = v.value
                        break
                    case 'sessionId':
                        data.fp = v.value
                        break
                }
            }
    
            return data
        })
    
        Object.assign(defaultInfo, inputs)
        await browser.close();
        console.log('         ,     ')
    
        console.log()
        console.log('   -------------------------------------   ')
        console.log('                    ')
        console.log('   -------------------------------------   ')
        console.log()
    
    })

    puppeteerはまずlaunchを必要とし、その後browserのインスタンスを生成し、browserを使用して新しいページを作成して私たちのウェブサイトを実行し、提供されたevaluateメソッドでDOMを操作することができます.上のコードも簡単で、一目瞭然です.
    これで基本的に自動発注機能が完了し、コマンドラインパラメータを拡張します
    const args = require('yargs').alias('h', 'help')
        .option('a', {
            alias: 'area',
            demand: true,
            describe: '    ',
        })
        .option('g', {
            alias: 'good',
            demand: true,
            describe: '    ',
        })
        .option('t', {
            alias: 'time',
            describe: '    ms',
            default: '10000'
        })
        .option('b', {
            alias: 'buy',
            describe: '    ',
            default: true
        })
        .usage('Usage: node index.js -a      -g     ')
        .example('node index.js -a 2_2830_51810_0 -g 5008395')
        .argv;

    ここで私は2つの必須パラメータと2つのオプションパラメータをあげました.-a必須、地域番号、-g必須、商品番号、-t商品照会の間隔時間、デフォルトは10 s、-b自動購入かどうか、デフォルトは購入です.ここはbooleanです.yargsは使いやすいですが、TJ大神のcommanderも使えます.同じです.
    完全なコードは次のプロジェクトアドレスに表示できます.
    プロジェクトのアドレスはstarを求めます