Forráskód Böngészése

Merge pull request #38 from huanghyw/feature-hyw-qrcode_login

Feature hyw qrcode login
红头土豆 5 éve
szülő
commit
7a49008552
6 módosított fájl, 383 hozzáadás és 103 törlés
  1. 3 0
      .gitignore
  2. 3 4
      README.md
  3. 9 9
      config.ini
  4. 10 0
      exception.py
  5. 323 56
      jd_spider_requests.py
  6. 35 34
      util.py

+ 3 - 0
.gitignore

@@ -115,6 +115,9 @@ venv.bak/
 logs/
 .test
 test/
+cookies/
+*.cookies
+qr_code.png
 
 # jupyter
 .ipynb_checkpoints

+ 3 - 4
README.md

@@ -27,6 +27,7 @@
 通过我这段时间的使用(2020-12-12至2020-12-17),证实这个脚本确实能抢到茅台。我自己三个账号抢了四瓶,帮两个朋友抢了4瓶。
 大家只要确认自己配置文件没有问题,Cookie没有失效,坚持下去总能成功的。
 
+
 ## 暗中观察
 
 根据12月14日以来抢茅台的日志分析,大胆推断再接再厉返回Json消息中`resultCode`与小白信用的关系。  
@@ -83,10 +84,8 @@
 > 随便找一个商品下单,然后进入结算页面,打开浏览器的调试窗口,切换到控制台Tab页,在控制台中输入变量`_JdTdudfp`,即可从输出的Json中获取`eid`和`fp`。  
 > 不会的话参考原作者的issue https://github.com/zhou-xiaojun/jd_mask/issues/22
 
-(2)`cookies_String`,`sku_id`,`DEFAULT_USER_AGENT`(和cookie获取同一个地方就会看到.直接复制进去就可以了) 
-> 这里注意每次扫码登陆后都需要重新获取cookies_string,其他两个不用  
-> `cookies_string` 就是`http request header`里面的`cookie`
-> Cookie最好在订单结算页面获取  
+(2)`sku_id`,`DEFAULT_USER_AGENT`(`user-agent`在cookie获取的那个地方就会看到.直接复制进去就可以了) 
+> `cookies_string` 现在已经不需要填写了
 > sku_id我已经按照茅台的填好 
 
 (3)配置一下时间

+ 9 - 9
config.ini

@@ -1,18 +1,18 @@
 [config]
 # eid, fp参数必须填写,具体请参考 wiki-常见问题
 # 随意填写可能导致订单无法提交等问题
-eid = "XMKWG35OFYBQN3C5JYD7J4FNOWNXSGBIZNIKBUVVQEMJW6766KKTNYZD43U3Y7VCWA73EEWJRCOTKU7QVG3WKUNNQA"
-fp = "2ea01d1acb70ab62c8607786d7273205"
-# cookie
-cookies_String ="__jdu=1586273021865136663377; shshshfpa=1d79a82c-037d-8e4e-47cb-e35d2f9e66ae-1586273130; shshshfpb=ypTYIktPFs89QK5nxn8nGIw%3D%3D; unpl=V2_ZzNtbRYHRxZ1X0EBfktdDGIDEFoSUhccJlxHXHNOWAQ3V0YJclRCFnQUR1BnGlQUZwQZWEdcQRBFCEdkeB5fA2AFEFlBZxpFK0oYEDlNDEY1WnxdRF9DFnEAR1B9KWwGZzMSXHJXRxxyC0BWehpcDGAHEl9FUEQXdw5AZEsebDVXARpZR1NHJXQ4R2Q5TQAFbgUQXA9XRxxyC0BWehpcDGAHEl9FUEQXdw5AZHopXw%3d%3d; __jdv=76161171|haosou-search|t_262767352_haosousearch|cpc|1791259057_0_ea521f6d4c084136a4e8be099f50aeee|1590741875314; user-key=e7b2ce43-c8ea-498b-aa14-1d72ba811c3b; cn=14; ipLocation=%u5e7f%u897f; pinId=RKLv5CxPY3leYrbiqjD_zrV9-x-f3wj7; pin=243864730-341142; unick=%E8%9B%8B%E9%BB%84%E9%85%B1%E5%95%8A%E5%AE%9D%E5%AE%9D; ceshi3.com=201; _tp=G9fKS8tOq9vYWtFzoQL6S58tnu1J7NXnsaIj9rKmcI8%3D; _pst=243864730-341142; areaId=20; ipLoc-djd=20-1761-25188-0; TrackID=164w4EaXgdVB8pmGgufMvZxffVg93QU3DR1qoyUmb9AbFnt_XMZ4cCggrBVqxQbwH6Sq6wvHiQ0tfbjTM9Mez0AhaI61woZr-qrMvbKOsZZT67SVvoLrkZfKMAeezI4Xx; thor=721925D5F86146929EDB9E937057356EBD65E8075AF346CDA6DFD00A839FDAAE5B800AD469815047B419498006934452FA72C6513EDDC62E79BDBB5883B46087391FF3C8EE2E5C2246FA96BF54ADD140DF2918C35D29DCE2BE90D0FEBEE2263DEEE9DEE73277CFE2BC97AA575462FDE847C8209285C17235C0566829976AABB1BFDA92428C8EA169ADB615E06CB05003B543B48B7BD5F744BDED08A66B10D5D3; __jda=122270672.1586273021865136663377.1586273022.1591320625.1591667887.11; __jdc=122270672; 3AB9D23F7A4B3C9B=XMKWG35OFYBQN3C5JYD7J4FNOWNXSGBIZNIKBUVVQEMJW6766KKTNYZD43U3Y7VCWA73EEWJRCOTKU7QVG3WKUNNQA; __jdb=122270672.8.1586273021865136663377|11.1591667887; shshshfp=4d12604ada2ade89734b633f704fe848; shshshsID=b512228e41868a27ab8199eb73396a90_4_1591667938758"
+eid = ""
+fp = ""
+# cookie现在不需要填写了
+# cookies_String = ""
 
-#cookies_String = "__jdu=1586273021865136663377; shshshfpa=1d79a82c-037d-8e4e-47cb-e35d2f9e66ae-1586273130; shshshfpb=ypTYIktPFs89QK5nxn8nGIw%3D%3D; ceshi3.com=201; unpl=V2_ZzNtbRYHRxZ1X0EBfktdDGIDEFoSUhccJlxHXHNOWAQ3V0YJclRCFnQUR1BnGlQUZwQZWEdcQRBFCEdkeB5fA2AFEFlBZxpFK0oYEDlNDEY1WnxdRF9DFnEAR1B9KWwGZzMSXHJXRxxyC0BWehpcDGAHEl9FUEQXdw5AZEsebDVXARpZR1NHJXQ4R2Q5TQAFbgUQXA9XRxxyC0BWehpcDGAHEl9FUEQXdw5AZHopXw%3d%3d; __jdv=76161171|haosou-search|t_262767352_haosousearch|cpc|1791259057_0_ea521f6d4c084136a4e8be099f50aeee|1590741875314; areaId=20; user-key=e7b2ce43-c8ea-498b-aa14-1d72ba811c3b; TrackID=14zpGGB-OpJ4mkipibzlKLH_cntpvRWi1YuLUjQQ9V-kkhdgGNzIYgnDptMlqfJhNhu8ozJ_qMXppTXszcg6uSBKf3xsYTd9LGMPVWn_V3mfk98AjllQ77oNoZNf0vk_n; pinId=tnz4f2JFgMgyIVQ1is9ieg; pin=275466402_m; unick=%E4%B8%8D%E6%83%B3%E8%B7%91%E6%AD%A5%E7%9A%84%E5%B9%B2%E9%94%85%E9%94%85; _tp=hbuicMy40IDnjEA3yKCYJg%3D%3D; _pst=275466402_m; __jda=122270672.1586273021865136663377.1586273022.1590741875.1590853863.6; __jdc=122270672; shshshfp=4502485e8b520f53be89fe44a27717ee; 3AB9D23F7A4B3C9B=XMKWG35OFYBQN3C5JYD7J4FNOWNXSGBIZNIKBUVVQEMJW6766KKTNYZD43U3Y7VCWA73EEWJRCOTKU7QVG3WKUNNQA; cn=14; ipLoc-djd=20-1761-25188-51466.2052701265; ipLocation=%u5e7f%u897f; __tak=c94559a7e3893645ee7f9f3ba3af391b4381d8e935b4895cf5f4df48e014c4aca54c30a40df88a95e670fcf3ef153823cfc92c013e7e892133119c9f79811f1b9303c52b4dffffebb6b145cbe13fc0e0; thor=A3006E40DFDA6A23E9353F8A5BE5D7B526D907891A33D2D63A28B4F6F19DC6F67E8BAA1251F8FC51DA54936D111277AEF79F42D5BBCA8C7F01068EA42B4B681CF3D78B4CB73DF9E870791980E609BABE4426FF4EF4089405842C640217A10FEC30256F6BC66C63C67D595F08AE093A375782F916DE74D980FE9E18720F2D5987B03269FB13DC923CCCEC51A5FD7155FD; shshshsID=b122acc455c4e500fe48a45f327cbeac_17_1590855792368; __jdb=122270672.25.1586273021865136663377|6.1590853863"
-# 商品id,已经是茅台的sku_id了
+# 商品id
+# 已经是茅台的sku_id了
 sku_id = 100012043978
-# 设定时间 # 2020-02-24 10:00:00.100000
-buy_time = 2020-06-09 10:00:00.150
+# 设定时间 # 2020-12-09 10:00:00.100000
+buy_time = 2020-12-22 09:59:59.500
 # 默认UA
-DEFAULT_USER_AGENT = ""
+DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
 # 是否使用随机 useragent,默认为 false
 random_useragent = false
 

+ 10 - 0
exception.py

@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# -*- encoding=utf8 -*-
+from jd_logger import logger
+
+
+class SKException(Exception):
+
+    def __init__(self, message):
+        super().__init__(message)
+        logger.error(message)

+ 323 - 56
jd_spider_requests.py

@@ -1,44 +1,330 @@
 import random
-import sys
 import time
+import requests
+import functools
+import json
+import os
+import pickle
+
+from lxml import etree
 from jd_logger import logger
 from timer import Timer
-import requests
-from util import parse_json, get_session, get_sku_title,send_wechat
 from config import global_config
 from concurrent.futures import ProcessPoolExecutor
+from exception import SKException
+from util import (
+    parse_json,
+    send_wechat,
+    wait_some_time,
+    response_status,
+    save_image,
+    open_image
+)
+
+
+class SpiderSession:
+    """
+    Session相关操作
+    """
+    def __init__(self):
+        self.cookies_dir_path = "./cookies/"
+        self.user_agent = global_config.getRaw('config', 'DEFAULT_USER_AGENT')
+
+        self.session = self._init_session()
+
+    def _init_session(self):
+        session = requests.session()
+        session.headers = self.get_headers()
+        return session
+
+    def get_headers(self):
+        return {"User-Agent": self.user_agent,
+                "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"}
+
+    def get_user_agent(self):
+        return self.user_agent
+
+    def get_session(self):
+        """
+        获取当前Session
+        :return:
+        """
+        return self.session
+
+    def get_cookies(self):
+        """
+        获取当前Cookies
+        :return:
+        """
+        return self.get_session().cookies
+
+    def set_cookies(self, cookies):
+        self.session.cookies.update(cookies)
+
+    def load_cookies_from_local(self):
+        """
+        从本地加载Cookie
+        :return:
+        """
+        cookies_file = ''
+        if not os.path.exists(self.cookies_dir_path):
+            return False
+        for name in os.listdir(self.cookies_dir_path):
+            if name.endswith(".cookies"):
+                cookies_file = '{}{}'.format(self.cookies_dir_path, name)
+                break
+        if cookies_file == '':
+            return False
+        with open(cookies_file, 'rb') as f:
+            local_cookies = pickle.load(f)
+        self.set_cookies(local_cookies)
+
+    def save_cookies_to_local(self, cookie_file_name):
+        """
+        保存Cookie到本地
+        :param cookie_file_name: 存放Cookie的文件名称
+        :return:
+        """
+        cookies_file = '{}{}.cookies'.format(self.cookies_dir_path, cookie_file_name)
+        directory = os.path.dirname(cookies_file)
+        if not os.path.exists(directory):
+            os.makedirs(directory)
+        with open(cookies_file, 'wb') as f:
+            pickle.dump(self.get_cookies(), f)
+
+
+class QrLogin:
+    """
+    扫码登录
+    """
+    def __init__(self, spider_session: SpiderSession):
+        """
+        初始化扫码登录
+        大致流程:
+            1、访问登录二维码页面,获取Token
+            2、使用Token获取票据
+            3、校验票据
+        :param spider_session:
+        """
+        self.qrcode_img_file = 'qr_code.png'
+
+        self.spider_session = spider_session
+        self.session = self.spider_session.get_session()
+
+        self.is_login = False
+        self.refresh_login_status()
+
+    def refresh_login_status(self):
+        """
+        刷新是否登录状态
+        :return:
+        """
+        self.is_login = self._validate_cookies()
+
+    def _validate_cookies(self):
+        """
+        验证cookies是否有效(是否登陆)
+        通过访问用户订单列表页进行判断:若未登录,将会重定向到登陆页面。
+        :return: cookies是否有效 True/False
+        """
+        url = 'https://order.jd.com/center/list.action'
+        payload = {
+            'rid': str(int(time.time() * 1000)),
+        }
+        try:
+            resp = self.session.get(url=url, params=payload, allow_redirects=False)
+            if resp.status_code == requests.codes.OK:
+                return True
+        except Exception as e:
+            logger.error("验证cookies是否有效发生异常", e)
+        return False
+
+    def _get_login_page(self):
+        """
+        获取PC端登录页面
+        :return:
+        """
+        url = "https://passport.jd.com/new/login.aspx"
+        page = self.session.get(url, headers=self.spider_session.get_headers())
+        return page
+
+    def _get_qrcode(self):
+        """
+        缓存并展示登录二维码
+        :return:
+        """
+        url = 'https://qr.m.jd.com/show'
+        payload = {
+            'appid': 133,
+            'size': 147,
+            't': str(int(time.time() * 1000)),
+        }
+        headers = {
+            'User-Agent': self.spider_session.get_user_agent(),
+            'Referer': 'https://passport.jd.com/new/login.aspx',
+        }
+        resp = self.session.get(url=url, headers=headers, params=payload)
+
+        if not response_status(resp):
+            logger.info('获取二维码失败')
+            return False
+
+        save_image(resp, self.qrcode_img_file)
+        logger.info('二维码获取成功,请打开京东APP扫描')
+        open_image(self.qrcode_img_file)
+        return True
+
+    def _get_qrcode_ticket(self):
+        """
+        通过 token 获取票据
+        :return:
+        """
+        url = 'https://qr.m.jd.com/check'
+        payload = {
+            'appid': '133',
+            'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)),
+            'token': self.session.cookies.get('wlfstk_smdl'),
+            '_': str(int(time.time() * 1000)),
+        }
+        headers = {
+            'User-Agent': self.spider_session.get_user_agent(),
+            'Referer': 'https://passport.jd.com/new/login.aspx',
+        }
+        resp = self.session.get(url=url, headers=headers, params=payload)
+
+        if not response_status(resp):
+            logger.error('获取二维码扫描结果异常')
+            return False
+
+        resp_json = parse_json(resp.text)
+        if resp_json['code'] != 200:
+            logger.info('Code: %s, Message: %s', resp_json['code'], resp_json['msg'])
+            return None
+        else:
+            logger.info('已完成手机客户端确认')
+            return resp_json['ticket']
+
+    def _validate_qrcode_ticket(self, ticket):
+        """
+        通过已获取的票据进行校验
+        :param ticket: 已获取的票据
+        :return:
+        """
+        url = 'https://passport.jd.com/uc/qrCodeTicketValidation'
+        headers = {
+            'User-Agent': self.spider_session.get_user_agent(),
+            'Referer': 'https://passport.jd.com/uc/login?ltype=logout',
+        }
+
+        resp = self.session.get(url=url, headers=headers, params={'t': ticket})
+        if not response_status(resp):
+            return False
+
+        resp_json = json.loads(resp.text)
+        if resp_json['returnCode'] == 0:
+            return True
+        else:
+            logger.info(resp_json)
+            return False
+
+    def login_by_qrcode(self):
+        """
+        二维码登陆
+        :return:
+        """
+        self._get_login_page()
+
+        # download QR code
+        if not self._get_qrcode():
+            raise SKException('二维码下载失败')
+
+        # get QR code ticket
+        ticket = None
+        retry_times = 85
+        for _ in range(retry_times):
+            ticket = self._get_qrcode_ticket()
+            if ticket:
+                break
+            time.sleep(2)
+        else:
+            raise SKException('二维码过期,请重新获取扫描')
+
+        # validate QR code ticket
+        if not self._validate_qrcode_ticket(ticket):
+            raise SKException('二维码信息校验失败')
+
+        self.refresh_login_status()
+
+        logger.info('二维码登录成功')
 
 
 class JdSeckill(object):
     def __init__(self):
+        self.spider_session = SpiderSession()
+        self.spider_session.load_cookies_from_local()
+
+        self.qrlogin = QrLogin(self.spider_session)
+
         # 初始化信息
-        self.session = get_session()
         self.sku_id = global_config.getRaw('config', 'sku_id')
         self.seckill_num = 2
         self.seckill_init_info = dict()
         self.seckill_url = dict()
         self.seckill_order_data = dict()
         self.timers = Timer()
-        self.default_user_agent = global_config.getRaw('config', 'DEFAULT_USER_AGENT')
 
+        self.session = self.spider_session.get_session()
+        self.user_agent = self.spider_session.user_agent
+        self.nick_name = None
+
+    def login_by_qrcode(self):
+        """
+        二维码登陆
+        :return:
+        """
+        if self.qrlogin.is_login:
+            logger.info('登录成功')
+            return
+
+        self.qrlogin.login_by_qrcode()
+
+        if self.qrlogin.is_login:
+            self.nick_name = self.get_username()
+            self.spider_session.save_cookies_to_local(self.nick_name)
+        else:
+            raise SKException("二维码登录失败!")
+
+    def check_login(func):
+        """
+        用户登陆态校验装饰器。若用户未登陆,则调用扫码登陆
+        """
+        @functools.wraps(func)
+        def new_func(self, *args, **kwargs):
+            if not self.qrlogin.is_login:
+                logger.info("{0} 需登陆后调用,开始扫码登陆".format(func.__name__))
+                self.login_by_qrcode()
+            return func(self, *args, **kwargs)
+        return new_func
+
+    @check_login
     def reserve(self):
         """
         预约
         """
-        self.__reserve()
+        self._reserve()
 
+    @check_login
     def seckill(self):
         """
         抢购
         """
-        self.__seckill()
-
-    def system_exit(self):
-        sys.exit(1)
-
-    def wait_some_time(self):
-        time.sleep(random.randint(100, 300) / 1000)
+        self._seckill()
 
+    @check_login
     def seckill_by_proc_pool(self, work_count=5):
         """
         多进程进行抢购
@@ -48,61 +334,34 @@ class JdSeckill(object):
             for i in range(work_count):
                 pool.submit(self.seckill)
 
-    def __reserve(self):
+    def _reserve(self):
         """
         预约
         """
-        self.login()
         while True:
             try:
                 self.make_reserve()
             except Exception as e:
                 logger.info('预约发生异常!', e)
-            self.wait_some_time()
+            wait_some_time()
 
-    def __seckill(self):
+    def _seckill(self):
         """
         抢购
         """
-        self.login()
         while True:
             try:
                 self.request_seckill_url()
                 while True:
                     self.request_seckill_checkout_page()
-                    if self.submit_seckill_order():
-                        self.system_exit()
+                    self.submit_seckill_order()
             except Exception as e:
                 logger.info('抢购发生异常,稍后继续执行!', e)
-            self.wait_some_time()
-
-    def login(self):
-        for flag in range(1, 3):
-            try:
-                targetURL = 'https://order.jd.com/center/list.action'
-                payload = {
-                    'rid': str(int(time.time() * 1000)),
-                }
-                resp = self.session.get(
-                    url=targetURL, params=payload, allow_redirects=False)
-                if resp.status_code == requests.codes.OK:
-                    logger.info('校验是否登录[成功]')
-                    logger.info('用户:{}'.format(self.get_username()))
-                    return True
-                else:
-                    logger.info('校验是否登录[失败]')
-                    logger.info('请重新输入cookie')
-                    time.sleep(1)
-                    continue
-            except Exception as e:
-                logger.info('第【%s】次失败请重新获取cookie', flag)
-                time.sleep(1)
-                continue
-        self.system_exit()
+            wait_some_time()
 
     def make_reserve(self):
         """商品预约"""
-        logger.info('商品名称:{}'.format(get_sku_title()))
+        logger.info('商品名称:{}'.format(self.get_sku_title()))
         url = 'https://yushou.jd.com/youshouinfo.action?'
         payload = {
             'callback': 'fetchJSON',
@@ -110,7 +369,7 @@ class JdSeckill(object):
             '_': str(int(time.time() * 1000)),
         }
         headers = {
-            'User-Agent': self.default_user_agent,
+            'User-Agent': self.user_agent,
             'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id),
         }
         resp = self.session.get(url=url, params=payload, headers=headers)
@@ -136,7 +395,7 @@ class JdSeckill(object):
             '_': str(int(time.time() * 1000)),
         }
         headers = {
-            'User-Agent': self.default_user_agent,
+            'User-Agent': self.user_agent,
             'Referer': 'https://order.jd.com/center/list.action',
         }
 
@@ -149,11 +408,19 @@ class JdSeckill(object):
                 resp = self.session.get(url=url, params=payload, headers=headers)
             else:
                 break
-            self.wait_some_time()
+            wait_some_time()
         # 响应中包含了许多用户信息,现在在其中返回昵称
         # jQuery2381773({"imgUrl":"//storage.360buyimg.com/i.imageUpload/xxx.jpg","lastLoginTime":"","nickName":"xxx","plusStatus":"0","realName":"xxx","userLevel":x,"userScoreVO":{"accountScore":xx,"activityScore":xx,"consumptionScore":xxxxx,"default":false,"financeScore":xxx,"pin":"xxx","riskScore":x,"totalScore":xxxxx}})
         return parse_json(resp.text).get('nickName')
 
+    def get_sku_title(self):
+        """获取商品名称"""
+        url = 'https://item.jd.com/{}.html'.format(global_config.getRaw('config', 'sku_id'))
+        resp = self.session.get(url).content
+        x_data = etree.HTML(resp)
+        sku_title = x_data.xpath('/html/head/title/text()')
+        return sku_title[0]
+
     def get_seckill_url(self):
         """获取商品的抢购链接
         点击"抢购"按钮后,会有两次302跳转,最后到达订单结算页面
@@ -168,7 +435,7 @@ class JdSeckill(object):
             '_': str(int(time.time() * 1000)),
         }
         headers = {
-            'User-Agent': self.default_user_agent,
+            'User-Agent': self.user_agent,
             'Host': 'itemko.jd.com',
             'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id),
         }
@@ -186,17 +453,17 @@ class JdSeckill(object):
                 return seckill_url
             else:
                 logger.info("抢购链接获取失败,稍后自动重试")
-                self.wait_some_time()
+                wait_some_time()
 
     def request_seckill_url(self):
         """访问商品的抢购链接(用于设置cookie等"""
         logger.info('用户:{}'.format(self.get_username()))
-        logger.info('商品名称:{}'.format(get_sku_title()))
+        logger.info('商品名称:{}'.format(self.get_sku_title()))
         self.timers.start()
         self.seckill_url[self.sku_id] = self.get_seckill_url()
         logger.info('访问商品的抢购连接...')
         headers = {
-            'User-Agent': self.default_user_agent,
+            'User-Agent': self.user_agent,
             'Host': 'marathon.jd.com',
             'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id),
         }
@@ -216,7 +483,7 @@ class JdSeckill(object):
             'rid': int(time.time())
         }
         headers = {
-            'User-Agent': self.default_user_agent,
+            'User-Agent': self.user_agent,
             'Host': 'marathon.jd.com',
             'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id),
         }
@@ -234,7 +501,7 @@ class JdSeckill(object):
             'isModifyAddress': 'false',
         }
         headers = {
-            'User-Agent': self.default_user_agent,
+            'User-Agent': self.user_agent,
             'Host': 'marathon.jd.com',
         }
         resp = self.session.post(url=url, data=data, headers=headers)
@@ -299,7 +566,7 @@ class JdSeckill(object):
         self.seckill_order_data[self.sku_id] = self._get_seckill_order_data()
         logger.info('提交抢购订单...')
         headers = {
-            'User-Agent': self.default_user_agent,
+            'User-Agent': self.user_agent,
             'Host': 'marathon.jd.com',
             'Referer': 'https://marathon.jd.com/seckill/seckill.action?skuId={0}&num={1}&rid={2}'.format(
                 self.sku_id, self.seckill_num, int(time.time())),

+ 35 - 34
util.py

@@ -1,8 +1,10 @@
 import json
 import random
 import requests
+import os
+import time
+
 from config import global_config
-from lxml import etree
 
 USER_AGENTS = [
     "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
@@ -57,50 +59,23 @@ USER_AGENTS = [
     "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.14 (KHTML, like Gecko) Chrome/24.0.1292.0 Safari/537.14"
     ]
 
+
 def parse_json(s):
     begin = s.find('{')
     end = s.rfind('}') + 1
     return json.loads(s[begin:end])
 
+
 def get_random_useragent():
     """生成随机的UserAgent
     :return: UserAgent字符串
     """
     return random.choice(USER_AGENTS)
 
-def get_cookies():
-    """解析cookies内容并添加到cookiesJar"""
-    manual_cookies = {}
-    for item in global_config.getRaw('config','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)
-    return cookiesJar
-
-def get_session():
-    # 初始化session
-    session = requests.session()
-    session.headers = {"User-Agent": global_config.getRaw('config', 'DEFAULT_USER_AGENT'),
-                       "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": global_config.getRaw('config', 'DEFAULT_USER_AGENT'),
-                            "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"}
-    # 获取cookies保存到session
-    session.cookies = get_cookies()
-    return session
-
-def get_sku_title():
-    """获取商品名称"""
-    url = 'https://item.jd.com/{}.html'.format(global_config.getRaw('config','sku_id'))
-    session = get_session()
-    resp = session.get(url).content
-    x_data = etree.HTML(resp)
-    sku_title = x_data.xpath('/html/head/title/text()')
-    return sku_title[0]
+
+def wait_some_time():
+    time.sleep(random.randint(100, 300) / 1000)
+
 
 def send_wechat(message):
     """推送信息到微信"""
@@ -113,3 +88,29 @@ def send_wechat(message):
         'User-Agent':global_config.getRaw('config', 'DEFAULT_USER_AGENT')
     }
     requests.get(url, params=payload, headers=headers)
+
+
+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 open_image(image_file):
+    if os.name == "nt":
+        os.system('start ' + image_file)  # for Windows
+    else:
+        if os.uname()[0] == "Linux":
+            if "deepin" in os.uname()[2]:
+                os.system("deepin-image-viewer " + image_file)  # for deepin
+            else:
+                os.system("eog " + image_file)  # for Linux
+        else:
+            os.system("open " + image_file)  # for Mac
+
+
+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)