提交 567e88a9 编写于 作者: C cycz

goodbye

上级 45a9e85c
# -*- coding=utf-8 -*-
from jdlogger import logger
'''
测试 https://c0.3.cn/stock?skuId=100003406321&area=19_1607_4773_0&venderId=1000000946&buyNum=1&choseSuitSkuIds=&cat=9192,9197,12588&extraParam={%22originid%22:%221%22}&fqsp=0&pdpin=jd_7c3992aa27d1a&pduid=1580535906442142991701&ch=1&callback=jQuery4291064
请看教程寻找自己的url
'''
url = ''
skuId = url.split('skuId=')[1].split('&')[0]
area = url.split('area=')[1].split('&')[0]
logger.info('你的area是[ %s ],链接的商品id是[ %s ]', area, skuId)
****
发现有人滥用,代码暂不更新了。
因官方善意通知,本仓库和大家88了
exe版本仅能购买一件,定时过期。
仅记录一段疫情下口罩的匮乏,程序员使用技术相互帮助
祝大家早点买到**自己所需**的口罩
****
使用方法 : https://blog.csdn.net/cyz52/article/details/104239558
## exe版本[口罩小助手] 最多添加99种口罩
链接:https://pan.baidu.com/s/1bgGXsH071GkHWGlLIwgRVw
提取码:do3f
看到有人在闲鱼转卖口罩,打码的接口已经改了
有需要的说明原因,加群要吧(1053467385)
后续有积极的爬虫工具需求也可以和作者私信询问
江湖再见
****
祝大家早点买到口罩
......
# -*- coding: utf-8 -*-
import os
import configparser
class Config(object):
def __init__(self, config_file='configDemo.ini'):
self._path = os.path.join(os.getcwd(), config_file)
if not os.path.exists(self._path):
raise FileNotFoundError("No such file: config.ini")
self._config = configparser.ConfigParser()
self._config.read(self._path, encoding='utf-8-sig')
self._configRaw = configparser.RawConfigParser()
self._configRaw.read(self._path, encoding='utf-8-sig')
def get(self, section, name):
return self._config.get(section, name)
def getRaw(self, section, name):
return self._configRaw.get(section, name)
[config]
# 地区id (不知道怎么获取的使用一下AreaTool.py工具)
area = 19_1607_4773_0
# cookie
cookies_String = shshshfpa=21a5db2
# 推送方式 1为邮箱,2为微信
messageType =1
# mail
mail = xxxxxx@qq.com
# sc_key 方糖微信推送的key 不知道的请看http://sc.ftqq.com/3.version
sc_key = test
# 支付密码
# 如果你的账户中有可用的京券(注意不是东券)或 在上次购买订单中使用了京豆,
# 那么京东可能会在下单时自动选择京券支付 或 自动勾选京豆支付。
# 此时下单会要求输入六位数字的支付密码。请在下方配置你的支付密码,如 123456 。
# 如果没有上述情况,下方请留空。
payment_pwd =
#暂时不填写
[V2]
# skuids 英文逗号[,]相隔 (分清楚英文和中文逗号) 末尾不要带逗号
skuids = 3088512,65437208345,7498169,7498165,7263128,7498167,17449572304,37934196731,100001086804,56657322838,56657322841,100005294853,1938795,15595191653,15595191654,45923412989
# V2版本下单速度区分 极速模式 【1】 和 正常模式 【2】(极速模式缩短不必要的流程)
model = 1
[V3]
# v3版本的skuid,最多写一件
skuid = 3088512
[Temporary]
#一般不需要修改
eid =
fp =
# 打码服务器
captchaUrl = http://122.51.18.81:8111/pic
\ No newline at end of file
此差异已折叠。
# -*- coding=utf-8 -*-
'''
2020/2/13
(避免滥用,代码已经废弃,现已不更新,有需要请适量使用exe版本)
京东抢购口罩程序
通过商品的skuid、地区id抢购
'''
import sys
import requests
from bs4 import BeautifulSoup
from config import Config
from jdProgram import *
from message import message
from util import getconfigMd5, _setDNSCache
global cookies_String, mail, sc_key, messageType, modelType, area, skuidsString, skuids, captchaUrl, eid, fp, payment_pwd
def getconfig():
global cookies_String, mail, sc_key, messageType, modelType, area, skuidsString, skuids, captchaUrl, eid, fp, payment_pwd
global_config = Config()
# cookie 网页获取
cookies_String = global_config.getRaw('config', 'cookies_String')
# 有货通知 收件邮箱
mail = global_config.getRaw('config', 'mail')
# 方糖微信推送的key 不知道的请看http://sc.ftqq.com/3.version
sc_key = global_config.getRaw('config', 'sc_key')
# 推送方式 1(mail)或 2(wechat)
messageType = global_config.getRaw('config', 'messageType')
# 下单模式
modelType = global_config.getRaw('V2', 'model')
# 地区id
area = global_config.getRaw('config', 'area')
# 商品id
skuidsString = global_config.getRaw('V2', 'skuids')
skuids = str(skuidsString).split(',')
# 验证码服务地址
captchaUrl = global_config.getRaw('Temporary', 'captchaUrl')
if not modelType:
logger.error('请在configDemo.ini文件填写下单model')
if len(skuids[0]) == 0:
logger.error('请在configDemo.ini文件中输入你的商品id')
sys.exit(1)
'''
备用
'''
# eid
eid = global_config.getRaw('Temporary', 'eid')
fp = global_config.getRaw('Temporary', 'fp')
# 支付密码
payment_pwd = global_config.getRaw('config', 'payment_pwd')
# 初次
configTime = int(time.time())
getconfig()
configMd5 = getconfigMd5()
message = message(messageType=messageType, sc_key=sc_key, mail=mail)
is_Submit_captcha = False
submit_captcha_rid = ''
submit_captcha_text = ''
encryptClientInfo = ''
submit_Time = 0
session = requests.session()
session.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Connection": "keep-alive"
}
checksession = requests.session()
checksession.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Connection": "keep-alive"
}
manual_cookies = {}
def get_tag_value(tag, key='', index=0):
if key:
value = tag[index].get(key)
else:
value = tag[index].text
return value.strip(' \t\r\n')
for item in cookies_String.split(';'):
name, value = item.strip().split('=', 1)
# 用=号分割,分割1次
manual_cookies[name] = value
# 为字典cookies添加内容
cookiesJar = requests.utils.cookiejar_from_dict(manual_cookies, cookiejar=None, overwrite=True)
session.cookies = cookiesJar
def validate_cookies():
for flag in range(1, 3):
try:
targetURL = 'https://order.jd.com/center/list.action'
payload = {
'rid': str(int(time.time() * 1000)),
}
resp = session.get(url=targetURL, params=payload, allow_redirects=False)
if resp.status_code == requests.codes.OK:
logger.info('校验是否登录[成功]')
return True
else:
logger.info('校验是否登录[失败]')
logger.info('请在configDemo.ini文件下更新cookie')
time.sleep(5)
continue
except Exception as e:
logger.info('第【%s】次请重新获取cookie', flag)
time.sleep(5)
continue
message.sendAny('脚本登录cookie失效了,请重新登录')
sys.exit(1)
def getUsername():
userName_Url = 'https://passport.jd.com/new/helloService.ashx?callback=jQuery339448&_=' + str(
int(time.time() * 1000))
session.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://order.jd.com/center/list.action",
"Connection": "keep-alive"
}
resp = session.get(url=userName_Url, allow_redirects=True)
resultText = resp.text
resultText = resultText.replace('jQuery339448(', '')
resultText = resultText.replace(')', '')
usernameJson = json.loads(resultText)
logger.info('登录账号名称[%s]', usernameJson['nick'])
'''
检查是否有货
'''
def check_item_stock(itemUrl):
response = session.get(itemUrl)
if (response.text.find('无货') > 0):
return True
else:
return False
'''
取消勾选购物车中的所有商品
'''
def cancel_select_all_cart_item():
url = "https://cart.jd.com/cancelAllItem.action"
data = {
't': 0,
'outSkus': '',
'random': random.random()
}
resp = session.post(url, data=data)
if resp.status_code != requests.codes.OK:
print('Status: %u, Url: %s' % (resp.status_code, resp.url))
return False
return True
'''
购物车详情
'''
def cart_detail():
url = 'https://cart.jd.com/cart.action'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://order.jd.com/center/list.action",
"Host": "cart.jd.com",
"Connection": "keep-alive"
}
resp = session.get(url, headers=headers)
soup = BeautifulSoup(resp.text, "html.parser")
cart_detail = dict()
for item in soup.find_all(class_='item-item'):
try:
sku_id = item['skuid'] # 商品id
except Exception as e:
logger.info('购物车中有套装商品,跳过')
continue
try:
# 例如:['increment', '8888', '100001071956', '1', '13', '0', '50067652554']
# ['increment', '8888', '100002404322', '2', '1', '0']
item_attr_list = item.find(class_='increment')['id'].split('_')
p_type = item_attr_list[4]
promo_id = target_id = item_attr_list[-1] if len(item_attr_list) == 7 else 0
cart_detail[sku_id] = {
'name': get_tag_value(item.select('div.p-name a')), # 商品名称
'verder_id': item['venderid'], # 商家id
'count': int(item['num']), # 数量
'unit_price': get_tag_value(item.select('div.p-price strong'))[1:], # 单价
'total_price': get_tag_value(item.select('div.p-sum strong'))[1:], # 总价
'is_selected': 'item-selected' in item['class'], # 商品是否被勾选
'p_type': p_type,
'target_id': target_id,
'promo_id': promo_id
}
except Exception as e:
logger.error("商品%s在购物车中的信息无法解析,报错信息: %s,该商品自动忽略", sku_id, e)
logger.info('购物车信息:%s', cart_detail)
return cart_detail
'''
修改购物车商品的数量
'''
def change_item_num_in_cart(sku_id, vender_id, num, p_type, target_id, promo_id):
url = "https://cart.jd.com/changeNum.action"
data = {
't': 0,
'venderId': vender_id,
'pid': sku_id,
'pcount': num,
'ptype': p_type,
'targetId': target_id,
'promoID': promo_id,
'outSkus': '',
'random': random.random(),
# 'locationId'
}
session.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://cart.jd.com/cart",
"Connection": "keep-alive"
}
resp = session.post(url, data=data)
return json.loads(resp.text)['sortedWebCartResult']['achieveSevenState'] == 2
'''
添加商品到购物车
'''
def add_item_to_cart(sku_id):
url = 'https://cart.jd.com/gate.action'
payload = {
'pid': sku_id,
'pcount': 1,
'ptype': 1,
}
resp = session.get(url=url, params=payload)
if 'https://cart.jd.com/cart.action' in resp.url: # 套装商品加入购物车后直接跳转到购物车页面
result = True
else: # 普通商品成功加入购物车后会跳转到提示 "商品已成功加入购物车!" 页面
soup = BeautifulSoup(resp.text, "html.parser")
result = bool(soup.select('h3.ftx-02')) # [<h3 class="ftx-02">商品已成功加入购物车!</h3>]
if result:
logger.info('%s 已成功加入购物车', sku_id)
else:
logger.error('%s 添加到购物车失败', sku_id)
def get_checkout_page_detail():
"""获取订单结算页面信息
该方法会返回订单结算页面的详细信息:商品名称、价格、数量、库存状态等。
:return: 结算信息 dict
"""
url = 'http://trade.jd.com/shopping/order/getOrderInfo.action'
# url = 'https://cart.jd.com/gotoOrder.action'
payload = {
'rid': str(int(time.time() * 1000)),
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://cart.jd.com/cart.action",
"Connection": "keep-alive",
'Host': 'trade.jd.com',
}
try:
resp = session.get(url=url, params=payload, headers=headers)
if not response_status(resp):
logger.error('获取订单结算页信息失败')
return ''
if '刷新太频繁了' in resp.text:
return '刷新太频繁了'
soup = BeautifulSoup(resp.text, "html.parser")
risk_control = get_tag_value(soup.select('input#riskControl'), 'value')
showCheckCode = get_tag_value(soup.select('input#showCheckCode'), 'value')
if not showCheckCode:
pass
else:
if showCheckCode == 'true':
logger.info('提交订单需要验证码')
global is_Submit_captcha, encryptClientInfo
encryptClientInfo = get_tag_value(soup.select('input#encryptClientInfo'), 'value')
is_Submit_captcha = True
order_detail = {
'address': soup.find('span', id='sendAddr').text[5:], # remove '寄送至: ' from the begin
'receiver': soup.find('span', id='sendMobile').text[4:], # remove '收件人:' from the begin
'total_price': soup.find('span', id='sumPayPriceId').text[1:], # remove '¥' from the begin
'items': []
}
logger.info("下单信息:%s", order_detail)
return risk_control
except requests.exceptions.RequestException as e:
logger.error('订单结算页面获取异常:%s' % e)
except Exception as e:
logger.error('下单页面数据解析异常:%s', e)
return ''
'''
下柜商品检测
'''
def item_removed(sku_id):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "http://trade.jd.com/shopping/order/getOrderInfo.action",
"Connection": "keep-alive",
'Host': 'item.jd.com',
}
url = 'https://item.jd.com/{}.html'.format(sku_id)
page = requests.get(url=url, headers=headers)
return '该商品已下柜' not in page.text
'''
购买环节
测试三次
'''
def normalModeBuyMask(sku_id):
cancel_select_all_cart_item()
cart = cart_detail()
if sku_id in cart:
logger.info('%s 已在购物车中,调整数量为 %s', sku_id, 1)
cart_item = cart.get(sku_id)
change_item_num_in_cart(
sku_id=sku_id,
vender_id=cart_item.get('vender_id'),
num=1,
p_type=cart_item.get('p_type'),
target_id=cart_item.get('target_id'),
promo_id=cart_item.get('promo_id')
)
else:
add_item_to_cart(sku_id)
risk_control = get_checkout_page_detail()
if risk_control == '刷新太频繁了':
return False
if len(risk_control) > 0:
if submit_order(session, risk_control, sku_id, skuids, submit_Time, encryptClientInfo, is_Submit_captcha,
payment_pwd, submit_captcha_text, submit_captcha_rid):
return True
return False
def fastModeBuyMask(sku_id):
add_item_to_cart(sku_id)
risk_control = get_checkout_page_detail()
if risk_control == '刷新太频繁了':
return False
if len(risk_control) > 0:
if submit_order(session, risk_control, sku_id, skuids, submit_Time, encryptClientInfo, is_Submit_captcha,
payment_pwd, submit_captcha_text, submit_captcha_rid):
return True
return False
'''
删除购物车选中商品
'''
def remove_item():
url = "https://cart.jd.com/batchRemoveSkusFromCart.action"
data = {
't': 0,
'null': '',
'outSkus': '',
'random': random.random(),
'locationId': '19-1607-4773-0'
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.37",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Referer": "https://cart.jd.com/cart.action",
"Host": "cart.jd.com",
"Content-Type": "application/x-www-form-urlencoded",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Encoding": "zh-CN,zh;q=0.9,ja;q=0.8",
"Origin": "https://cart.jd.com",
"Connection": "keep-alive"
}
resp = session.post(url, data=data, headers=headers)
logger.info('清空购物车')
if resp.status_code != requests.codes.OK:
print('Status: %u, Url: %s' % (resp.status_code, resp.url))
return False
return True
def select_all_cart_item():
url = "https://cart.jd.com/selectAllItem.action"
data = {
't': 0,
'outSkus': '',
'random': random.random()
}
resp = session.post(url, data=data)
if resp.status_code != requests.codes.OK:
print('Status: %u, Url: %s' % (resp.status_code, resp.url))
return False
return True
def normalModeAutoBuy(inStockSkuid):
for skuId in inStockSkuid:
if item_removed(skuId):
global submit_Time
submit_Time = int(time.time() * 1000)
logger.info('[%s]类型口罩有货啦!马上下单', skuId)
skuidUrl = 'https://item.jd.com/' + skuId + '.html'
if normalModeBuyMask(skuId):
message.send(skuidUrl, True)
sys.exit(1)
else:
message.send(skuidUrl, False)
else:
logger.info('[%s]类型口罩有货,但已下柜商品', skuId)
def fastModeAutoBuy(inStockSkuid):
for skuId in inStockSkuid:
global submit_Time
submit_Time = int(time.time() * 1000)
logger.info('[%s]类型口罩有货啦!马上下单', skuId)
skuidUrl = 'https://item.jd.com/' + skuId + '.html'
if fastModeBuyMask(skuId):
message.send(skuidUrl, True)
sys.exit(1)
else:
if item_removed(skuId):
message.send(skuidUrl, False)
else:
logger.info('[%s]商品已下柜,商品列表中踢出', skuId)
skuids.remove(skuId)
select_all_cart_item()
remove_item()
def check_Config():
global configMd5, configTime
nowMd5 = getconfigMd5()
configTime = time.time()
if not nowMd5 == configMd5:
logger.info('配置文件修改,重新读取文件')
getconfig()
configMd5 = nowMd5
def normalMode():
flag = 1
while (1):
try:
if flag == 1:
validate_cookies()
getUsername()
# 检测配置文件是否修改
if int(time.time()) - configTime >= 60:
check_Config()
# modelType
logger.info('第' + str(flag) + '次 ')
flag += 1
# 检测库存
inStockSkuid = check_stock(checksession, skuids, area)
# 下单任务
normalModeAutoBuy(inStockSkuid)
# 休眠模块
timesleep = random.randint(1, 3) / 10
time.sleep(timesleep)
# 校验是否还在登录模块
if flag % 100 == 0:
logger.info('校验是否还在登录')
validate_cookies()
except Exception as e:
print(traceback.format_exc())
time.sleep(10)
def fastMode():
flag = 1
while (1):
try:
if flag == 1:
validate_cookies()
getUsername()
select_all_cart_item()
remove_item()
# 检测配置文件修改
if int(time.time()) - configTime >= 600:
check_Config()
# modelType
logger.info('第' + str(flag) + '次 ')
flag += 1
# 检测库存
inStockSkuid = check_stock(checksession, skuids, area)
# 下单任务
fastModeAutoBuy(inStockSkuid)
# 休眠模块
timesleep = random.randint(1, 3) / 10
time.sleep(timesleep)
# 校验是否还在登录模块
if flag % 100 == 0:
logger.info('校验是否还在登录')
validate_cookies()
except Exception as e:
print(traceback.format_exc())
time.sleep(10)
# _setDNSCache()
if modelType == '2':
logger.info('V2版本当前模式[普通模式]')
normalMode()
elif modelType == '1':
logger.info('V2版本当前模式[极速模式]')
fastMode()
# -*- coding=utf-8 -*-
'''
2020/2/13
(避免滥用,代码已经废弃,现已不更新,有需要请适量使用exe版本)
京东抢购口罩程序V3版本
'''
import sys
import traceback
from bs4 import BeautifulSoup
from config import Config
from jdProgram import *
from jdlogger import logger
from message import message
from util import *
from util import _setDNSCache
'''
需要修改
'''
global cookies_String, mail, sc_key, messageType, area, skuidsString, skuids, captchaUrl, eid, fp, payment_pwd
def getconfig():
global_config = Config()
global cookies_String, mail, sc_key, messageType, area, skuidsString, skuids, captchaUrl, eid, fp, payment_pwd
# cookie 网页获取
cookies_String = global_config.getRaw('config', 'cookies_String')
# 有货通知 收件邮箱
mail = global_config.getRaw('config', 'mail')
# 方糖微信推送的key 不知道的请看http://sc.ftqq.com/3.version
sc_key = global_config.getRaw('config', 'sc_key')
# 推送方式 1(mail)或 2(wechat)
messageType = global_config.getRaw('config', 'messageType')
# 地区id
area = global_config.getRaw('config', 'area')
# 商品id
skuidsString = global_config.getRaw('V3', 'skuid')
skuids = str(skuidsString).split(',')
# 验证码服务地址
captchaUrl = global_config.getRaw('Temporary', 'captchaUrl')
if len(skuids[0]) == 0:
logger.error('请在configDemo.ini文件中输入你的商品id')
sys.exit(1)
'''
备用
'''
# eid
eid = global_config.getRaw('Temporary', 'eid')
fp = global_config.getRaw('Temporary', 'fp')
# 支付密码
payment_pwd = global_config.getRaw('config', 'payment_pwd')
# 初次
configTime = int(time.time())
getconfig()
configMd5 = getconfigMd5()
message = message(messageType=messageType, sc_key=sc_key, mail=mail)
is_Submit_captcha = False
submit_captcha_rid = ''
submit_captcha_text = ''
encryptClientInfo = ''
submit_Time = 0
session = requests.session()
checksession = requests.session()
session.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Connection": "keep-alive"
}
checksession.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Connection": "keep-alive"
}
manual_cookies = {}
def get_tag_value(tag, key='', index=0):
if key:
value = tag[index].get(key)
else:
value = tag[index].text
return value.strip(' \t\r\n')
def response_status(resp):
if resp.status_code != requests.codes.OK:
print('Status: %u, Url: %s' % (resp.status_code, resp.url))
return False
return True
for item in cookies_String.split(';'):
name, value = item.strip().split('=', 1)
# 用=号分割,分割1次
manual_cookies[name] = value
# 为字典cookies添加内容
cookiesJar = requests.utils.cookiejar_from_dict(manual_cookies, cookiejar=None, overwrite=True)
session.cookies = cookiesJar
def validate_cookies():
for flag in range(1, 3):
try:
targetURL = 'https://order.jd.com/center/list.action'
payload = {
'rid': str(int(time.time() * 1000)),
}
resp = session.get(url=targetURL, params=payload, allow_redirects=False)
if resp.status_code == requests.codes.OK:
logger.info('登录成功')
return True
else:
logger.info('第【%s】次请重新获取cookie', flag)
time.sleep(5)
continue
except Exception as e:
logger.info('第【%s】次请重新获取cookie', flag)
time.sleep(5)
continue
message.sendAny('脚本登录cookie失效了,请重新登录')
sys.exit(1)
def getUsername():
userName_Url = 'https://passport.jd.com/new/helloService.ashx?callback=jQuery339448&_=' + str(
int(time.time() * 1000))
session.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://order.jd.com/center/list.action",
"Connection": "keep-alive"
}
resp = session.get(url=userName_Url, allow_redirects=True)
resultText = resp.text
resultText = resultText.replace('jQuery339448(', '')
resultText = resultText.replace(')', '')
usernameJson = json.loads(resultText)
logger.info('登录账号名称' + usernameJson['nick'])
'''
检查是否有货
'''
def check_item_stock(itemUrl):
response = session.get(itemUrl)
if (response.text.find('无货') > 0):
return True
else:
return False
'''
取消勾选购物车中的所有商品
'''
def cancel_select_all_cart_item():
url = "https://cart.jd.com/cancelAllItem.action"
data = {
't': 0,
'outSkus': '',
'random': random.random()
}
resp = session.post(url, data=data)
if resp.status_code != requests.codes.OK:
print('Status: %u, Url: %s' % (resp.status_code, resp.url))
return False
return True
'''
勾选购物车中的所有商品
'''
def select_all_cart_item():
url = "https://cart.jd.com/selectAllItem.action"
data = {
't': 0,
'outSkus': '',
'random': random.random()
}
resp = session.post(url, data=data)
if resp.status_code != requests.codes.OK:
print('Status: %u, Url: %s' % (resp.status_code, resp.url))
return False
return True
'''
删除购物车选中商品
'''
def remove_item():
url = "https://cart.jd.com/batchRemoveSkusFromCart.action"
data = {
't': 0,
'null': '',
'outSkus': '',
'random': random.random(),
'locationId': '19-1607-4773-0'
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.37",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Referer": "https://cart.jd.com/cart.action",
"Host": "cart.jd.com",
"Content-Type": "application/x-www-form-urlencoded",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Encoding": "zh-CN,zh;q=0.9,ja;q=0.8",
"Origin": "https://cart.jd.com",
"Connection": "keep-alive"
}
resp = session.post(url, data=data, headers=headers)
logger.info('清空购物车')
if resp.status_code != requests.codes.OK:
print('Status: %u, Url: %s' % (resp.status_code, resp.url))
return False
return True
'''
购物车详情
'''
def cart_detail():
url = 'https://cart.jd.com/cart.action'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://order.jd.com/center/list.action",
"Host": "cart.jd.com",
"Connection": "keep-alive"
}
resp = session.get(url, headers=headers)
soup = BeautifulSoup(resp.text, "html.parser")
cart_detail = dict()
for item in soup.find_all(class_='item-item'):
try:
sku_id = item['skuid'] # 商品id
except Exception as e:
logger.info('购物车中有套装商品,跳过')
continue
try:
# 例如:['increment', '8888', '100001071956', '1', '13', '0', '50067652554']
# ['increment', '8888', '100002404322', '2', '1', '0']
item_attr_list = item.find(class_='increment')['id'].split('_')
p_type = item_attr_list[4]
promo_id = target_id = item_attr_list[-1] if len(item_attr_list) == 7 else 0
cart_detail[sku_id] = {
'name': get_tag_value(item.select('div.p-name a')), # 商品名称
'verder_id': item['venderid'], # 商家id
'count': int(item['num']), # 数量
'unit_price': get_tag_value(item.select('div.p-price strong'))[1:], # 单价
'total_price': get_tag_value(item.select('div.p-sum strong'))[1:], # 总价
'is_selected': 'item-selected' in item['class'], # 商品是否被勾选
'p_type': p_type,
'target_id': target_id,
'promo_id': promo_id
}
except Exception as e:
logger.error("商品%s在购物车中的信息无法解析,报错信息: %s,该商品自动忽略", sku_id, e)
logger.info('购物车信息:%s', cart_detail)
return cart_detail
'''
修改购物车商品的数量
'''
def change_item_num_in_cart(sku_id, vender_id, num, p_type, target_id, promo_id):
url = "https://cart.jd.com/changeNum.action"
data = {
't': 0,
'venderId': vender_id,
'pid': sku_id,
'pcount': num,
'ptype': p_type,
'targetId': target_id,
'promoID': promo_id,
'outSkus': '',
'random': random.random(),
# 'locationId'
}
session.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://cart.jd.com/cart",
"Connection": "keep-alive"
}
resp = session.post(url, data=data)
return json.loads(resp.text)['sortedWebCartResult']['achieveSevenState'] == 2
'''
添加商品到购物车
'''
def add_item_to_cart(sku_id):
url = 'https://cart.jd.com/gate.action'
payload = {
'pid': sku_id,
'pcount': 1,
'ptype': 1,
}
resp = session.get(url=url, params=payload)
if 'https://cart.jd.com/cart.action' in resp.url: # 套装商品加入购物车后直接跳转到购物车页面
result = True
else: # 普通商品成功加入购物车后会跳转到提示 "商品已成功加入购物车!" 页面
soup = BeautifulSoup(resp.text, "html.parser")
result = bool(soup.select('h3.ftx-02')) # [<h3 class="ftx-02">商品已成功加入购物车!</h3>]
if result:
logger.info('%s 已成功加入购物车', sku_id)
else:
logger.error('%s 添加到购物车失败', sku_id)
def get_checkout_page_detail():
"""获取订单结算页面信息
该方法会返回订单结算页面的详细信息:商品名称、价格、数量、库存状态等。
:return: 结算信息 dict
"""
url = 'http://trade.jd.com/shopping/order/getOrderInfo.action'
# url = 'https://cart.jd.com/gotoOrder.action'
payload = {
'rid': str(int(time.time() * 1000)),
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://cart.jd.com/cart.action",
"Connection": "keep-alive",
'Host': 'trade.jd.com',
}
try:
resp = session.get(url=url, params=payload, headers=headers)
if not response_status(resp):
logger.error('获取订单结算页信息失败')
return ''
if '刷新太频繁了' in resp.text:
return '刷新太频繁了'
soup = BeautifulSoup(resp.text, "html.parser")
showCheckCode = get_tag_value(soup.select('input#showCheckCode'), 'value')
if not showCheckCode:
pass
else:
if showCheckCode == 'true':
logger.info('提交订单需要验证码')
global is_Submit_captcha, encryptClientInfo
encryptClientInfo = get_tag_value(soup.select('input#encryptClientInfo'), 'value')
is_Submit_captcha = True
risk_control = get_tag_value(soup.select('input#riskControl'), 'value')
order_detail = {
'address': soup.find('span', id='sendAddr').text[5:], # remove '寄送至: ' from the begin
'receiver': soup.find('span', id='sendMobile').text[4:], # remove '收件人:' from the begin
'total_price': soup.find('span', id='sumPayPriceId').text[1:], # remove '¥' from the begin
'items': []
}
logger.info("下单信息:%s", order_detail)
return risk_control
except requests.exceptions.RequestException as e:
logger.error('订单结算页面获取异常:%s' % e)
except Exception as e:
logger.error('下单页面数据解析异常:%s', e)
return ''
'''
商品下柜检测
'''
def item_removed(sku_id):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "http://trade.jd.com/shopping/order/getOrderInfo.action",
"Connection": "keep-alive",
'Host': 'item.jd.com',
}
url = 'https://item.jd.com/{}.html'.format(sku_id)
page = requests.get(url=url, headers=headers)
return '该商品已下柜' not in page.text
'''
购买环节
测试三次
'''
def buyMask(sku_id):
risk_control = get_checkout_page_detail()
if risk_control == '刷新太频繁了':
return False
if len(risk_control) > 0:
if submit_order(session, risk_control, sku_id, skuids, submit_Time, encryptClientInfo, is_Submit_captcha,
payment_pwd, submit_captcha_text, submit_captcha_rid):
return True
def V3check(skuId):
select_all_cart_item()
remove_item()
validate_cookies()
logger.info('校验是否还在登录')
add_item_to_cart(skuId)
if not item_removed(skuId):
logger.info('[%s]已下柜商品', skuId)
sys.exit(1)
def V3AutoBuy(inStockSkuid):
if skuId in inStockSkuid:
global submit_Time
submit_Time = int(time.time() * 1000)
logger.info('[%s]类型口罩有货啦!马上下单', skuId)
skuidUrl = 'https://item.jd.com/' + skuId + '.html'
if buyMask(skuId):
message.send(skuidUrl, True)
sys.exit(1)
else:
if item_removed(skuId):
message.send(skuidUrl, False)
else:
logger.info('[%s]已下柜商品', skuId)
sys.exit(1)
def check_Config():
global configMd5, configTime
nowMd5 = getconfigMd5()
configTime = time.time()
if not nowMd5 == configMd5:
logger.info('配置文件修改,重新读取文件')
getconfig()
configMd5 = nowMd5
# _setDNSCache()
if len(skuids) != 1:
logger.info('请准备一件商品')
skuId = skuids[0]
flag = 1
while (1):
try:
# 初始化校验
if flag == 1:
logger.info('当前是V3版本')
validate_cookies()
getUsername()
select_all_cart_item()
remove_item()
add_item_to_cart(skuId)
# 检测配置文件修改
if int(time.time()) - configTime >= 60:
check_Config()
logger.info('第' + str(flag) + '次 ')
flag += 1
# 检查库存模块
inStockSkuid = check_stock(checksession, skuids, area)
# 自动下单模块
V3AutoBuy(inStockSkuid)
# 休眠模块
timesleep = random.randint(1, 3) / 10
time.sleep(timesleep)
# 校验是否还在登录模块
if flag % 100 == 0:
V3check(skuId)
except Exception as e:
print(traceback.format_exc())
time.sleep(10)
# -*- coding=utf-8 -*-
'''
发送邮件模块
'''
import traceback
def sendMail(mail, msgtext):
try:
import smtplib
from email.mime.text import MIMEText
# email 用于构建邮件内容
from email.header import Header
# 用于构建邮件头
# 发信方的信息:发信邮箱,QQ 邮箱授权码
from_addr = 'jdbuymask@163.com'
password = 'alpsneahcyz123'
# 收信方邮箱
to_addr = mail
# 发信服务器
smtp_server = 'smtp.163.com'
# 邮箱正文内容,第一个参数为内容,第二个参数为格式(plain 为纯文本),第三个参数为编码
msg = MIMEText(msgtext, 'plain', 'utf-8')
# 邮件头信息
# msg['From'] = Header(from_addr)
msg['From'] = Header(u'from Mark<{}>'.format(from_addr), 'utf-8')
msg['To'] = Header(to_addr)
msg['Subject'] = Header('京东口罩监控','utf-8')
# 开启发信服务,这里使用的是加密传输
server = smtplib.SMTP_SSL(host=smtp_server)
server.connect(smtp_server, 465)
# 登录发信邮箱
server.login(from_addr, password)
# 发送邮件
server.sendmail(from_addr, to_addr, msg.as_string())
# 关闭服务器
server.quit()
except Exception as e:
print(traceback.format_exc())
# -*- coding=utf-8 -*-
import json
import random
import time
from io import BytesIO
from PIL import Image
from jdlogger import logger
from util import parse_json, response_status
import traceback
'''
2020/2/13
(避免滥用,代码已经废弃,现已不更新,有需要请适量使用exe版本)
'''
'''
查询库存(旧)
'''
def check_stock(checksession, skuids, area):
start = int(time.time() * 1000)
skuidString = ','.join(skuids)
callback = 'jQuery' + str(random.randint(1000000, 9999999))
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://cart.jd.com/cart.action",
"Connection": "keep-alive",
"Host": "c0.3.cn"
}
#
url = 'https://c0.3.cn/stocks'
payload = {
'type': 'getstocks',
'skuIds': skuidString,
'area': area,
'callback': callback,
'_': int(time.time() * 1000),
}
resp = checksession.get(url=url, params=payload, headers=headers)
inStockSkuid = []
nohasSkuid = []
unUseSkuid = []
for sku_id, info in parse_json(resp.text).items():
sku_state = info.get('skuState') # 商品是否上架
stock_state = info.get('StockState') # 商品库存状态
if sku_state == 1 and stock_state in (33, 40):
inStockSkuid.append(sku_id)
if sku_state == 0:
unUseSkuid.append(sku_id)
if stock_state == 34:
nohasSkuid.append(sku_id)
logger.info('检测[%s]个口罩有货,[%s]个口罩无货,[%s]个口罩下柜,耗时[%s]ms', len(inStockSkuid), len(nohasSkuid), len(unUseSkuid),
int(time.time() * 1000) - start)
if len(unUseSkuid) > 0:
logger.info('[%s]口罩已经下柜', ','.join(unUseSkuid))
return inStockSkuid
'''
提交订单
'''
def submit_order(session, risk_control, sku_id, skuids, submit_Time, encryptClientInfo, is_Submit_captcha, payment_pwd,
submit_captcha_text, submit_captcha_rid):
"""
重要:
1.该方法只适用于普通商品的提交订单(即可以加入购物车,然后结算提交订单的商品)
2.提交订单时,会对购物车中勾选✓的商品进行结算(如果勾选了多个商品,将会提交成一个订单)
:return: True/False 订单提交结果
"""
url = 'https://trade.jd.com/shopping/order/submitOrder.action'
# js function of submit order is included in https://trade.jd.com/shopping/misc/js/order.js?r=2018070403091
data = {
'overseaPurchaseCookies': '',
'vendorRemarks': '[]',
'submitOrderParam.sopNotPutInvoice': 'false',
'submitOrderParam.trackID': 'TestTrackId',
'submitOrderParam.ignorePriceChange': '0',
'submitOrderParam.btSupport': '0',
'riskControl': risk_control,
'submitOrderParam.isBestCoupon': 1,
'submitOrderParam.jxj': 1,
'submitOrderParam.trackId': '9643cbd55bbbe103eef18a213e069eb0', # Todo: need to get trackId
# 'submitOrderParam.eid': eid,
# 'submitOrderParam.fp': fp,
'submitOrderParam.needCheck': 1,
}
def encrypt_payment_pwd(payment_pwd):
return ''.join(['u3' + x for x in payment_pwd])
if len(payment_pwd) > 0:
data['submitOrderParam.payPassword'] = encrypt_payment_pwd(payment_pwd)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "http://trade.jd.com/shopping/order/getOrderInfo.action",
"Connection": "keep-alive",
'Host': 'trade.jd.com',
}
for count in range(1, 3):
logger.info('第[%s/%s]次尝试提交订单', count, 3)
try:
if is_Submit_captcha:
captcha_result = page_detail_captcha(session, encryptClientInfo)
# 验证码服务错误
if not captcha_result:
logger.error('验证码服务异常')
continue
data['submitOrderParam.checkcodeTxt'] = submit_captcha_text
data['submitOrderParam.checkCodeRid'] = submit_captcha_rid
resp = session.post(url=url, data=data, headers=headers)
resp_json = json.loads(resp.text)
logger.info('本次提交订单耗时[%s]毫秒', str(int(time.time() * 1000) - submit_Time))
# 返回信息示例:
# 下单失败
# {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 60123, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '请输入支付密码!'}
# {'overSea': False, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'orderXml': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 60017, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '您多次提交过快,请稍后再试'}
# {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 60077, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '获取用户订单信息失败'}
# {"cartXml":null,"noStockSkuIds":"xxx","reqInfo":null,"hasJxj":false,"addedServiceList":null,"overSea":false,"orderXml":null,"sign":null,"pin":"xxx","needCheckCode":false,"success":false,"resultCode":600157,"orderId":0,"submitSkuNum":0,"deductMoneyFlag":0,"goJumpOrderCenter":false,"payInfo":null,"scaleSkuInfoListVO":null,"purchaseSkuInfoListVO":null,"noSupportHomeServiceSkuList":null,"msgMobile":null,"addressVO":{"pin":"xxx","areaName":"","provinceId":xx,"cityId":xx,"countyId":xx,"townId":xx,"paymentId":0,"selected":false,"addressDetail":"xx","mobile":"xx","idCard":"","phone":null,"email":null,"selfPickMobile":null,"selfPickPhone":null,"provinceName":null,"cityName":null,"countyName":null,"townName":null,"giftSenderConsigneeName":null,"giftSenderConsigneeMobile":null,"gcLat":0.0,"gcLng":0.0,"coord_type":0,"longitude":0.0,"latitude":0.0,"selfPickOptimize":0,"consigneeId":0,"selectedAddressType":0,"siteType":0,"helpMessage":null,"tipInfo":null,"cabinetAvailable":true,"limitKeyword":0,"specialRemark":null,"siteProvinceId":0,"siteCityId":0,"siteCountyId":0,"siteTownId":0,"skuSupported":false,"addressSupported":0,"isCod":0,"consigneeName":null,"pickVOname":null,"shipmentType":0,"retTag":0,"tagSource":0,"userDefinedTag":null,"newProvinceId":0,"newCityId":0,"newCountyId":0,"newTownId":0,"newProvinceName":null,"newCityName":null,"newCountyName":null,"newTownName":null,"checkLevel":0,"optimizePickID":0,"pickType":0,"dataSign":0,"overseas":0,"areaCode":null,"nameCode":null,"appSelfPickAddress":0,"associatePickId":0,"associateAddressId":0,"appId":null,"encryptText":null,"certNum":null,"used":false,"oldAddress":false,"mapping":false,"addressType":0,"fullAddress":"xxxx","postCode":null,"addressDefault":false,"addressName":null,"selfPickAddressShuntFlag":0,"pickId":0,"pickName":null,"pickVOselected":false,"mapUrl":null,"branchId":0,"canSelected":false,"address":null,"name":"xxx","message":null,"id":0},"msgUuid":null,"message":"xxxxxx商品无货"}
# {'orderXml': None, 'overSea': False, 'noStockSkuIds': 'xxx', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'cartXml': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 600158, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': {'oldAddress': False, 'mapping': False, 'pin': 'xxx', 'areaName': '', 'provinceId': xx, 'cityId': xx, 'countyId': xx, 'townId': xx, 'paymentId': 0, 'selected': False, 'addressDetail': 'xxxx', 'mobile': 'xxxx', 'idCard': '', 'phone': None, 'email': None, 'selfPickMobile': None, 'selfPickPhone': None, 'provinceName': None, 'cityName': None, 'countyName': None, 'townName': None, 'giftSenderConsigneeName': None, 'giftSenderConsigneeMobile': None, 'gcLat': 0.0, 'gcLng': 0.0, 'coord_type': 0, 'longitude': 0.0, 'latitude': 0.0, 'selfPickOptimize': 0, 'consigneeId': 0, 'selectedAddressType': 0, 'newCityName': None, 'newCountyName': None, 'newTownName': None, 'checkLevel': 0, 'optimizePickID': 0, 'pickType': 0, 'dataSign': 0, 'overseas': 0, 'areaCode': None, 'nameCode': None, 'appSelfPickAddress': 0, 'associatePickId': 0, 'associateAddressId': 0, 'appId': None, 'encryptText': None, 'certNum': None, 'addressType': 0, 'fullAddress': 'xxxx', 'postCode': None, 'addressDefault': False, 'addressName': None, 'selfPickAddressShuntFlag': 0, 'pickId': 0, 'pickName': None, 'pickVOselected': False, 'mapUrl': None, 'branchId': 0, 'canSelected': False, 'siteType': 0, 'helpMessage': None, 'tipInfo': None, 'cabinetAvailable': True, 'limitKeyword': 0, 'specialRemark': None, 'siteProvinceId': 0, 'siteCityId': 0, 'siteCountyId': 0, 'siteTownId': 0, 'skuSupported': False, 'addressSupported': 0, 'isCod': 0, 'consigneeName': None, 'pickVOname': None, 'shipmentType': 0, 'retTag': 0, 'tagSource': 0, 'userDefinedTag': None, 'newProvinceId': 0, 'newCityId': 0, 'newCountyId': 0, 'newTownId': 0, 'newProvinceName': None, 'used': False, 'address': None, 'name': 'xx', 'message': None, 'id': 0}, 'msgUuid': None, 'message': 'xxxxxx商品无货'}
# {"orderXml":null,"cartXml":null,"noStockSkuIds":"","reqInfo":null,"hasJxj":false,"overSea":false,"addedServiceList":null,"sign":null,"pin":null,"needCheckCode":true,"success":false,"resultCode":0,"orderId":0,"submitSkuNum":0,"deductMoneyFlag":0,"goJumpOrderCenter":false,"payInfo":null,"scaleSkuInfoListVO":null,"purchaseSkuInfoListVO":null,"noSupportHomeServiceSkuList":null,"msgMobile":null,"addressVO":null,"msgUuid":null,"message":"验证码不正确,请重新填写"}
# {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'jd_7c3992aa27d1a', 'needCheckCode': False, 'success': False, 'resultCode': 60070, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '抱歉,您当前选择的省份无法购买商品星工 KN95口罩防雾霾防尘防花粉PM2.5 硅胶鼻垫带阀耳戴透气 工业粉尘防护口罩 白色25只独立包装'}
# 下单成功
# {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': True, 'resultCode': 0, 'orderId': 8740xxxxx, 'submitSkuNum': 1, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': None}
if resp_json.get('success'):
logger.info('订单提交成功! 订单号:%s', resp_json.get('orderId'))
return True
else:
resultMessage, result_code = resp_json.get('message'), resp_json.get('resultCode')
if result_code == 0:
# self._save_invoice()
if '验证码不正确' in resultMessage:
resultMessage = resultMessage + '(验证码错误)'
logger.info('提交订单验证码[错误]')
continue
else:
resultMessage = resultMessage + '(下单商品可能为第三方商品,将切换为普通发票进行尝试)'
elif result_code == 60077:
resultMessage = resultMessage + '(可能是购物车为空 或 未勾选购物车中商品)'
elif result_code == 60123:
resultMessage = resultMessage + '(需要在payment_pwd参数配置支付密码)'
elif result_code == 60070:
resultMessage = resultMessage + '(省份不支持销售)'
skuids.remove(sku_id)
logger.info('[%s]类型口罩不支持销售踢出', sku_id)
logger.info('订单提交失败, 错误码:%s, 返回信息:%s', result_code, resultMessage)
logger.info(resp_json)
return False
except Exception as e:
print(traceback.format_exc())
continue
'''
订单页面验证码
'''
def page_detail_captcha(session, isId):
url = 'https://captcha.jd.com/verify/image'
acid = '{}_{}'.format(random.random(), random.random())
payload = {
'acid': acid,
'srcid': 'trackWeb',
'is': isId,
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/531.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Referer": "https://trade.jd.com/shopping/order/getOrderInfo.action",
"Connection": "keep-alive",
'Host': 'captcha.jd.com',
}
try:
resp = session.get(url=url, params=payload, headers=headers)
if not response_status(resp):
logger.error('获取订单验证码失败')
return ''
logger.info('解析验证码开始')
image = Image.open(BytesIO(resp.content))
image.save('captcha.jpg')
result = analysis_captcha(resp.content)
if not result:
logger.error('解析订单验证码失败')
return ''
global submit_captcha_text, submit_captcha_rid
submit_captcha_text = result
submit_captcha_rid = acid
return result
except Exception as e:
logger.error('订单验证码获取异常:%s', e)
return ''
def analysis_captcha(session, captchaUrl, pic):
for i in range(1, 10):
try:
url = captchaUrl
resp = session.post(url, pic)
if not response_status(resp):
logger.error('解析验证码失败')
continue
logger.info('解析验证码[%s]', resp.text)
return resp.text
except Exception as e:
print(traceback.format_exc())
continue
return ''
# -*- coding=utf-8 -*-
import logging
import logging.handlers
'''
日志模块
'''
LOG_FILENAME = 'jdBuyMask.log'
logger = logging.getLogger()
def set_logger():
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s')
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
file_handler = logging.handlers.RotatingFileHandler(
LOG_FILENAME, maxBytes=10485760, backupCount=5, encoding="utf-8")
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
set_logger()
# -*- encoding=utf8 -*-
from jdEmail import sendMail
from wechat_ftqq import sendWechat
class message(object):
"""消息推送类"""
def __init__(self, messageType, sc_key, mail):
if messageType == '2':
if not sc_key:
raise Exception('sc_key can not be empty')
self.sc_key = sc_key
elif messageType == '1':
if not mail:
raise Exception('mail can not be empty')
self.mail = mail
self.messageType = messageType
def send(self, desp='', isOrder=False):
desp = str(desp)
if isOrder:
msg = desp + ' 类型口罩,已经下单了。24小时内付款'
else:
msg = desp + ' 类型口罩,下单失败了'
if self.messageType == '1':
sendMail(self.mail, msg)
if self.messageType == '2':
sendWechat(sc_key=self.sc_key, desp=msg)
def sendAny(self, desp=''):
desp = str(desp)
msg = desp
if self.messageType == '1':
sendMail(self.mail, msg)
if self.messageType == '2':
sendWechat(sc_key=self.sc_key, desp=msg)
# -*- coding=utf-8 -*-
import hashlib
import json
import socket
import requests
_dnscache = {}
def parse_json(s):
begin = s.find('{')
end = s.rfind('}') + 1
return json.loads(s[begin:end])
def getconfigMd5():
with open('configDemo.ini', 'r', encoding='utf-8') as f:
configText = f.read()
return hashlib.md5(configText.encode('utf-8')).hexdigest()
def response_status(resp):
if resp.status_code != requests.codes.OK:
print('Status: %u, Url: %s' % (resp.status_code, resp.url))
return False
return True
def _setDNSCache():
"""
Makes a cached version of socket._getaddrinfo to avoid subsequent DNS requests.
"""
def _getaddrinfo(*args, **kwargs):
global _dnscache
if args in _dnscache:
# print(str(args) + " in cache")
return _dnscache[args]
else:
# print(str(args) + " not in cache")
_dnscache[args] = socket._getaddrinfo(*args, **kwargs)
return _dnscache[args]
if not hasattr(socket, '_getaddrinfo'):
socket._getaddrinfo = socket.getaddrinfo
socket.getaddrinfo = _getaddrinfo
#!/usr/bin/env python
# -*- encoding=utf8 -*-
import datetime
import json
import requests
from jdlogger import logger
def sendWechat(sc_key, text='京东商品监控', desp=''):
if not text.strip():
logger.error('Text of message is empty!')
return
now_time = str(datetime.datetime.now())
desp = '[{0}]'.format(now_time) if not desp else '{0} [{1}]'.format(desp, now_time)
try:
resp = requests.get(
'https://sc.ftqq.com/{}.send?text={}&desp={}'.format(sc_key, text, desp)
)
resp_json = json.loads(resp.text)
if resp_json.get('errno') == 0:
logger.info('Message sent successfully [text: %s, desp: %s]', text, desp)
else:
logger.error('Fail to send message, reason: %s', resp.text)
except requests.exceptions.RequestException as req_error:
logger.error('Request error: %s', req_error)
except Exception as e:
logger.error('Fail to send message [text: %s, desp: %s]: %s', text, desp, e)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册