Skip to content

Commit

Permalink
Merge pull request #1 from huanghyw/master
Browse files Browse the repository at this point in the history
pull
  • Loading branch information
fyqtian authored Dec 30, 2020
2 parents 06294f3 + 73b5719 commit fb01d3c
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 21 deletions.
51 changes: 36 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@

### 数据统计

| 案例 | 小白信用 | 90016 | 90008 | 抢到耗时 |
| ---- | ---- | ---- | ---- | ---- |
| 张三 | 63.8 | 59.63% | 40.37% | 暂未抢到 |
| 李四 | 92.9 | 72.05% | 27.94% | 4天 |
| 王五 | 99.6 | 75.70% | 24.29% | 暂未抢到 |
| 赵六 | 103.4 | 91.02% | 8.9% | 2天 |
| 案例 | 小白信用 | 90016 | 90008 | 抢到耗时 |
| ---- | -------- | ------ | ------ | -------- |
| 张三 | 63.8 | 59.63% | 40.37% | 暂未抢到 |
| 李四 | 92.9 | 72.05% | 27.94% | 4天 |
| 王五 | 99.6 | 75.70% | 24.29% | 暂未抢到 |
| 赵六 | 103.4 | 91.02% | 8.9% | 2天 |

### 猜测
推测返回90008是京东的风控机制,代表这次请求直接失败,不参与抢购。
Expand Down Expand Up @@ -79,6 +79,8 @@

- 需要使用到的库已经放在requirements.txt,使用pip安装的可以使用指令
`pip install -r requirements.txt`
- 如果国内安装第三方库比较慢,可以使用以下指令进行清华源加速
`pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/`

## 使用教程
#### 1. 推荐Chrome浏览器
Expand All @@ -88,23 +90,42 @@
> 随便找一个商品下单,然后进入结算页面,打开浏览器的调试窗口,切换到控制台Tab页,在控制台中输入变量`_JdTdudfp`,即可从输出的Json中获取`eid``fp`
> 不会的话参考原作者的issue https://github.com/zhou-xiaojun/jd_mask/issues/22
(2)`sku_id`,`DEFAULT_USER_AGENT`
(2)`sku_id`,`DEFAULT_USER_AGENT`
> `sku_id`已经按照茅台的填好。
> `cookies_string` 现在已经不需要填写了
> `DEFAULT_USER_AGENT` 可以用默认的
> sku_id我已经按照茅台的填好
> `DEFAULT_USER_AGENT` 可以用默认的。谷歌浏览器也可以浏览器地址栏中输入about:version 查看`USER_AGENT`替换
(3)配置一下时间
> 建议同步网络时间,有条件的设置定时,每小时同步一次
> 现在不强制要求同步最新时间了,程序会自动同步京东时间
>> 但要是电脑时间快慢了好几个小时,最好还是同步一下吧
以上都是必须的.
> tips:
> 在程序开始运行后,会检测本地时间与京东服务器时间,输出的差值为本地时间-京东服务器时间,即-50为本地时间比京东服务器时间慢50ms。
> 本代码的执行的抢购时间以本地电脑/服务器时间为准
(4)修改抢购瓶数
> 代码中默认抢购瓶数为2,且无法在配置文件中修改
> 如果一个月内抢购过一瓶,最好修改抢购瓶数为1
> 具体修改为:在`jd_spider_requests.py`文件中搜索`self.seckill_num = 2`,将`2`改为`1`
#### 4.运行main.py
根据提示选择相应功能即可
根据提示选择相应功能即可。如果出现请扫码登录的提示可查看项目目录下是否存在`qr_code.png`文件,若存在打开图片,并使用京东手机APP扫码登录即可。

## 打赏
要是客官抢到了茅台,心情好,请我喝一杯咖啡好不好:)
![收款二维码](https://github.com/huanghyw/jd_seckill/blob/master/resource/payment_code.png)
- *Linux下命令行方式显示二维码(以Ubuntu为例)*

```bash
$ sudo apt-get install qrencode zbar-tools # 安装二维码解析和生成的工具,用于读取二维码并在命令行输出。
$ zbarimg qr_code.png > qrcode.txt && qrencode -r qrcode.txt -o - -t UTF8 # 解析二维码输出到命令行窗口。
```

#### 5.抢购结果确认
抢购是否成功通常在程序开始的一分钟内可见分晓!
搜索日志,出现“抢购成功,订单号xxxxx",代表成功抢到了,务必半小时内支付订单!程序暂时不支持自动停止,需要手动STOP!
若两分钟还未抢购成功,基本上就是没抢到!程序暂时不支持自动停止,需要手动STOP!

## 打赏
不用再打赏了,抢到茅台的同学请保持这份喜悦,没抢到的继续加油 :)

## 感谢
##### 非常感谢原作者 https://github.com/zhou-xiaojun/jd_mask 提供的代码
Expand Down
11 changes: 11 additions & 0 deletions config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,14 @@ payment_pwd = ""
# 开启消息推送必须填入 sckey,如何获取请参考 http://sc.ftqq.com/3.version。感谢Server酱~
enable = false
sckey =

# 使用了smtp邮箱推送服务
# 如果想开启登录二维码邮箱推送,则将 email_enable 设置为 true,默认为 false 不开启邮箱推送
# 开启消息推送必须填入 email_user、email_pwd,email_host 若不填则会自动判断,email_pwd 如何获取请自行百度。
email_enable = false
# 邮箱域名 smtp.xx.com
email_host =
# 邮箱地址 [email protected]
email_user =
# 邮箱授权码(并不一定是邮箱密码) xxxxxxxxxxxxxxxx
email_pwd =
11 changes: 8 additions & 3 deletions jd_spider_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
wait_some_time,
response_status,
save_image,
open_image
open_image,
email
)


Expand Down Expand Up @@ -176,6 +177,9 @@ def _get_qrcode(self):
save_image(resp, self.qrcode_img_file)
logger.info('二维码获取成功,请打开京东APP扫描')
open_image(self.qrcode_img_file)
if global_config.getRaw('messenger', 'email_enable') == 'true':
email.send('二维码获取成功,请打开京东APP扫描', "<img src='cid:qr_code.png'>", [email.mail_user], 'qr_code.png')

return True

def _get_qrcode_ticket(self):
Expand Down Expand Up @@ -341,6 +345,7 @@ def _reserve(self):
while True:
try:
self.make_reserve()
break
except Exception as e:
logger.info('预约发生异常!', e)
wait_some_time()
Expand Down Expand Up @@ -391,7 +396,7 @@ def get_username(self):
"""获取用户信息"""
url = 'https://passport.jd.com/user/petName/getUserInfoForMiniJd.action'
payload = {
'callback': 'jQuery'.format(random.randint(1000000, 9999999)),
'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)),
'_': str(int(time.time() * 1000)),
}
headers = {
Expand Down Expand Up @@ -617,4 +622,4 @@ def submit_seckill_order(self):
if global_config.getRaw('messenger', 'enable') == 'true':
error_message = '抢购失败,返回信息:{}'.format(resp_json)
send_wechat(error_message)
return False
return False
Binary file removed resource/payment_code.png
Binary file not shown.
38 changes: 35 additions & 3 deletions timer.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,52 @@
# -*- coding:utf-8 -*-
import time
import requests
import json

from datetime import datetime
from jd_logger import logger
from config import global_config


class Timer(object):
def __init__(self, sleep_interval=0.5):
# '2018-09-28 22:45:50.000'
self.buy_time = datetime.strptime(global_config.getRaw('config','buy_time'), "%Y-%m-%d %H:%M:%S.%f")
self.buy_time_ms = int(time.mktime(self.buy_time.timetuple()) * 1000.0 + self.buy_time.microsecond / 1000)
self.sleep_interval = sleep_interval

self.diff_time = self.local_jd_time_diff()

def jd_time(self):
"""
从京东服务器获取时间毫秒
:return:
"""
url = 'https://a.jd.com//ajax/queryServerData.html'
ret = requests.get(url).text
js = json.loads(ret)
return int(js["serverTime"])

def local_time(self):
"""
获取本地毫秒时间
:return:
"""
return int(round(time.time() * 1000))

def local_jd_time_diff(self):
"""
计算本地与京东服务器时间差
:return:
"""
return self.local_time() - self.jd_time()

def start(self):
logger.info('正在等待到达设定时间:%s' % self.buy_time)
now_time = datetime.now
logger.info('正在等待到达设定时间:{},检测本地时间与京东服务器时间误差为【{}】毫秒'.format(self.buy_time, self.diff_time))
while True:
if now_time() >= self.buy_time:
# 本地时间减去与京东的时间差,能够将时间误差提升到0.1秒附近
# 具体精度依赖获取京东服务器时间的网络时间损耗
if self.local_time() - self.diff_time >= self.buy_time_ms:
logger.info('时间到达,开始执行……')
break
else:
Expand Down
72 changes: 72 additions & 0 deletions util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@
import requests
import os
import time
import smtplib

from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage

from config import global_config
from jd_logger import logger


USER_AGENTS = [
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
Expand Down Expand Up @@ -114,3 +121,68 @@ def save_image(resp, image_file):
with open(image_file, 'wb') as f:
for chunk in resp.iter_content(chunk_size=1024):
f.write(chunk)


class Email():

def __init__(self, mail_user, mail_pwd, mail_host=''):
if global_config.getRaw('messenger', 'email_enable') == 'false':
return

smtpObj = smtplib.SMTP()
# 没传会自动判断 判断不出来默认QQ邮箱
if mail_host:
self.mail_host = mail_host
elif mail_user.endswith('163.com'):
self.mail_host = 'smtp.163.com'
elif mail_user.endswith(('sina.com', 'sina.cn')):
self.mail_host = 'smtp.163.com'
elif mail_user.endswith('qq.com'):
self.mail_host = 'smtp.qq.com'
elif mail_user.endswith('sohu.com'):
self.mail_host = 'smtp.sohu.com'
else:
self.mail_host = 'smtp.qq.com'
self.mail_user = mail_user
self.is_login = False
try:
smtpObj.connect(mail_host, 25)
smtpObj.login(mail_user, mail_pwd)
self.is_login = True
except Exception as e:
logger.info('邮箱登录失败!', e)
self.smtpObj = smtpObj

def send(self, title, msg, receivers: list, img=''):
"""
发送smtp邮件至收件人
:param title:
:param msg: 如果发送图片,需在msg内嵌入<img src='cid:xxx'>,xxx为图片名
:param receivers:
:param img: 图片名
:return:
"""
if self.is_login:
message = MIMEMultipart('alternative')
msg_html = MIMEText(msg, 'html', 'utf-8')
message.attach(msg_html)
message['Subject'] = title
message['From'] = self.mail_user
if img:
with open(img, "rb") as f:
msg_img = MIMEImage(f.read())
msg_img.add_header('Content-ID', img)
message.attach(msg_img)
try:
self.smtpObj.sendmail(self.mail_user, receivers, message.as_string())
except Exception as e:
logger.info('邮件发送失败!', e)
else:
logger.info('邮箱未登录')


email = Email(
mail_host=global_config.getRaw('messenger', 'email_host'),
mail_user=global_config.getRaw('messenger', 'email_user'),
mail_pwd=global_config.getRaw('messenger', 'email_pwd'),
)

0 comments on commit fb01d3c

Please sign in to comment.