ダブル11に乗じて、京東商品を書いて自動注文します
14671 ワード
プロジェクトのアドレスは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を手に入れます
headerは私达を手に入れてブラウザに伪装してQRコードのピクチャーを要求することができて、京东のスキャンのピクチャーの住所の
パラメータ
このステップのcookieはすでに手に入れて、ここで私は2つのステップの処理をして、1歩は自分で書いた
スキャンする前にスキャンのステータスを監視します
最初はタイマーをつけてポーリングしたいと思っていました.「いいか悪いか」は、私が1秒もしないうちに聞いてみました.ここではasync/awaitの強力な機能を使ってsleepを実現し、settimoutの使用よりも優雅で非同期の処理も自在に操作できます.
ここではheaderを組み合わせて、さっき手に入れたクッキーを持ってきて、
この时、あなたの携帯で京东を开けて开いたQRコードの画像をスキャンして、确认してスキャンに成功して、入场券で登录します
この一歩は何も言わないで、入場券があって、当然登録に成功したはずで、p 3 pパラメータを手に入れてクッキーを更新するという合法的な身分が誕生しました.
アイデンティティがあればget商品ページに行けますが、このステップでは3つのリクエストの情報をつづる必要があります.
商品ページのhtmlを入手
商品の値段をもらう
商品を受け取った状態
最後にオール一波で連れ去る
ここでdomを解析するには、
defaultInfoのgoodIdは商品のidですが、解析コマンドラインのパラメータが得られるのは、どこで見られるのでしょうか、図
areaIdは地域情報に対応していますが、都市ごとに在庫が異なります
京東のショッピングの流れのショッピングカートは先に行って、それから注文して支払いを始めて、品物があったら私達はショッピングカートに参加します
何も言うことがなくて、加入してから注文します.
実はここはposthttp://trade.jd.com/shopping/...これでいいです.前のリクエストは注文ページで注文の情報を見せてください.ここには2つの注意点があります.商品の数量京東注文はショッピングカートという商品を全部注文します.数量にかかわらず、例えば、あなたのショッピングカートにはすでにこの商品があります.では、前の流れが終わったら、ショッピングカートには今この商品が2つあります.注文後は2つ注文しました.もちろん、ここで数量を変更することができますが、私は と書いていません.注文のパラメータ上で注文の要求は3つの見知らぬパラメータ を使いました.
puppeteerの使用も簡単で、ノードベースのheadless Chromeツールです
puppeteerはまずlaunchを必要とし、その後browserのインスタンスを生成し、browserを使用して新しいページを作成して私たちのウェブサイトを実行し、提供されたevaluateメソッドでDOMを操作することができます.上のコードも簡単で、一目瞭然です.
これで基本的に自動発注機能が完了し、コマンドラインパラメータを拡張します
ここで私は2つの必須パラメータと2つのオプションパラメータをあげました.
完全なコードは次のプロジェクトアドレスに表示できます.
プロジェクトのアドレスは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
size
とt
はバッグをつかむことで手に入れることができます.ここで私の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を組み合わせて、さっき手に入れたクッキーを持ってきて、
host
とreferer
を加えて、私たちがどこからどこへ行くかを示します.パラメータの中の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つの注意点があります.
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を求めます