├── .gitignore ├── Config ├── __init__.py ├── config.ini └── settings.py ├── Core ├── __init__.py ├── core.py ├── exception.py ├── login.py ├── spider.py ├── timer.py └── util.py ├── Docs ├── change-log.md ├── img │ ├── banner.jpg │ ├── shopper.png │ └── shopper.svg └── notice.md ├── GUI ├── gui.py ├── pack_requirements.txt └── packing.py ├── LICENSE ├── Logger ├── __init__.py └── logger.py ├── Message ├── __init__.py └── message.py ├── README.md ├── Scheduler ├── __init__.py └── scheduler.py ├── Server ├── __init__.py ├── api.py ├── app.py ├── handler.py ├── server.py └── url.py ├── Static ├── 404.html ├── css │ ├── element_index.css │ ├── fonts │ │ └── element-icons.woff │ └── index.css ├── favicon.ico ├── img │ └── __init__.py ├── index.html └── js │ ├── element_index.js │ ├── index.js │ └── vue.js ├── TEST ├── Seckill.py ├── area_id │ ├── 1.北京.txt │ ├── 10.黑龙江.txt │ ├── 11.内蒙古.txt │ ├── 12.江苏.txt │ ├── 13.山东.txt │ ├── 14.安徽.txt │ ├── 15.浙江.txt │ ├── 16.福建.txt │ ├── 17.湖北.txt │ ├── 18.湖南.txt │ ├── 19.广东.txt │ ├── 2.上海.txt │ ├── 20.广西.txt │ ├── 21.江西.txt │ ├── 22.四川.txt │ ├── 23.海南.txt │ ├── 24.贵州.txt │ ├── 25.云南.txt │ ├── 26.西藏.txt │ ├── 27.陕西.txt │ ├── 28.甘肃.txt │ ├── 29.青海.txt │ ├── 3.天津.txt │ ├── 30.宁夏.txt │ ├── 31.新疆.txt │ ├── 32.台湾.txt │ ├── 4.重庆.txt │ ├── 5.河北.txt │ ├── 52993.港澳.txt │ ├── 53283.海外.txt │ ├── 6.山西.txt │ ├── 7.河南.txt │ ├── 8.辽宁.txt │ ├── 84.钓鱼岛.txt │ ├── 9.吉林.txt │ ├── README.md │ └── get_area_id.py ├── get_eid_fp.html ├── pack.py └── py2app_setup.py ├── main.py ├── requirements.txt └── runserver.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # IPython 78 | profile_default/ 79 | ipython_config.py 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | .dmypy.json 112 | dmypy.json 113 | 114 | # Pyre type checker 115 | .pyre/ 116 | 117 | # PyCharm 118 | .idea/ 119 | -------------------------------------------------------------------------------- /Config/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 设置模块 3 | ''' -------------------------------------------------------------------------------- /Config/config.ini: -------------------------------------------------------------------------------- 1 | [Spider] 2 | # eid, fp参数必须填写,具体请参考Wiki-常见问题 https://github.com/tychxn/jd-assistant/wiki/1.-%E4%BA%AC%E4%B8%9C%E6%8A%A2%E8%B4%AD%E5%8A%A9%E6%89%8B%E7%94%A8%E6%B3%95 3 | eid = "2PBCMB2WINZUQI6XRDCSKOZXTCHEOHLDABNAVSAEYTS6DBH2DWOACQPUEKXGHV7ZYTKCRDXZX34SPL2XI3KQYVNVSM" 4 | fp = "63a2c6fceb97a082753cdf00710f4f46" 5 | # area到area_id目录下人工查找,例如:江西省南昌市昌北区蛟桥镇的area是21_1827_4101_40925 6 | area = "21_1827_4101_40925" 7 | # 商品id 8 | sku_id = "10027576824361" 9 | # 购买数量 10 | amount = 1 11 | # 设定时间 # 2020-12-09 10:00:00.100000(修改成每天的几点几分几秒几毫秒) 12 | buy_time = "2021-11-13 04:30:00.000" 13 | # 提交订单失败重试次数 14 | retry = 100 15 | # 查询库存请求超时(秒),可选配置,默认30秒 16 | timeout = 30 17 | # 最大查询随机数,给查询请求加的随机间隔时间,默认5s 18 | random_time = 5 19 | # 默认UA 20 | 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" 21 | # 是否使用随机 useragent,默认为 False 22 | random_useragent = False 23 | # 多进程数量 24 | work_count = 1 25 | 26 | [Account] 27 | # 支付密码 28 | # 如果你的账户中有可用的京券(注意不是东券)或 在上次购买订单中使用了京豆, 29 | # 那么京东可能会在下单时自动选择京券支付 或 自动勾选京豆支付。 30 | # 此时下单会要求输入六位数字的支付密码。请在下方配置你的支付密码,如 123456 。 31 | # 如果没有上述情况,下方请留空。 32 | payment_pwd = "" 33 | 34 | [Message] 35 | # 使用了Server酱的推送服务 36 | # 如果想开启下单成功后消息推送,则将 enable 设置为 true,默认为 false 不开启推送 37 | # 开启消息推送必须填入 sckey,如何获取请参考 http://sc.ftqq.com/3.version。感谢Server酱~ 38 | enable = False 39 | sckey = "?????" 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | # ============================= 61 | # ============================= 62 | # ====== 以下框架配置请勿动 ====== 63 | # ============================= 64 | # ============================= 65 | 66 | 67 | [Information] 68 | # 本项目的信息 69 | # PROJECT-项目名,VERSION-版本,AUTHOR-作者,TIME-创作时间 70 | PROJECT = "jd-shopper" 71 | VERSION = "1.0.0-Alpha" 72 | AUTHOR = "Louis·Young" 73 | TIME = "2021-11-13" 74 | 75 | [Debug] 76 | # DEBUG 设置 77 | # DEBUG-开启DEBUG后无视Scheduler只会执行Core.core.main函数中代码 78 | DEBUG = False 79 | 80 | [Server] 81 | # 服务器信息 82 | # SERVER_NAME-服务器名,SERVER_VERSION-服务器版本 83 | # START_USING-是否开启服务器 84 | # SERVER_HOST-填写服务器域名,LOCAL_HOST-用于启动项目的局域网IP,PORT-端口 85 | # PROCESS_MODEL-是否开启多进程,PROCESS_COUNT-进程数量 86 | SERVER_NAME = "TinyServer" 87 | SERVER_VERSION = "0.4.8" 88 | START_USING = True 89 | SERVER_HOST = "localhost" 90 | LOCAL_HOST = "0.0.0.0" 91 | PORT = 12021 92 | PROCESS_MODEL = False 93 | PROCESS_COUNT = 4 94 | 95 | [GUI] 96 | # GUI Settings 97 | # 进行GUI打包时需要确保安装pywebview, 并手动取消GUI/gui.py下的注释 98 | START_USING = False 99 | 100 | [Logger] 101 | # 记录设置 102 | # FILE_NAME-记录文件名,AMOUNT-记录文件个数,MAX_BYTES-单个记录文件的大小 103 | # CLEAR_UP每次启动程序是否清理log文件中内容,SERVER_LOGGER-开启HTTP Server的log记录 104 | FILE_NAME = "TinyServerManager.log" 105 | FILE_PATH = "/Logger/Log_Files/" 106 | AMOUNT = 1 107 | MAX_BYTES = 10485760 108 | CLEAR_UP = True 109 | SERVER_LOGGER = False 110 | 111 | [Scheduler] 112 | # 调度器设置(用于定时执行) 113 | # START_USING-是否开启定时执行,调度器开启后main函数将被scheduler调度器代理,开启定时执行main 114 | # START_TIME-任务开启时间,SKIP_WEEKEND-周末不执行任务 115 | START_USING = False 116 | START_TIME = "18:00:00" 117 | SKIP_WEEKEND = True 118 | -------------------------------------------------------------------------------- /Config/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import configparser 3 | 4 | class Config(object): 5 | def __init__(self, config_file='config.ini'): 6 | self.rootPath = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 7 | self._path = self.rootPath + "/Config/" + config_file 8 | if not os.path.exists(self._path): 9 | raise FileNotFoundError("No such file: config.ini") 10 | self._config = configparser.ConfigParser() 11 | self._config.read(self._path, encoding='utf-8-sig') 12 | self._configRaw = configparser.RawConfigParser() 13 | self._configRaw.read(self._path, encoding='utf-8-sig') 14 | 15 | def settings(self, section, name): 16 | return eval(self._config.get(section, name)) 17 | 18 | def raw(self, section, name): 19 | return self._configRaw.get(section, name) 20 | 21 | def path(self): 22 | return self.rootPath 23 | 24 | 25 | config = Config() 26 | -------------------------------------------------------------------------------- /Core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aa342138039/JD-SHOPPER/7baca21df4e2e1db53067a5813d8b31ba586df34/Core/__init__.py -------------------------------------------------------------------------------- /Core/core.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | pass 3 | -------------------------------------------------------------------------------- /Core/exception.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding=utf8 -*- 3 | 4 | 5 | class SKException(Exception): 6 | 7 | def __init__(self, message): 8 | super().__init__(message) 9 | -------------------------------------------------------------------------------- /Core/login.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | import requests 4 | import functools 5 | import json 6 | import os 7 | import pickle 8 | from Logger.logger import logger 9 | from Config.settings import config 10 | from Core.exception import SKException 11 | from Core.util import ( 12 | parse_json, 13 | wait_some_time, 14 | response_status, 15 | save_image, 16 | open_image 17 | ) 18 | class SpiderSession: 19 | """ 20 | Session相关操作 21 | """ 22 | def __init__(self): 23 | self.cookies_dir_path = "./cookies/" 24 | self.user_agent = config.settings('Spider', 'DEFAULT_USER_AGENT') 25 | 26 | self.session = self._init_session() 27 | 28 | def _init_session(self): 29 | session = requests.session() 30 | session.headers = self.get_headers() 31 | return session 32 | 33 | def get_headers(self): 34 | return {"User-Agent": self.user_agent, 35 | "Accept": "text/html,application/xhtml+xml,application/xml;" 36 | "q=0.9,image/webp,image/apng,*/*;" 37 | "q=0.8,application/signed-exchange;" 38 | "v=b3", 39 | "Connection": "keep-alive"} 40 | 41 | def get_user_agent(self): 42 | return self.user_agent 43 | 44 | def get_session(self): 45 | """ 46 | 获取当前Session 47 | :return: 48 | """ 49 | return self.session 50 | 51 | def get_cookies(self): 52 | """ 53 | 获取当前Cookies 54 | :return: 55 | """ 56 | return self.get_session().cookies 57 | 58 | def set_cookies(self, cookies): 59 | self.session.cookies.update(cookies) 60 | 61 | def load_cookies_from_local(self): 62 | """ 63 | 从本地加载Cookie 64 | :return: 65 | """ 66 | cookies_file = '' 67 | if not os.path.exists(self.cookies_dir_path): 68 | return False 69 | for name in os.listdir(self.cookies_dir_path): 70 | if name.endswith(".cookies"): 71 | cookies_file = '{}{}'.format(self.cookies_dir_path, name) 72 | break 73 | if cookies_file == '': 74 | return False 75 | with open(cookies_file, 'rb') as f: 76 | local_cookies = pickle.load(f) 77 | self.set_cookies(local_cookies) 78 | 79 | def save_cookies_to_local(self, cookie_file_name): 80 | """ 81 | 保存Cookie到本地 82 | :param cookie_file_name: 存放Cookie的文件名称 83 | :return: 84 | """ 85 | cookies_file = '{}{}.cookies'.format(self.cookies_dir_path, cookie_file_name) 86 | directory = os.path.dirname(cookies_file) 87 | if not os.path.exists(directory): 88 | os.makedirs(directory) 89 | with open(cookies_file, 'wb') as f: 90 | pickle.dump(self.get_cookies(), f) 91 | 92 | 93 | class QrLogin: 94 | """ 95 | 扫码登录 96 | """ 97 | def __init__(self, spider_session: SpiderSession): 98 | """ 99 | 初始化扫码登录 100 | 大致流程: 101 | 1、访问登录二维码页面,获取Token 102 | 2、使用Token获取票据 103 | 3、校验票据 104 | :param spider_session: 105 | """ 106 | self.qrcode_img_file = 'qr_code.png' 107 | 108 | self.spider_session = spider_session 109 | self.session = self.spider_session.get_session() 110 | 111 | self.is_login = False 112 | self.refresh_login_status() 113 | 114 | def refresh_login_status(self): 115 | """ 116 | 刷新是否登录状态 117 | :return: 118 | """ 119 | self.is_login = self._validate_cookies() 120 | 121 | def _validate_cookies(self): 122 | """ 123 | 验证cookies是否有效(是否登陆) 124 | 通过访问用户订单列表页进行判断:若未登录,将会重定向到登陆页面。 125 | :return: cookies是否有效 True/False 126 | """ 127 | url = 'https://order.jd.com/center/list.action' 128 | payload = { 129 | 'rid': str(int(time.time() * 1000)), 130 | } 131 | try: 132 | resp = self.session.get(url=url, params=payload, allow_redirects=False) 133 | if resp.status_code == requests.codes.OK: 134 | return True 135 | except Exception as e: 136 | logger.error("验证cookies是否有效发生异常", e) 137 | return False 138 | 139 | def _get_login_page(self): 140 | """ 141 | 获取PC端登录页面 142 | :return: 143 | """ 144 | url = "https://passport.jd.com/new/login.aspx" 145 | page = self.session.get(url, headers=self.spider_session.get_headers()) 146 | return page 147 | 148 | def _get_qrcode(self): 149 | """ 150 | 缓存并展示登录二维码 151 | :return: 152 | """ 153 | url = 'https://qr.m.jd.com/show' 154 | payload = { 155 | 'appid': 133, 156 | 'size': 147, 157 | 't': str(int(time.time() * 1000)), 158 | } 159 | headers = { 160 | 'User-Agent': self.spider_session.get_user_agent(), 161 | 'Referer': 'https://passport.jd.com/new/login.aspx', 162 | } 163 | resp = self.session.get(url=url, headers=headers, params=payload) 164 | 165 | if not response_status(resp): 166 | logger.info('获取二维码失败') 167 | return False 168 | 169 | save_image(resp, self.qrcode_img_file) 170 | logger.info('二维码获取成功,请打开京东APP扫描') 171 | open_image(self.qrcode_img_file) 172 | return True 173 | 174 | def _get_qrcode_ticket(self): 175 | """ 176 | 通过 token 获取票据 177 | :return: 178 | """ 179 | url = 'https://qr.m.jd.com/check' 180 | payload = { 181 | 'appid': '133', 182 | 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)), 183 | 'token': self.session.cookies.get('wlfstk_smdl'), 184 | '_': str(int(time.time() * 1000)), 185 | } 186 | headers = { 187 | 'User-Agent': self.spider_session.get_user_agent(), 188 | 'Referer': 'https://passport.jd.com/new/login.aspx', 189 | } 190 | resp = self.session.get(url=url, headers=headers, params=payload) 191 | 192 | if not response_status(resp): 193 | logger.error('获取二维码扫描结果异常') 194 | return False 195 | 196 | resp_json = parse_json(resp.text) 197 | if resp_json['code'] != 200: 198 | logger.info('Code: %s, Message: %s', resp_json['code'], resp_json['msg']) 199 | return None 200 | else: 201 | logger.info('已完成手机客户端确认') 202 | return resp_json['ticket'] 203 | 204 | def _validate_qrcode_ticket(self, ticket): 205 | """ 206 | 通过已获取的票据进行校验 207 | :param ticket: 已获取的票据 208 | :return: 209 | """ 210 | url = 'https://passport.jd.com/uc/qrCodeTicketValidation' 211 | headers = { 212 | 'User-Agent': self.spider_session.get_user_agent(), 213 | 'Referer': 'https://passport.jd.com/uc/login?ltype=logout', 214 | } 215 | 216 | resp = self.session.get(url=url, headers=headers, params={'t': ticket}) 217 | if not response_status(resp): 218 | return False 219 | 220 | resp_json = json.loads(resp.text) 221 | if resp_json['returnCode'] == 0: 222 | return True 223 | else: 224 | logger.info(resp_json) 225 | return False 226 | 227 | def login_by_qrcode(self): 228 | """ 229 | 二维码登陆 230 | :return: 231 | """ 232 | self._get_login_page() 233 | 234 | # download QR code 235 | if not self._get_qrcode(): 236 | raise SKException('二维码下载失败') 237 | 238 | # get QR code ticket 239 | ticket = None 240 | retry_times = 85 241 | for _ in range(retry_times): 242 | ticket = self._get_qrcode_ticket() 243 | if ticket: 244 | break 245 | time.sleep(2) 246 | else: 247 | raise SKException('二维码过期,请重新获取扫描') 248 | 249 | # validate QR code ticket 250 | if not self._validate_qrcode_ticket(ticket): 251 | raise SKException('二维码信息校验失败') 252 | 253 | self.refresh_login_status() 254 | 255 | logger.info('二维码登录成功') -------------------------------------------------------------------------------- /Core/spider.py: -------------------------------------------------------------------------------- 1 | import math 2 | import functools 3 | from lxml import html 4 | import requests 5 | import time 6 | import json 7 | import random 8 | import sys 9 | from concurrent.futures import ProcessPoolExecutor 10 | from Core.exception import SKException 11 | from bs4 import BeautifulSoup 12 | from Config.settings import config 13 | from Logger.logger import logger 14 | from Core.login import SpiderSession, QrLogin 15 | from Message.message import sendMessage 16 | from Core.timer import Timer 17 | from Core.util import parse_json 18 | 19 | 20 | class Waiter(): 21 | def __init__(self, 22 | skuids=config.settings("Spider", "sku_id"), 23 | area=config.settings("Spider", "area"), 24 | eid=config.settings("Spider", "eid"), 25 | fp=config.settings("Spider", "fp"), 26 | count=config.settings("Spider", "amount"), 27 | payment_pwd=config.settings( 28 | "Account", "payment_pwd"), 29 | retry=config.settings("Spider", "retry"), 30 | work_count=config.settings( 31 | 'Spider', 'work_count'), 32 | random_time=config.settings( 33 | 'Spider', 'random_time'), 34 | timeout=float(config.raw( 35 | "Spider", "timeout")), 36 | date=config.settings('Spider', 'buy_time').__str__() 37 | ): 38 | 39 | self.skuids = skuids 40 | self.area = area 41 | self.eid = eid 42 | self.fp = fp 43 | self.count = count 44 | self.payment_pwd = payment_pwd 45 | self.retry = retry 46 | self.work_count = work_count 47 | self.random_time = random_time 48 | self.timeout = timeout 49 | self.buyTime = date 50 | 51 | self.spider_session = SpiderSession() 52 | self.spider_session.load_cookies_from_local() 53 | self.session = self.spider_session.get_session() 54 | self.qrlogin = QrLogin(self.spider_session) 55 | self.user_agent = self.spider_session.user_agent 56 | self.item_cat = {} 57 | self.item_vender_ids = {} # 记录商家id 58 | self.headers = {'User-Agent': self.user_agent} 59 | self.timers = Timer(self.buyTime) 60 | 61 | self.sku_title = None 62 | self.init_sku_title() 63 | 64 | def login_by_qrcode(self): 65 | """ 66 | 二维码登陆 67 | :return: 68 | """ 69 | if self.qrlogin.is_login: 70 | logger.info('登录成功') 71 | return 72 | 73 | self.qrlogin.login_by_qrcode() 74 | 75 | if self.qrlogin.is_login: 76 | self.nick_name = self.getUsername() 77 | self.spider_session.save_cookies_to_local(self.nick_name) 78 | else: 79 | raise SKException("二维码登录失败!") 80 | 81 | def check_login(func): 82 | """ 83 | 用户登陆态校验装饰器。若用户未登陆,则调用扫码登陆 84 | """ 85 | 86 | @functools.wraps(func) 87 | def new_func(self, *args, **kwargs): 88 | if not self.qrlogin.is_login: 89 | logger.info("{0} 需登陆后调用,开始扫码登陆".format(func.__name__)) 90 | self.login_by_qrcode() 91 | return func(self, *args, **kwargs) 92 | 93 | return new_func 94 | 95 | def init_sku_title(self,max_try=5): 96 | for i in range(max_try): 97 | if self._get_sku_title(): 98 | return True 99 | else: 100 | time.sleep(1) 101 | logger.error(f'尝试获取sku名称失败,将名称设置为sku_id: {self.skuids}') 102 | self.sku_title = self.skuids 103 | return False 104 | 105 | def _get_sku_title(self): 106 | """获取商品名称""" 107 | url = 'https://item.jd.com/{}.html'.format(self.skuids) 108 | try: 109 | resp = self.session.get(url) 110 | if 'https://www.jd.com/?d' == resp.url: 111 | logger.critical(f"skuid({self.skuids}) 无效, 请检查Config\config.ini中配置是否正确") 112 | sys.exit(1) 113 | data = resp.content 114 | x_data = html.etree.HTML(data) 115 | sku_title = x_data.xpath('/html/head/title/text()') 116 | except requests.exceptions.Timeout: 117 | logger.error('查询 %s 名称信息超时(%ss)', self.skuids, self.timeout) 118 | return False 119 | except requests.exceptions.RequestException as request_exception: 120 | logger.error('查询 %s 名称信息发生网络请求异常:\n%s', self.skuids, request_exception) 121 | return False 122 | try: 123 | result = sku_title[0] 124 | self.sku_title = result 125 | return True 126 | except Exception as e: 127 | logger.error('解析 %s 名称信息发生异常:\nresp: %s\nexception: %s', 128 | self.skuids, data, e) 129 | return False 130 | 131 | @check_login 132 | def waitForSell(self): 133 | self._waitForSell() 134 | 135 | @check_login 136 | def waitTimeForSell(self): 137 | self._waitTimeForSell() 138 | 139 | def get_tag_value(self, tag, key='', index=0): 140 | if key: 141 | value = tag[index].get(key) 142 | else: 143 | value = tag[index].text 144 | return value.strip(' \t\r\n') 145 | 146 | def response_status(self, resp): 147 | if resp.status_code != requests.codes.OK: 148 | print('Status: %u, Url: %s' % (resp.status_code, resp.url)) 149 | return False 150 | return True 151 | 152 | def getUsername(self): 153 | userName_Url = 'https://passport.jd.com/new/helloService.ashx?callback=jQuery339448&_=' + str( 154 | int(time.time() * 1000)) 155 | self.session.headers = { 156 | "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", 157 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 158 | "Referer": "https://order.jd.com/center/list.action", 159 | "Connection": "keep-alive" 160 | } 161 | resp = self.session.get(url=userName_Url, allow_redirects=True) 162 | resultText = resp.text 163 | resultText = resultText.replace('jQuery339448(', '') 164 | resultText = resultText.replace(')', '') 165 | usernameJson = json.loads(resultText) 166 | logger.info('登录账号名称' + usernameJson['nick']) 167 | return usernameJson['nick'] 168 | 169 | 170 | 171 | @check_login 172 | def waitAndBuy_by_proc_pool(self): 173 | """ 174 | 多进程进行抢购 175 | work_count:进程数量 176 | """ 177 | work_count = self.work_count 178 | with ProcessPoolExecutor(work_count) as pool: 179 | for i in range(work_count): 180 | pool.submit(self.buy) 181 | 182 | def check_item_stock(self): 183 | """ 184 | 检查是否有货 185 | """ 186 | stockurl = 'http://c0.3.cn/stock?skuId=' + self.skuids + \ 187 | '&cat=652,829,854&area=' + self.area + \ 188 | '&extraParam={%22originid%22:%221%22}' 189 | response = self.session.get(stockurl) 190 | resp = self.session.get(stockurl) 191 | jsparser = json.loads(resp.text) 192 | # 33 有货 34 无货 193 | if jsparser['StockState'] == 33 and jsparser['StockStateName'] == '现货': 194 | print('库存状态:', jsparser['StockStateName']) 195 | return True 196 | else: 197 | print('库存状态:{}(无现货)'.format(jsparser['StockStateName'])) 198 | return False 199 | 200 | def _get_item_detail_page(self, sku_id): 201 | """访问商品详情页 202 | :param sku_id: 商品id 203 | :return: 响应 204 | """ 205 | url = 'https://item.jd.com/{}.html'.format(sku_id) 206 | page = requests.get(url=url, headers=self.headers) 207 | return page 208 | 209 | def _waitForSell(self): 210 | area_id = self.area 211 | sku_id = self.skuids 212 | logger.info("正在等待商品上架:{}".format( 213 | self.sku_title[:80] + " ......")) 214 | while True: 215 | if self.get_single_item_stock(sku_id, area_id): 216 | logger.info("商品上架: {}".format( 217 | self.sku_title[:80] + " ......")) 218 | # self.waitAndBuy_by_proc_pool() 219 | self.buy() 220 | return True 221 | else: 222 | sleep_time = self.timeout + random.randint(1, self.random_time) 223 | logger.info("查询间隔:{}秒 等待商品上架: {}".format(sleep_time, 224 | self.sku_title[:80] + " ......")) 225 | time.sleep(sleep_time) 226 | 227 | def _waitTimeForSell(self): 228 | self.initCart() 229 | logger.info("正在等待商品上架:{}".format( 230 | self.sku_title[:80] + " ......")) 231 | self.timers.start() 232 | self.fastBuy() 233 | 234 | def get_single_item_stock(self, sku_id, area_id): 235 | """获取单个商品库存状态 236 | :param sku_id: 商品id 237 | :param num: 商品数量 238 | :param area: 地区id 239 | :return: 商品是否有货 True/False 240 | """ 241 | url = 'https://cd.jd.com/stocks' 242 | # https://cd.jd.com/stocks?callback=jQuery3528455&type=getstocks&skuIds=100011513445&area=21_1827_4101_40925&_=1625970219360 243 | payload = { 244 | 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)), 245 | 'type': 'getstocks', 246 | 'skuIds': sku_id, 247 | 'area': area_id, 248 | '_': str(int(time.time() * 1000)), 249 | } 250 | headers = { 251 | 'User-Agent': self.user_agent, 252 | 'Referer': 'https://item.jd.com/{}.html'.format(sku_id), 253 | } 254 | 255 | resp_text = '' 256 | try: 257 | resp_text = requests.get( 258 | url=url, params=payload, headers=headers, timeout=self.timeout).text 259 | resp_json = parse_json(resp_text) 260 | stock_info = resp_json.get(sku_id) 261 | sku_state = stock_info.get('skuState') # 商品是否上架 262 | # 商品库存状态:33 -- 现货 0,34 -- 无货 36 -- 采购中 40 -- 可配货 263 | stock_state = stock_info.get('StockState') 264 | if sku_state == 1 and stock_state in (33, 40): 265 | logger.info("有货: " + stock_info.get('StockStateName')) 266 | return True 267 | # else: 268 | # logger.info(stock_info.get('StockStateName')) 269 | # return False 270 | except requests.exceptions.Timeout: 271 | logger.error('查询 %s 库存信息超时(%ss)', sku_id, self.timeout) 272 | return False 273 | except requests.exceptions.RequestException as request_exception: 274 | logger.error('查询 %s 库存信息发生网络请求异常:\n%s', sku_id, request_exception) 275 | return False 276 | except Exception as e: 277 | logger.error('查询 %s 库存信息发生异常:\nresp: %s\nexception: %s', 278 | sku_id, resp_text, e) 279 | return False 280 | 281 | def cancel_select_all_cart_item(self): 282 | ''' 283 | 取消勾选购物车中的所有商品 284 | ''' 285 | url = "https://cart.jd.com/cancelAllItem.action" 286 | data = { 287 | 't': 0, 288 | 'outSkus': '', 289 | 'random': random.random() 290 | } 291 | resp = self.session.post(url, data=data) 292 | if resp.status_code != requests.codes.OK: 293 | print('Status: %u, Url: %s' % (resp.status_code, resp.url)) 294 | return False 295 | return True 296 | 297 | ''' 298 | 勾选购物车中的所有商品 299 | ''' 300 | 301 | def select_all_cart_item(self): 302 | url = "https://cart.jd.com/selectAllItem.action" 303 | data = { 304 | 't': 0, 305 | 'outSkus': '', 306 | 'random': random.random() 307 | } 308 | resp = self.session.post(url, data=data) 309 | if resp.status_code != requests.codes.OK: 310 | print('Status: %u, Url: %s' % (resp.status_code, resp.url)) 311 | return False 312 | return True 313 | 314 | ''' 315 | 删除购物车选中商品 316 | ''' 317 | 318 | def remove_item(self): 319 | url = "https://cart.jd.com/batchRemoveSkusFromCart.action" 320 | data = { 321 | 't': 0, 322 | 'null': '', 323 | 'outSkus': '', 324 | 'random': random.random(), 325 | 'locationId': '19-1607-4773-0' 326 | } 327 | headers = { 328 | "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", 329 | "Accept": "application/json, text/javascript, */*; q=0.01", 330 | "Referer": "https://cart.jd.com/cart.action", 331 | "Host": "cart.jd.com", 332 | "Content-Type": "application/x-www-form-urlencoded", 333 | "Accept-Encoding": "gzip, deflate, br", 334 | "Accept-Encoding": "zh-CN,zh;q=0.9,ja;q=0.8", 335 | "Origin": "https://cart.jd.com", 336 | "Connection": "keep-alive" 337 | } 338 | resp = self.session.post(url, data=data, headers=headers) 339 | logger.info('清空购物车') 340 | if resp.status_code != requests.codes.OK: 341 | print('Status: %u, Url: %s' % (resp.status_code, resp.url)) 342 | return False 343 | return True 344 | 345 | ''' 346 | 购物车详情 347 | ''' 348 | 349 | def cart_detail(self): 350 | url = 'https://cart.jd.com/cart.action' 351 | headers = { 352 | "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", 353 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 354 | "Referer": "https://order.jd.com/center/list.action", 355 | "Host": "cart.jd.com", 356 | "Connection": "keep-alive" 357 | } 358 | resp = self.session.get(url, headers=headers) 359 | soup = BeautifulSoup(resp.text, "html.parser") 360 | 361 | cart_detail = dict() 362 | for item in soup.find_all(class_='item-item'): 363 | try: 364 | sku_id = item['skuid'] # 商品id 365 | except Exception as e: 366 | logger.info('购物车中有套装商品,跳过') 367 | continue 368 | try: 369 | # 例如:['increment', '8888', '100001071956', '1', '13', '0', '50067652554'] 370 | # ['increment', '8888', '100002404322', '2', '1', '0'] 371 | item_attr_list = item.find(class_='increment')['id'].split('_') 372 | p_type = item_attr_list[4] 373 | promo_id = target_id = item_attr_list[-1] if len( 374 | item_attr_list) == 7 else 0 375 | 376 | cart_detail[sku_id] = { 377 | # 商品名称 378 | 'name': self.get_tag_value(item.select('div.p-name a')), 379 | 'verder_id': item['venderid'], # 商家id 380 | 'count': int(item['num']), # 数量 381 | # 单价 382 | 'unit_price': self.get_tag_value(item.select('div.p-price strong'))[1:], 383 | # 总价 384 | 'total_price': self.get_tag_value(item.select('div.p-sum strong'))[1:], 385 | 'is_selected': 'item-selected' in item['class'], # 商品是否被勾选 386 | 'p_type': p_type, 387 | 'target_id': target_id, 388 | 'promo_id': promo_id 389 | } 390 | except Exception as e: 391 | logger.error("商品%s在购物车中的信息无法解析,报错信息: %s,该商品自动忽略", sku_id, e) 392 | 393 | logger.info('购物车信息:%s', cart_detail) 394 | return cart_detail 395 | 396 | ''' 397 | 修改购物车商品的数量 398 | ''' 399 | 400 | def change_item_num_in_cart(self, sku_id, vender_id, num, p_type, target_id, promo_id): 401 | url = "https://cart.jd.com/changeNum.action" 402 | data = { 403 | 't': 0, 404 | 'venderId': vender_id, 405 | 'pid': sku_id, 406 | 'pcount': num, 407 | 'ptype': p_type, 408 | 'targetId': target_id, 409 | 'promoID': promo_id, 410 | 'outSkus': '', 411 | 'random': random.random(), 412 | # 'locationId' 413 | } 414 | self.session.headers = { 415 | "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", 416 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 417 | "Referer": "https://cart.jd.com/cart", 418 | "Connection": "keep-alive" 419 | } 420 | resp = self.session.post(url, data=data) 421 | return json.loads(resp.text)['sortedWebCartResult']['achieveSevenState'] == 2 422 | 423 | ''' 424 | 添加商品到购物车 425 | ''' 426 | 427 | def add_item_to_cart(self, sku_id): 428 | url = 'https://cart.jd.com/gate.action' 429 | payload = { 430 | 'pid': sku_id, 431 | 'pcount': self.count, 432 | 'ptype': 1, 433 | } 434 | resp = self.session.get(url=url, params=payload) 435 | if 'https://cart.jd.com/cart.action' in resp.url: # 套装商品加入购物车后直接跳转到购物车页面 436 | result = True 437 | else: # 普通商品成功加入购物车后会跳转到提示 "商品已成功加入购物车!" 页面 438 | soup = BeautifulSoup(resp.text, "html.parser") 439 | # [

商品已成功加入购物车!

] 440 | result = bool(soup.select('h3.ftx-02')) 441 | 442 | if result: 443 | logger.info('%s 已成功加入购物车', sku_id) 444 | else: 445 | logger.error('%s 添加到购物车失败', sku_id) 446 | 447 | def get_checkout_page_detail(self): 448 | """获取订单结算页面信息 449 | 450 | 该方法会返回订单结算页面的详细信息:商品名称、价格、数量、库存状态等。 451 | 452 | :return: 结算信息 dict 453 | """ 454 | url = 'http://trade.jd.com/shopping/order/getOrderInfo.action' 455 | # url = 'https://cart.jd.com/gotoOrder.action' 456 | payload = { 457 | 'rid': str(int(time.time() * 1000)), 458 | } 459 | headers = { 460 | "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", 461 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 462 | "Referer": "https://cart.jd.com/cart.action", 463 | "Connection": "keep-alive", 464 | 'Host': 'trade.jd.com', 465 | } 466 | try: 467 | # print(url) 468 | resp = self.session.get(url=url, params=payload, headers=headers) 469 | if "刷新太频繁了" in resp.text: 470 | logger.error("刷新太频繁了 url: {}".format(url)) 471 | return '' 472 | 473 | if not self.response_status(resp): 474 | logger.error('获取订单结算页信息失败') 475 | return '' 476 | 477 | soup = BeautifulSoup(resp.text, "html.parser") 478 | # print(soup.title) 479 | risk_control = self.get_tag_value( 480 | soup.select('input#riskControl'), 'value') 481 | 482 | order_detail = { 483 | # remove '寄送至: ' from the begin 484 | 'address': soup.find('span', id='sendAddr').text[5:], 485 | # remove '收件人:' from the begin 486 | 'receiver': soup.find('span', id='sendMobile').text[4:], 487 | # remove '¥' from the begin 488 | 'total_price': soup.find('span', id='sumPayPriceId').text[1:], 489 | 'items': [] 490 | } 491 | 492 | logger.info("下单信息:%s", order_detail) 493 | return order_detail 494 | except Exception as e: 495 | logger.error('该商品频繁刷新被京东风控!!!(仅限该商品,请勿重复多次下单,易被风控)') 496 | return '' 497 | 498 | def submit_order(self, risk_control): 499 | """提交订单 500 | 501 | 重要: 502 | 1.该方法只适用于普通商品的提交订单(即可以加入购物车,然后结算提交订单的商品) 503 | 2.提交订单时,会对购物车中勾选✓的商品进行结算(如果勾选了多个商品,将会提交成一个订单) 504 | 505 | :return: True/False 订单提交结果 506 | """ 507 | url = 'https://trade.jd.com/shopping/order/submitOrder.action' 508 | # js function of submit order is included in https://trade.jd.com/shopping/misc/js/order.js?r=2018070403091 509 | 510 | # overseaPurchaseCookies: 511 | # vendorRemarks: [] 512 | # submitOrderParam.sopNotPutInvoice: false 513 | # submitOrderParam.trackID: TestTrackId 514 | # submitOrderParam.ignorePriceChange: 0 515 | # submitOrderParam.btSupport: 0 516 | # riskControl: 517 | # submitOrderParam.isBestCoupon: 1 518 | # submitOrderParam.jxj: 1 519 | # submitOrderParam.trackId: 520 | 521 | data = { 522 | 'overseaPurchaseCookies': '', 523 | 'vendorRemarks': '[]', 524 | 'submitOrderParam.sopNotPutInvoice': 'false', 525 | 'submitOrderParam.trackID': 'TestTrackId', 526 | 'submitOrderParam.ignorePriceChange': '0', 527 | 'submitOrderParam.btSupport': '0', 528 | 'riskControl': risk_control, 529 | 'submitOrderParam.isBestCoupon': 1, 530 | 'submitOrderParam.jxj': 1, 531 | # Todo: need to get trackId 532 | 'submitOrderParam.trackId': '9643cbd55bbbe103eef18a213e069eb0', 533 | # 'submitOrderParam.eid': eid, 534 | # 'submitOrderParam.fp': fp, 535 | 'submitOrderParam.needCheck': 1, 536 | } 537 | 538 | def encrypt_payment_pwd(payment_pwd): 539 | return ''.join(['u3' + x for x in payment_pwd]) 540 | 541 | if len(self.payment_pwd) > 0: 542 | data['submitOrderParam.payPassword'] = encrypt_payment_pwd( 543 | self.payment_pwd) 544 | 545 | headers = { 546 | "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", 547 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 548 | "Referer": "http://trade.jd.com/shopping/order/getOrderInfo.action", 549 | "Connection": "keep-alive", 550 | 'Host': 'trade.jd.com', 551 | } 552 | 553 | try: 554 | resp = self.session.post(url=url, data=data, headers=headers) 555 | if "刷新太频繁了" in resp.text: 556 | logger.error("刷新太频繁了 url: {}".format(url)) 557 | return False 558 | resp_json = json.loads(resp.text) 559 | 560 | # 返回信息示例: 561 | # 下单失败 562 | # {'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': '请输入支付密码!'} 563 | # {'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': '您多次提交过快,请稍后再试'} 564 | # {'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': '获取用户订单信息失败'} 565 | # {"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商品无货"} 566 | # {'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商品无货'} 567 | # 下单成功 568 | # {'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} 569 | 570 | if resp_json.get('success'): 571 | logger.info('订单提交成功! 订单号:%s', resp_json.get('orderId')) 572 | sendMessage('订单提交成功! 订单号:{}'.format(resp_json.get('orderId'))) 573 | sys.exit(1) 574 | return True 575 | else: 576 | message, result_code = resp_json.get( 577 | 'message'), resp_json.get('resultCode') 578 | if result_code == 0: 579 | # self._save_invoice() 580 | message = message + '<<<<<<<<<<京东返回的限制信息<<<<<<<-------该商品被京东限制购买' 581 | elif result_code == 60077: 582 | message = message + '(可能是购物车为空 或 未勾选购物车中商品)' 583 | elif result_code == 60123: 584 | message = message + '(需要在payment_pwd参数配置支付密码)' 585 | logger.info('订单提交失败, 错误码:%s, 返回信息:%s', result_code, message) 586 | logger.info(resp_json) 587 | return False 588 | except Exception as e: 589 | logger.error(e) 590 | return False 591 | 592 | ''' 593 | 购买环节 594 | 测试三次 595 | ''' 596 | 597 | def buyMask(self, sku_id): 598 | retry = self.retry 599 | for count in range(retry): 600 | logger.info('第[%s/%s]次尝试提交订单', count, 3) 601 | self.cancel_select_all_cart_item() 602 | cart = self.cart_detail() 603 | if sku_id in cart: 604 | logger.info('%s 已在购物车中,调整数量为 %s', sku_id, 1) 605 | cart_item = cart.get(sku_id) 606 | self.change_item_num_in_cart( 607 | sku_id=sku_id, 608 | vender_id=cart_item.get('vender_id'), 609 | num=self.skuids, 610 | p_type=cart_item.get('p_type'), 611 | target_id=cart_item.get('target_id'), 612 | promo_id=cart_item.get('promo_id') 613 | ) 614 | else: 615 | self.add_item_to_cart(sku_id) 616 | risk_control = self.get_checkout_page_detail() 617 | if risk_control == '刷新太频繁了': 618 | return False 619 | if len(risk_control) > 0: 620 | if self.submit_order(risk_control): 621 | return True 622 | logger.info('休息%ss', 3) 623 | time.sleep(3) 624 | else: 625 | logger.info('执行结束,提交订单失败!') 626 | return False 627 | 628 | ''' 629 | 查询库存 630 | ''' 631 | 632 | ''' 633 | update by rlacat 634 | 解决skuid长度过长(超过99个)导致无法查询问题 635 | ''' 636 | 637 | def check_stock(self): 638 | st_tmp = [] 639 | len_arg = 70 640 | # print("skustr:",skuidStr) 641 | # print("skuids:",len(skuids)) 642 | skuid_nums = len(self.skuids) 643 | skuid_batchs = math.ceil(skuid_nums / len_arg) 644 | # print("skuid_batchs:",skuid_batchs) 645 | if (skuid_batchs > 1): 646 | for i in range(0, skuid_batchs): 647 | if (len_arg * (i + 1) <= len(self.skuids)): 648 | # print("取个数:",len_arg*i,"至",len_arg*(i+1)) 649 | skuidStr = ','.join( 650 | self.skuids[len_arg * i:len_arg * (i + 1)]) 651 | st_tmp += self.check_stock_tmp(skuidStr, 652 | self.skuids[len_arg * i:len_arg * (i + 1)]) 653 | else: 654 | # print("取个数:",len_arg*i,"至",len_arg*(i+1)) 655 | skuidStr = ','.join( 656 | self.skuids[len_arg * i:skuid_nums]) # skuid配置的最后一段 657 | # print(skuidStr) 658 | st_tmp += self.check_stock_tmp(skuidStr, 659 | self.skuids[len_arg * i:skuid_nums]) 660 | else: 661 | # <=1的情况 662 | skuidStr = ','.join(self.skuids) 663 | st_tmp = self.check_stock_tmp(skuidStr, self.skuids) 664 | return st_tmp 665 | 666 | def check_stock_tmp(self, skuidString, skuids_a): 667 | callback = 'jQuery' + str(random.randint(1000000, 9999999)) 668 | headers = { 669 | "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", 670 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 671 | "Referer": "https://cart.jd.com/cart.action", 672 | "Connection": "keep-alive", 673 | } 674 | url = 'https://c0.3.cn/stocks' 675 | payload = { 676 | 'type': 'getstocks', 677 | 'skuIds': skuidString, 678 | 'area': self.area, 679 | 'callback': callback, 680 | '_': int(time.time() * 1000), 681 | } 682 | resp = self.session.get(url=url, params=payload, headers=headers) 683 | resptext = resp.text.replace(callback + '(', '').replace(')', '') 684 | respjson = json.loads(resptext) 685 | inStockSkuid = [] 686 | nohasSkuid = [] 687 | # print(resptext,respjson) 688 | for i in skuids_a: 689 | # print("当前处理:",i) 690 | if (respjson[i]['StockStateName'] != '无货'): 691 | inStockSkuid.append(i) 692 | else: 693 | nohasSkuid.append(i) 694 | # print(nohasSkuid) 695 | logger.info('[%s]无货', ','.join(nohasSkuid)) 696 | return inStockSkuid 697 | 698 | @check_login 699 | def buy(self): 700 | sku_id = self.skuids 701 | retry = self.retry 702 | for count in range(retry): 703 | logger.info('第[%s/%s]次尝试提交订单', count, retry) 704 | self.cancel_select_all_cart_item() 705 | cart = self.cart_detail() 706 | if sku_id in cart: 707 | logger.info('%s 已在购物车中,调整数量为 %s', sku_id, self.count) 708 | cart_item = cart.get(sku_id) 709 | self.change_item_num_in_cart( 710 | sku_id=sku_id, 711 | vender_id=cart_item.get('vender_id'), 712 | num=self.skuids, 713 | p_type=cart_item.get('p_type'), 714 | target_id=cart_item.get('target_id'), 715 | promo_id=cart_item.get('promo_id') 716 | ) 717 | else: 718 | self.add_item_to_cart(sku_id) 719 | risk_control = self.get_checkout_page_detail() 720 | if risk_control == '刷新太频繁了': 721 | return False 722 | if len(risk_control) > 0: 723 | if self.submit_order(risk_control): 724 | return True 725 | logger.info('休息%ss', 3) 726 | time.sleep(3) 727 | else: 728 | logger.info('执行结束,提交订单失败!') 729 | return False 730 | 731 | @check_login 732 | def initCart(self): 733 | sku_id = self.skuids 734 | self.cancel_select_all_cart_item() 735 | cart = self.cart_detail() 736 | if sku_id in cart: 737 | logger.info('%s 已在购物车中,调整数量为 %s', sku_id, self.count) 738 | cart_item = cart.get(sku_id) 739 | self.change_item_num_in_cart( 740 | sku_id=sku_id, 741 | vender_id=cart_item.get('vender_id'), 742 | num=self.count, 743 | p_type=cart_item.get('p_type'), 744 | target_id=cart_item.get('target_id'), 745 | promo_id=cart_item.get('promo_id') 746 | ) 747 | else: 748 | self.add_item_to_cart(sku_id) 749 | logger.info('购物车初始化结束,程序开始后请勿更改购物车') 750 | 751 | @check_login 752 | def fastBuy(self): 753 | retry = self.retry 754 | for count in range(retry): 755 | logger.info('第[%s/%s]次尝试提交订单', count, retry) 756 | risk_control = self.get_checkout_page_detail() 757 | if risk_control == -1: 758 | continue 759 | if risk_control == '刷新太频繁了': 760 | return False 761 | if len(risk_control) > 0: 762 | if self.submit_order(risk_control): 763 | return True 764 | logger.info('休息%ss', 5) 765 | time.sleep(5) 766 | else: 767 | logger.info('执行结束,提交订单失败!') 768 | return False 769 | -------------------------------------------------------------------------------- /Core/timer.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import time 3 | import requests 4 | import json 5 | 6 | from datetime import datetime 7 | from Logger.logger import logger 8 | from Config.settings import config 9 | 10 | 11 | class Timer(object): 12 | def __init__(self, buyTime, sleep_interval=0.5): 13 | # '2018-09-28 22:45:50.000' 14 | # buy_time = 2020-12-22 09:59:59.500 15 | buy_time_everyday = buyTime 16 | localtime = time.localtime(time.time()) 17 | #self.buy_time = datetime.strptime( 18 | # localtime.tm_year.__str__() + '-' + localtime.tm_mon.__str__() + '-' + localtime.tm_mday.__str__() 19 | # + ' ' + buy_time_everyday, 20 | # "%Y-%m-%d %H:%M:%S.%f") 21 | self.buy_time = datetime.strptime(buy_time_everyday, "%Y-%m-%d %H:%M:%S.%f") 22 | self.buy_time_ms = int(time.mktime(self.buy_time.timetuple()) * 1000.0 + self.buy_time.microsecond / 1000) 23 | self.sleep_interval = sleep_interval 24 | 25 | self.diff_time = self.local_jd_time_diff() 26 | 27 | def jd_time(self): 28 | """ 29 | 从京东服务器获取时间毫秒 30 | :return: 31 | """ 32 | url = 'https://api.m.jd.com/client.action?functionId=queryMaterialProducts&client=wh5' 33 | ret = requests.get(url).text 34 | js = json.loads(ret) 35 | return int(js["currentTime2"]) 36 | # return int(round(time.time() * 1000)) 37 | 38 | def local_time(self): 39 | """ 40 | 获取本地毫秒时间 41 | :return: 42 | """ 43 | return int(round(time.time() * 1000)) 44 | 45 | def local_jd_time_diff(self): 46 | """ 47 | 计算本地与京东服务器时间差 48 | :return: 49 | """ 50 | return self.local_time() - self.jd_time() 51 | 52 | def start(self): 53 | logger.info('正在等待到达设定时间:{}'.format(self.buy_time)) 54 | logger.info('正检测本地时间与京东服务器时间误差为【{}】毫秒'.format(self.diff_time)) 55 | 56 | while True: 57 | # 本地时间减去与京东的时间差,能够将时间误差提升到0.1秒附近 58 | # 具体精度依赖获取京东服务器时间的网络时间损耗 59 | if self.local_time() - self.diff_time >= self.buy_time_ms: 60 | logger.info('时间到达,开始执行……') 61 | break 62 | else: 63 | time.sleep(self.sleep_interval) 64 | 65 | -------------------------------------------------------------------------------- /Core/util.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | import requests 4 | import os 5 | import time 6 | 7 | from Config.settings import config 8 | 9 | USER_AGENTS = [ 10 | "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", 11 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36", 12 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36", 13 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36", 14 | "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2226.0 Safari/537.36", 15 | "Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36", 16 | "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36", 17 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2224.3 Safari/537.36", 18 | "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36", 19 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36", 20 | "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", 21 | "Mozilla/5.0 (Windows NT 4.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", 22 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36", 23 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36", 24 | "Mozilla/5.0 (X11; OpenBSD i386) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36", 25 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36", 26 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36", 27 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.2309.372 Safari/537.36", 28 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.2117.157 Safari/537.36", 29 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36", 30 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1866.237 Safari/537.36", 31 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/4E423F", 32 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36 Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10", 33 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.517 Safari/537.36", 34 | "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36", 35 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36", 36 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36", 37 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36", 38 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1623.0 Safari/537.36", 39 | "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36", 40 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36", 41 | "Mozilla/5.0 (X11; CrOS i686 4319.74.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36", 42 | "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.2 Safari/537.36", 43 | "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1468.0 Safari/537.36", 44 | "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1467.0 Safari/537.36", 45 | "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1464.0 Safari/537.36", 46 | "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1500.55 Safari/537.36", 47 | "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", 48 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", 49 | "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", 50 | "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", 51 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", 52 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", 53 | "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.90 Safari/537.36", 54 | "Mozilla/5.0 (X11; NetBSD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36", 55 | "Mozilla/5.0 (X11; CrOS i686 3912.101.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36", 56 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17", 57 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17", 58 | "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 Safari/537.15", 59 | "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.14 (KHTML, like Gecko) Chrome/24.0.1292.0 Safari/537.14" 60 | ] 61 | 62 | 63 | def parse_json(s): 64 | begin = s.find('{') 65 | end = s.rfind('}') + 1 66 | return json.loads(s[begin:end]) 67 | 68 | 69 | def get_random_useragent(): 70 | """生成随机的UserAgent 71 | :return: UserAgent字符串 72 | """ 73 | return random.choice(USER_AGENTS) 74 | 75 | 76 | def wait_some_time(): 77 | time.sleep(random.randint(100, 300) / 1000) 78 | 79 | 80 | def send_wechat(message): 81 | """推送信息到微信""" 82 | url = 'http://sc.ftqq.com/{}.send'.format(config.settings('messenger', 'sckey')) 83 | payload = { 84 | "text":'抢购结果', 85 | "desp": message 86 | } 87 | headers = { 88 | 'User-Agent':config.settings('config', 'DEFAULT_USER_AGENT') 89 | } 90 | requests.get(url, params=payload, headers=headers) 91 | 92 | 93 | def response_status(resp): 94 | if resp.status_code != requests.codes.OK: 95 | print('Status: %u, Url: %s' % (resp.status_code, resp.url)) 96 | return False 97 | return True 98 | 99 | 100 | def open_image(image_file): 101 | if os.name == "nt": 102 | os.system('start ' + config.path() + '/Static/img/'+ image_file) # for Windows 103 | else: 104 | if os.uname()[0] == "Linux": 105 | if "deepin" in os.uname()[2]: 106 | os.system("deepin-image-viewer " + image_file) # for deepin 107 | else: 108 | os.system("eog " + image_file) # for Linux 109 | else: 110 | os.system("open " + image_file) # for Mac 111 | 112 | 113 | def save_image(resp, image_file): 114 | with open(config.path() + '/Static/img/' + image_file, 'wb') as f: 115 | for chunk in resp.iter_content(chunk_size=1024): 116 | f.write(chunk) 117 | -------------------------------------------------------------------------------- /Docs/change-log.md: -------------------------------------------------------------------------------- 1 | ## 更新日志 2 | 3 | ### v1.0.0-beta 4 | - 迁移整体框架到TinyServer 5 | - 更新 web ui 文件 6 | 7 | ### v1.0.0 8 | 感谢 [*@FlameGate*](https://gitee.com/yanwen0614) 对本项目的贡献 9 | - 修复了windows平台下的兼容性问题 10 | - 修复了网络波动带来的递归调用问题 11 | - 默认host从 *'0.0.0.0'* 迁移到 *'localhost'* 12 | 13 | ### v1.0.1 14 | - 修复了字体和图标显示错误的问题 15 | - 对本地GUI程序进行了初步配置 16 | - 对readme以及文档结构进行修改 17 | 18 | ### v1.1.0 19 | - 上线windows开箱即用exe程序 20 | - 修复windows web ui显示bug 21 | - 更新readme自行打包方法 22 | 23 | ### V1.2.0-计划 24 | - 上线价格低于设定值自动下单功能 25 | 26 | ### v2.0.0-计划 27 | - 上线秒杀抢购功能 -------------------------------------------------------------------------------- /Docs/img/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aa342138039/JD-SHOPPER/7baca21df4e2e1db53067a5813d8b31ba586df34/Docs/img/banner.jpg -------------------------------------------------------------------------------- /Docs/img/shopper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aa342138039/JD-SHOPPER/7baca21df4e2e1db53067a5813d8b31ba586df34/Docs/img/shopper.png -------------------------------------------------------------------------------- /Docs/img/shopper.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Docs/notice.md: -------------------------------------------------------------------------------- 1 | ## 使用须知: 2 | 3 | - 如有侵权违规,请提交PR,私信,联系我,会在7工作日内删除。 4 | 5 | * 本仓库发布的`JD_SHOPPER`项目中涉及的任何脚本,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。 6 | 7 | * 本项目内所有资源文件,禁止任何公众号、自媒体进行任何形式的转载、发布。 8 | 9 | * `louisyoungx` 对任何脚本问题概不负责,包括但不限于由任何脚本错误导致的任何损失或损害. 10 | 11 | * 间接使用脚本的任何用户,包括但不限于建立VPS或在某些行为违反国家/地区法律或相关法规的情况下进行传播, `louisyoungx` 对于由此引起的任何隐私泄漏或其他后果概不负责。 12 | 13 | * 请勿将`JD_SHOPPER`项目的任何内容用于商业或非法目的,否则后果自负。 14 | 15 | * 如果任何单位或个人认为该项目的脚本可能涉嫌侵犯其权利,则应及时通知并提供身份证明,所有权证明,我们将在收到认证文件后删除相关脚本。 16 | 17 | * 以任何方式查看此项目的人或直接或间接使用`JD_SHOPPER`项目的任何脚本的使用者都应仔细阅读此声明。`louisyoungx` 保留随时更改或补充此免责声明的权利。一旦使用并复制了任何相关脚本或`jd_seckill`项目,则视为您已接受此免责声明。 18 | 19 | * 您必须在下载后的24小时内从计算机或手机中完全删除以上内容。 20 | 21 | * 本项目遵循`GPL-3.0 License`协议,如果本特别声明与`GPL-3.0 License`协议有冲突之处,以本特别声明为准。 22 | -------------------------------------------------------------------------------- /GUI/gui.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from Config.settings import config 4 | 5 | PROJECT = config.settings("Information", "PROJECT") 6 | SERVER_HOST = config.settings("Server", "SERVER_HOST") 7 | PORT = config.settings("Server", "PORT") 8 | 9 | def gui(): 10 | url = "http://{}:{}/".format(SERVER_HOST, PORT) 11 | 12 | # import webview 13 | # webview.create_window(PROJECT, 14 | # url=url, 15 | # js_api=None, 16 | # width=900, 17 | # height=800, 18 | # resizable=True, 19 | # fullscreen=False, 20 | # min_size=(200, 200), 21 | # background_color='#FFF', 22 | # text_select=False) 23 | # webview.start() 24 | -------------------------------------------------------------------------------- /GUI/pack_requirements.txt: -------------------------------------------------------------------------------- 1 | lxml==4.5.1 2 | bs4==0.0.1 3 | requests==2.24.0 4 | pywebview==3.5 5 | PyInstaller==3.6 -------------------------------------------------------------------------------- /GUI/packing.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | RPATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 4 | PATH = RPATH.replace("\\", "/") 5 | 6 | 7 | def run(*commands): 8 | for command in commands: 9 | print(command) 10 | os.system(command) 11 | 12 | pack = f'pyinstaller --noconfirm --onedir --windowed \ 13 | --icon "{PATH}/Static/favicon.ico" \ 14 | --name "jd-shopper" --add-data "{PATH}/Config;Config/" \ 15 | --add-data "{PATH}/cookies;cookies/" \ 16 | --add-data "{PATH}/Core;Core/" \ 17 | --add-data "{PATH}/Docs;Docs/" \ 18 | --add-data "{PATH}/GUI;GUI/" \ 19 | --add-data "{PATH}/Logger;Logger/" \ 20 | --add-data "{PATH}/Message;Message/" \ 21 | --add-data "{PATH}/Scheduler;Scheduler/" \ 22 | --add-data "{PATH}/Server;Server/" \ 23 | --add-data "{PATH}/Static;Static/" \ 24 | --add-data "{PATH}/TEST;TEST/" \ 25 | "{PATH}/runserver.py" \ 26 | ' 27 | 28 | if __name__ == '__main__': 29 | run(pack) 30 | -------------------------------------------------------------------------------- /Logger/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 日志模块 3 | ''' 4 | -------------------------------------------------------------------------------- /Logger/logger.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import logging.handlers 4 | from Config.settings import config 5 | 6 | # 从config中查询所需数据 7 | path = config.path() + config.settings("Logger", "FILE_PATH") 8 | filename = config.path() + config.settings("Logger", "FILE_PATH") + config.settings("Logger", "FILE_NAME") 9 | maxBytes = config.settings("Logger", "MAX_BYTES") 10 | backupCount = config.settings("Logger", "AMOUNT") 11 | clearUp = config.settings("Logger", "CLEAR_UP") 12 | 13 | # 创建一个logger,创建一个列表存放logger数据 14 | logger = logging.getLogger() 15 | logger_records = [] 16 | 17 | class CustomFilter(logging.Filter): 18 | def filter(self, record): 19 | # logger_records.append(record.msg) 20 | return record.msg 21 | 22 | def clearUpLogFile(): 23 | if not os.path.exists(path): 24 | os.mkdir(path) 25 | with open(filename, "w") as file: 26 | file.seek(0) 27 | file.truncate() # 清空文件 28 | 29 | def logger_init(): 30 | # 清理log文件 31 | if clearUp: 32 | clearUpLogFile() 33 | 34 | # 设置logger输出级别 35 | logger.setLevel(logging.INFO) 36 | filter = CustomFilter() 37 | logger.addFilter(filter) 38 | 39 | # 设置logger输出格式 40 | # fmt = logging.Formatter('%(asctime)s - %(process)d-%(threadName)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s') 41 | fmt = '[%(asctime)s] (%(module)s.%(funcName)s): <%(levelname)s> %(message)s' 42 | datefmt = "%Y-%m-%d %H:%M:%S" 43 | formatter = logging.Formatter(fmt=fmt, datefmt=datefmt) 44 | 45 | # 定义一个console_handler,用于输出到控制台 46 | console_handler = logging.StreamHandler() 47 | 48 | # 定义一个console_handler,用于输出到控制台 49 | file_handler = logging.handlers.RotatingFileHandler( 50 | filename, maxBytes=maxBytes, backupCount=backupCount, encoding="utf-8") 51 | 52 | # 给handler添加formatter 53 | console_handler.setFormatter(formatter) 54 | file_handler.setFormatter(formatter) 55 | 56 | # 把初始化完毕的handler对象添加到logger对象中 57 | logger.addHandler(console_handler) 58 | logger.addHandler(file_handler) 59 | 60 | 61 | logger_init() 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Message/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 消息模块 3 | ''' -------------------------------------------------------------------------------- /Message/message.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding=utf8 -*- 3 | import datetime 4 | import json 5 | 6 | import requests 7 | 8 | from Logger.logger import logger 9 | from Config.settings import config 10 | 11 | 12 | class Messenger(object): 13 | """消息推送类""" 14 | 15 | def __init__(self, sc_key): 16 | if not sc_key: 17 | raise Exception('sc_key can not be empty') 18 | 19 | self.sc_key = sc_key 20 | 21 | def send(self, text, desp=''): 22 | if not text.strip(): 23 | logger.error('Text of message is empty!') 24 | return 25 | 26 | now_time = str(datetime.datetime.now()) 27 | desp = '[{0}]'.format(now_time) if not desp else '{0} [{1}]'.format(desp, now_time) 28 | 29 | try: 30 | resp = requests.get( 31 | 'https://sc.ftqq.com/{}.send?text={}&desp={}'.format(self.sc_key, text, desp) 32 | ) 33 | resp_json = json.loads(resp.text) 34 | if resp_json.get('errno') == 0: 35 | logger.info('Message sent successfully [text: %s, desp: %s]', text, desp) 36 | else: 37 | logger.error('Fail to send message, reason: %s', resp.text) 38 | except requests.exceptions.RequestException as req_error: 39 | logger.error('Request error: %s', req_error) 40 | except Exception as e: 41 | logger.error('Fail to send message [text: %s, desp: %s]: %s', text, desp, e) 42 | 43 | 44 | sckey = config.settings("Message", "sckey") 45 | message = Messenger(sckey) 46 | 47 | 48 | def sendMessage(mes): 49 | message.send(mes) 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JD_SHOPPER 2 | 3 | [![version](https://img.shields.io/badge/python-3.4+-blue.svg)](https://www.python.org/download/releases/3.4.0/) 4 | [![status](https://img.shields.io/badge/status-stable-green.svg)](https://github.com/tychxn/jd-assistant) 5 | [![license](https://img.shields.io/badge/license-GPL-blue.svg)](./LICENSE) 6 | [![star, issue](https://img.shields.io/badge/star%2C%20issue-welcome-brightgreen.svg)](https://github.com/tychxn/jd-assistant) 7 | 8 | 京东抢购助手 9 | 10 | 1.缺货上架自动加购物车下单 11 | 12 | 2.定时加购物车下单 13 | 14 | ![操作界面](./Docs/img/banner.jpg) 15 | 16 | ## 主要功能 17 | 18 | - 开箱即用的软件(仅windows) 19 | - web操作界面(跨平台) 20 | - 登陆京东商城([www.jd.com](http://www.jd.com/)) 21 | - 手机扫码登录 22 | - 保存/加载登录cookies (可验证cookies是否过期) 23 | - 商品查询操作 24 | - 提供完整的[`地址⇔ID`](./area_id/)对应关系 25 | - 根据商品ID和地址ID查询库存 26 | - 根据商品ID查询价格 27 | - 购物车操作 28 | - 清空/添加购物车 (无货商品也可以加入购物车,预约商品无法加入) 29 | - 获取购物车商品详情 30 | - 订单操作 31 | - 获取订单结算页面信息 (商品详情, 应付总额, 收货地址, 收货人等) 32 | - 提交订单(使用默认地址) 33 | - 其他 34 | - 商品预约 35 | - 用户信息查询 36 | 37 | ## 近期更新 38 | 39 | [查看详细日志](./Docs/change-log.md) 40 | 41 | ### v1.0.0 42 | 43 | 感谢 [*@FlameGate*](https://gitee.com/yanwen0614) 对本项目的贡献 44 | 45 | - 修复了windows平台下的兼容性问题 46 | - 修复了网络波动带来的递归调用问题 47 | - 默认host从 *'0.0.0.0'* 迁移到 *'localhost'* 48 | 49 | ### v1.0.1 50 | 51 | - 修复了字体和图标显示错误的问题 52 | - 对本地GUI程序进行了初步配置 53 | - 对readme以及文档结构进行修改 54 | 55 | ### v1.1.0 56 | - 上线windows开箱即用exe程序 57 | - 修复windows web ui显示bug 58 | - 更新readme自行打包方法 59 | 60 | ### 即将到来 61 | - 上线价格低于设定值自动下单功能 62 | 63 | ## 特别声明: 64 | 65 | 使用本仓库之前**必须阅读** -- [*使用须知*](./Docs/notice.md) 66 | > ***您使用或者复制了本仓库且本人制作的任何代码或项目,则视为`已接受`此声明,请仔细阅读*** 67 | > ***您在本声明未发出之时点使用或者复制了本仓库且本人制作的任何代码或项目且此时还在使用,则视为`已接受`此声明,请仔细阅读*** 68 | 69 | ## 快速开始 70 | 71 | ### 通过 *windows程序* 运行 72 | > [点我直接下载程序压缩包](https://gitee.com/louisyoungx/JD-SHOPPER/attach_files/888014/download/jd-shopper.zip) 73 | 1. 到release界面下载jd-shopper.zip包 74 | 2. 解压后在jd-shopper目录找到jd-shopper.exe文件 75 | 3. 点击运行,或者右键生成快捷方式后运行 76 | 77 | ### 通过 *源代码* 运行 78 | 79 | #### 运行环境 80 | 81 | - [Python 3](https://www.python.org/) 82 | 83 | #### 第三方库 84 | 85 | - [Requests](http://docs.python-requests.org/en/master/) 86 | - [Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) 87 | - [lxml](https://lxml.de) 88 | 89 | 安装: 90 | 91 | ```sh 92 | pip install -r pack_requirements.txt 93 | ``` 94 | 95 | Tips: 96 | > 97 | > 如果国内安装第三方库比较慢,可以使用以下指令进行清华源加速: 98 | > ```sh 99 | > pip install -r pack_requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ 100 | > ``` 101 | > 102 | > 如果安装错误也可尝试 103 | > ```sh 104 | > pip install requests lxml bs4 105 | > ``` 106 | 107 | ### *web*界面 - 使用教程 108 | 109 | 1. 推荐 **Chrome** 浏览器 110 | 2. 到 */TEST/area_id/* 目录下查询你所在地的地区标识ID 111 | 3. 如果你的账户有**京豆京券余额**,请到 */Config/config.ini* 设置密码,或者换个账号运行 112 | 4. 命令行运行服务 113 | 114 | ```shell 115 | python3 runserver.py 116 | ``` 117 | 118 | 5. 浏览器打开 http://localhost:12021/ 119 | 120 | ### *命令行* - 使用教程 121 | 122 | #### 1. 新版的web界面并不稳定,也许命令行更适合 123 | 124 | #### 2. 网页扫码登录,或者账号密码登录 125 | 126 | #### 3. 填写config.ini配置信息 127 | 128 | (1)`eid`和`fp`找个普通商品随便下单,然后抓包就能看到,这两个值可以填固定的 129 | > 随便找一个商品下单,然后进入结算页面,打开浏览器的调试窗口,切换到控制台Tab页,在控制台中输入变量`_JdTdudfp`,即可从输出的Json中获取`eid`和`fp`。 130 | > 不会的话参考作者3的👉 [使用教程请参看Wiki](https://github.com/tychxn/jd-assistant/wiki/1.-%E4%BA%AC%E4%B8%9C%E6%8A%A2%E8%B4%AD%E5%8A%A9%E6%89%8B%E7%94%A8%E6%B3%95) 131 | 132 | > *不会的同学也可以打开目录下的get_eid_fp.html文件,不过有概率失败* 133 | 134 | 135 | (2)`sku_id`,`DEFAULT_USER_AGENT` 136 | > `sku_id`已经按照Xbox Series S的填好。 137 | > `cookies_string` 现在已经不需要填写了 138 | > `DEFAULT_USER_AGENT` 可以用默认的。谷歌浏览器也可以浏览器地址栏中输入about:version 查看`USER_AGENT`替换 139 | 140 | (3)配置一下时间 141 | > 现在不强制要求同步最新时间了,程序会自动同步京东时间 142 | > 143 | > > 但要是电脑时间快慢了好几个小时,最好还是同步一下吧 144 | 145 | 以上都是必须的. 146 | > tips: 147 | > 在程序开始运行后,会检测本地时间与京东服务器时间,输出的差值为本地时间-京东服务器时间,即-50为本地时间比京东服务器时间慢50ms。 148 | > 本代码的执行的抢购时间以本地电脑/服务器时间为准 149 | 150 | (4)修改抢购件数 151 | > 代码中默认抢购件数为2 152 | > 具体修改为:在config.ini文件 153 | 154 | (5) **特别提示!** *不止一个人卡在这个问题,如果不注意至少花两个小时调试debug* 155 | > 如果你的账户中有可用的京券(注意不是东券)或 在上次购买订单中使用了京豆, 156 | > 那么京东可能会在下单时自动选择京券支付 或 自动勾选京豆支付。 157 | > 此时下单会要求输入六位数字的支付密码。请在config.ini配置你的支付密码,如 123456 。 158 | > 159 | > 显著特点是添加购物车能成功,但一到订单结算页面就报错,基本就是这个原因! 160 | 161 | #### 4.运行main.py 162 | 163 | ```sh 164 | python3 main.py 165 | ``` 166 | 167 | 根据提示选择相应功能即可 168 | 169 | #### 5.抢购结果确认 170 | 171 | 抢购是否成功通常在程序开始的一分钟内可见分晓! 172 | 搜索日志,出现“抢购成功,订单号xxxxx",代表成功抢到了,务必半小时内支付订单!程序暂时不支持自动停止,需要手动STOP! 173 | 若两分钟还未抢购成功,基本上就是没抢到!程序暂时不支持自动停止,需要手动STOP! 174 | 175 | ## 自行打包可执行文件 176 | > 本方法依赖 **PyInstaller**,也可采用其他模块进行打包 177 | > 打包入口在runserver.py 178 | 1. 修改Config/config.ini文件 179 | ```shell 180 | [GUI] 181 | START_USING=TRUE 182 | ``` 183 | 2. 安装打包依赖 184 | ```shell 185 | pip install GUI/pack_requirements.txt 186 | ``` 187 | 3. 使用 PyInstaller 进行打包 188 | ```shell 189 | python GUI/packing.py 190 | ``` 191 | 4. 生成可执行文件在 dist/目录中 192 | 193 | ## 感谢 194 | 195 | ##### 作者-1 https://github.com/zhou-xiaojun/jd_mask 的开源项目 196 | 197 | ##### 作者-2 https://github.com/wlwwu/jd_maotai 的开源项目 198 | 199 | ##### 作者-3 https://github.com/andyzys/jd_seckill 的开源项目 200 | 201 | ##### 作者-4 https://github.com/tychxn/jd-assistant 的开源项目 202 | 203 | ##### 作者-5 https://gitee.com/iszhangk/jd_robot 的开源项目 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /Scheduler/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 消息模块 3 | ''' -------------------------------------------------------------------------------- /Scheduler/scheduler.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import time 3 | from Logger.logger import logger 4 | 5 | 6 | class Timer(object): 7 | 8 | def __init__(self, task, startTime, skipWeekend): 9 | '''初始化''' 10 | self.task = task 11 | self.start_time = startTime 12 | self.skip_weekend = skipWeekend 13 | 14 | def schedule(self): 15 | '''调度执行上下文''' 16 | while True: # 一个循环为一天时间 17 | self._schedule() # 进入今天的循环 18 | self.sleepToTomorrow() # 今天的任务结束,休眠到下一天 19 | 20 | def _schedule(self): 21 | '''调度执行''' 22 | 23 | logger.info("Daily Task Initialized Successfully") 24 | 25 | if self.skip_weekend and not self.isTodayWorkday(): 26 | # 今天不是工作日,结束今天的任务 27 | return False 28 | 29 | elif self.isTimePass(): 30 | # 今天的任务时间已经过了,结束今天的任务 31 | return False 32 | 33 | else: 34 | self.execute() 35 | return True 36 | 37 | def execute(self): 38 | real_datetime = self.realDate() # 当前的时间(日期) 39 | real_mstime = self.dateMSTime(real_datetime) # 当前的时间(毫秒) 40 | today_task_datetime = self.todayTaskTime() # 今天任务时间(日期) 41 | today_task_mstime = self.dateMSTime(today_task_datetime) # 今天任务时间(毫秒) 42 | wait_time = today_task_mstime - real_mstime # 获取当前时间与任务的时间差 43 | logger.info("Waiting to Start Mission -> {}".format(today_task_datetime)) 44 | time.sleep(wait_time) # 线程休眠阻塞任务 45 | self.task() # 阻塞结束执行 46 | logger.info("Today's Mission Completed") 47 | 48 | 49 | def realDate(self): 50 | ''' 51 | 获取当前的日期与时间 52 | return: date "%Y-%m-%d %H:%M:%S" 53 | ''' 54 | localtime = time.localtime(time.time()) 55 | date = \ 56 | localtime.tm_year.__str__() + '-' + \ 57 | localtime.tm_mon.__str__() + '-' + \ 58 | localtime.tm_mday.__str__() + ' ' + \ 59 | localtime.tm_hour.__str__() + ':' + \ 60 | localtime.tm_min.__str__() + ':' + \ 61 | localtime.tm_sec.__str__() 62 | return date 63 | 64 | def realMSTime(self): 65 | ''' 66 | 获取当前的毫秒时间 67 | return: 毫秒时间 68 | ''' 69 | return time.time() 70 | 71 | def tomorrowMSTime(self): 72 | ''' 73 | 获取明天的00:00:00毫秒时间 74 | return: 毫秒时间 75 | ''' 76 | localtime = time.localtime(time.time()) 77 | # 今天00:00:00的日期时间 78 | today_start_date = \ 79 | localtime.tm_year.__str__() + '-' + \ 80 | localtime.tm_mon.__str__() + '-' + \ 81 | localtime.tm_mday.__str__() + ' ' + \ 82 | '00:00:00' 83 | today_start_time = self.dateMSTime(today_start_date) 84 | tomorrow_start_time = today_start_time + 60 * 60 * 24 85 | return tomorrow_start_time 86 | 87 | def isTodayWorkday(self): 88 | ''' 89 | 该日期是否为工作日 90 | params: 91 | date "%Y-%m-%d %H:%M:%S" 92 | return: 93 | 工作日: True 94 | 休息日: False 95 | ''' 96 | localtime = time.localtime(time.time()) 97 | week = localtime.tm_wday.__str__() 98 | if week in (5, 6): 99 | # 如果是休息日 100 | logger.info("Over The Weekend") 101 | return False 102 | else: 103 | # 如果是工作日 104 | logger.info("Working Day") 105 | return True 106 | 107 | def sleepToTomorrow(self): 108 | '''休眠到下一天''' 109 | real_datetime = self.realDate() # 当前的时间(日期) 110 | real_mstime = self.dateMSTime(real_datetime) # 当前的时间(毫秒) 111 | tomorrow_mstime = self.tomorrowMSTime() # 明天0点的时间(毫秒) 112 | diff_time = tomorrow_mstime - real_mstime # 现在到明天0点的毫秒时间 113 | logger.info("Sleeping to tomorrow -> {} Seconds".format(diff_time)) 114 | time.sleep(diff_time) 115 | 116 | def isTimePass(self): 117 | ''' 118 | 确认当前时间是否超过今天执行时间 119 | return: 120 | True:超时 121 | False:没超时 122 | ''' 123 | real_datetime = self.realDate() # 当前的时间(日期) 124 | real_mstime = self.dateMSTime(real_datetime) # 当前的时间(毫秒) 125 | today_task_datetime = self.todayTaskTime() # 今天任务时间(日期) 126 | today_task_mstime = self.dateMSTime(today_task_datetime) # 今天任务时间(毫秒) 127 | if real_mstime > today_task_mstime: 128 | logger.info("Time Pass - Now Time: {} TaskTime: {}".format(real_datetime, today_task_datetime)) 129 | return True 130 | else: 131 | logger.info("Time Waiting - RealTime: {}".format(real_datetime)) 132 | return False 133 | 134 | def dateMSTime(self, date): 135 | ''' 136 | 获取该日期对应的毫秒时间 137 | params: 138 | date 格式为"%Y-%m-%d %H:%M:%S" 139 | return: 140 | ms_time 毫秒时间 141 | ''' 142 | date_time = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S") 143 | ms_time = int(time.mktime(date_time.timetuple()) + date_time.microsecond) 144 | return ms_time 145 | 146 | def todayTaskTime(self): 147 | ''' 148 | 获取今天的任务日期 149 | return: today_date "%Y-%m-%d %H:%M:%S" 150 | ''' 151 | localtime = time.localtime(time.time()) 152 | today_date = \ 153 | localtime.tm_year.__str__() + '-' + \ 154 | localtime.tm_mon.__str__() + '-' + \ 155 | localtime.tm_mday.__str__() + ' ' + \ 156 | self.start_time 157 | return today_date 158 | -------------------------------------------------------------------------------- /Server/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aa342138039/JD-SHOPPER/7baca21df4e2e1db53067a5813d8b31ba586df34/Server/__init__.py -------------------------------------------------------------------------------- /Server/api.py: -------------------------------------------------------------------------------- 1 | import copy 2 | from Config.settings import config 3 | from Core.spider import Waiter 4 | from threading import Thread 5 | class Global(object): 6 | 7 | def __init__(self): 8 | self.waiter = None 9 | self.login = None 10 | self.thread= None 11 | 12 | def update(self): 13 | self.login = self.waiter.qrlogin.is_login 14 | 15 | glo = Global() 16 | 17 | def log(request): 18 | file_path = config.path() + config.settings("Logger", "FILE_PATH") + \ 19 | config.settings("Logger", "FILE_NAME") 20 | file_page_file = open(file_path, 'r', encoding="utf-8") 21 | return str(file_page_file.read()) 22 | 23 | 24 | def serverConfig(request): 25 | appConfig = copy.deepcopy(config._config._sections) 26 | for model in appConfig: 27 | for item in appConfig[model]: 28 | appConfig[model][item] = eval(appConfig[model][item]) 29 | value = appConfig[model][item] 30 | # DEBUG print(model, item, value, type(value)) 31 | return appConfig 32 | 33 | 34 | def jdShopper(request): 35 | mode = request['mode'] 36 | date = request['date'] 37 | skuids = request['skuid'] 38 | area = request['area'] 39 | eid = request['eid'] 40 | fp = request['fp'] 41 | count = request['count'] 42 | retry = request['retry'] 43 | work_count = request['work_count'] 44 | timeout = request['timeout'] 45 | if mode == '1': 46 | glo.waiter = Waiter(skuids=skuids, area=area, eid=eid, fp=fp, count=count, 47 | retry=retry, work_count=work_count, timeout=timeout) 48 | glo.thread = Thread(target=glo.waiter.waitForSell) 49 | glo.thread.start() 50 | elif mode == '2': 51 | date = date.replace("T", " ") 52 | date = date.replace("Z", "") 53 | glo.waiter = Waiter(skuids=skuids, area=area, eid=eid, fp=fp, count=count, 54 | retry=retry, work_count=work_count, timeout=timeout, date=date) 55 | glo.thread = Thread(target=glo.waiter.waitTimeForSell) 56 | glo.thread.start() 57 | glo.update() 58 | print(glo.login) 59 | return glo.login 60 | 61 | def loginStatus(request): 62 | try: 63 | glo.update() 64 | except: 65 | pass 66 | return glo.login 67 | -------------------------------------------------------------------------------- /Server/app.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | import os 4 | from flask import request, Flask, make_response, render_template 5 | 6 | HOME = os.path.dirname(os.path.abspath(__file__)) 7 | 8 | app = Flask(__name__, 9 | instance_path=HOME, 10 | instance_relative_config=True, 11 | template_folder="../Static", 12 | static_folder="../Static", 13 | static_url_path='') 14 | 15 | app.config['JSON_AS_ASCII'] = False 16 | 17 | @app.route("/") 18 | def homePage(): 19 | return render_template('index.html') 20 | 21 | @app.route('/api/zpy', methods=['POST', 'OPTIONS']) 22 | def zpy(): 23 | if request.method == 'POST': 24 | code = request.json['code'] 25 | output = subprocess.check_output([sys.executable, '-c', code]) 26 | print(output.decode().encode()) 27 | response = make_response(output.decode()) 28 | response.headers['Access-Control-Allow-Origin'] = '*' 29 | response.headers['Access-Control-Allow-Methods'] = 'GET,POST' 30 | response.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type' 31 | return response 32 | else: 33 | # CORS跨域配置 34 | response = make_response() 35 | response.headers['Access-Control-Allow-Origin'] = '*' 36 | response.headers['Access-Control-Allow-Methods'] = 'GET,POST' 37 | response.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type' 38 | return response 39 | 40 | 41 | if __name__ == '__main__': 42 | app.run(debug=False, host='0.0.0.0', port=5000) 43 | -------------------------------------------------------------------------------- /Server/handler.py: -------------------------------------------------------------------------------- 1 | import os, json, urllib, time 2 | 3 | from Logger.logger import logger 4 | from http.server import BaseHTTPRequestHandler 5 | from Config.settings import config 6 | from Server.url import urls 7 | 8 | 9 | # Document https://docs.python.org/3.9/library/http.server.html 10 | 11 | class RequestHandler(BaseHTTPRequestHandler): 12 | """处理请求并返回页面""" 13 | 14 | # 处理一个GET请求 15 | def do_GET(self): 16 | self.rootPath = config.path() + "/Static" 17 | url = self.requestline[4:-9] 18 | # print(url) 19 | request_data = {} # 存放GET请求数据 20 | try: 21 | if url.find('?') != -1: 22 | req = url.split('?', 1)[1] 23 | url = url.split('?', 1)[0] 24 | parameters = req.split('&') 25 | for i in parameters: 26 | key, val = i.split('=', 1) 27 | request_data[key] = val 28 | # request_data['body'] = self.rfile.read() 29 | except: 30 | logger.error("URL Format Error") 31 | if (url == "/"): 32 | self.home() 33 | elif (url == ""): 34 | self.noFound() 35 | elif ("/api" in url): 36 | self.api(url[4:], request_data) 37 | else: 38 | self.file(url) 39 | 40 | def do_POST(self): 41 | LOCAL_HOST = config.settings("Server", "LOCAL_HOST") 42 | PORT = config.settings("Server", "PORT") 43 | hostLen = len(f'/{LOCAL_HOST}:{PORT}') + 5 44 | self.rootPath = config.path() + "/Static" 45 | url = self.requestline[hostLen:-9] 46 | request_data = json.loads(self.rfile.read(int(self.headers['content-length'])).decode()) 47 | if (url == "/"): 48 | self.home() 49 | elif (url == ""): 50 | self.noFound() 51 | elif ("/api" in url): 52 | self.api(url[4:], request_data) 53 | else: 54 | self.file(url) 55 | 56 | def log_message(self, format, *args): 57 | SERVER_LOGGER = config.settings("Logger", "SERVER_LOGGER") 58 | if SERVER_LOGGER: 59 | logger.info(format % args) 60 | else: 61 | pass 62 | 63 | def home(self): 64 | 65 | file_path = self.rootPath + "/index.html" 66 | home_page_file = open(file_path, 'r', encoding="utf-8") 67 | content = str(home_page_file.read()) 68 | 69 | self.send_response(200) 70 | self.send_header("Content-Type", "text/html") 71 | self.send_header("Content-Length", str(len(content))) 72 | self.end_headers() 73 | self.wfile.write(content.encode()) 74 | 75 | def file(self, url): 76 | file_name = url.split("/")[-1] 77 | file_sys_path = self.rootPath + url[:-len(file_name)] 78 | file_path = "" 79 | for root, dirs, files in os.walk(file_sys_path): 80 | for file in files: 81 | if file == file_name: 82 | file_path = os.path.join(root, file) 83 | else: 84 | continue 85 | if file_path != "": 86 | self.send_response(200) 87 | if file_path == "": 88 | # file_path = self.rootPath + "/404.html" # Hard Code 89 | self.noFound() 90 | elif file_name[-5:] == ".html": 91 | self.send_header("Content-Type", "text/html") 92 | elif file_name[-4:] == ".css": 93 | self.send_header("Content-Type", "text/css") 94 | elif file_name[-3:] == ".js": 95 | self.send_header("Content-Type", "application/javascript") 96 | elif file_name[-4:] == ".png": # 二进制文件 97 | self.send_header("Content-Type", "img/png") 98 | file_page_file = open(file_path, 'rb') 99 | self.end_headers() 100 | self.wfile.write(file_page_file.read()) 101 | return 102 | elif file_name[-4:] == ".jpg": # 二进制文件 103 | self.send_header("Content-Type", "img/jpg") 104 | file_page_file = open(file_path, 'rb') 105 | self.end_headers() 106 | self.wfile.write(file_page_file.read()) 107 | return 108 | elif file_name[-4:] == ".ico": # 二进制文件 109 | self.send_header("Content-Type", "img/ico") 110 | file_page_file = open(file_path, 'rb') 111 | self.end_headers() 112 | self.wfile.write(file_page_file.read()) 113 | return 114 | elif file_name[-5:] == ".woff": # 二进制文件 115 | self.send_header("Content-Type", "img/ico") 116 | file_page_file = open(file_path, 'rb') 117 | self.end_headers() 118 | self.wfile.write(file_page_file.read()) 119 | return 120 | file_page_file = open(file_path, 'r', encoding="utf-8") 121 | content = str(file_page_file.read()) 122 | self.send_header("Content-Length", str(len(content))) 123 | self.end_headers() 124 | self.wfile.write(content.encode()) 125 | 126 | def api(self, url, request_data): 127 | # ---------------------------------------------------------------- 128 | # 此处写API 129 | content = urls(url, request_data) 130 | # ---------------------------------------------------------------- 131 | localtime = time.localtime(time.time()) 132 | date = \ 133 | localtime.tm_year.__str__() + '-' + \ 134 | localtime.tm_mon.__str__() + '-' + \ 135 | localtime.tm_mday.__str__() + ' ' + \ 136 | localtime.tm_hour.__str__() + ':' + \ 137 | localtime.tm_min.__str__() + ':' + \ 138 | localtime.tm_sec.__str__() 139 | jsondict = {} 140 | jsondict["data"] = content 141 | jsondict["time"] = date 142 | res = json.dumps(jsondict) 143 | self.send_response(200) 144 | self.send_header("Content-Type", "text/html") 145 | self.send_header("Content-Length", str(len(res))) 146 | self.end_headers() 147 | self.wfile.write(res.encode()) 148 | 149 | def noFound(self): 150 | self.file("/404.html") 151 | -------------------------------------------------------------------------------- /Server/server.py: -------------------------------------------------------------------------------- 1 | import json 2 | from http.server import HTTPServer 3 | from Logger.logger import logger 4 | from Server.handler import RequestHandler 5 | from Config.settings import config 6 | 7 | NAME = config.settings("Server", "SERVER_NAME") 8 | VERSION = config.settings("Server", "SERVER_VERSION") 9 | DEBUG = config.settings("Debug", "DEBUG") 10 | LOCAL_HOST = config.settings("Server", "LOCAL_HOST") 11 | SERVER_HOST = config.settings("Server", "SERVER_HOST") 12 | PORT = config.settings("Server", "PORT") 13 | 14 | 15 | def server(): 16 | if DEBUG: 17 | name = LOCAL_HOST 18 | else: 19 | name = SERVER_HOST 20 | port = PORT 21 | host = LOCAL_HOST 22 | serverAddress = (host, port) 23 | logger.info("{}-{}".format(NAME, VERSION)) 24 | logger.info("http://{}:{}/".format(name, port)) 25 | server = HTTPServer(serverAddress, RequestHandler) 26 | server.serve_forever() 27 | -------------------------------------------------------------------------------- /Server/url.py: -------------------------------------------------------------------------------- 1 | from Server.api import log, serverConfig, jdShopper, loginStatus 2 | 3 | def urls(url, request): 4 | if (url == "/log"): return log(request) 5 | elif (url == "/config"): return serverConfig(request) 6 | elif (url == "/jd-shopper"): return jdShopper(request) 7 | elif (url == "/jd-login-status"): return loginStatus(request) 8 | else: return "No Response" -------------------------------------------------------------------------------- /Static/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 404 No Found - Louis Young 5 | 6 | 125 | 126 | 130 | 131 | 132 | 137 | 138 | 139 | 140 | 141 | 142 |
143 |
144 | 145 | 146 | 147 | 148 | 149 | 150 |

404

151 |

NOT FOUND

152 | 153 | 154 |
155 |
156 |
157 | 158 |
159 | 160 | 161 | 162 | 163 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /Static/css/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aa342138039/JD-SHOPPER/7baca21df4e2e1db53067a5813d8b31ba586df34/Static/css/fonts/element-icons.woff -------------------------------------------------------------------------------- /Static/css/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | display: flex; 3 | justify-content: center; 4 | align-self: center; 5 | height: 100vh; 6 | width: 100vw; 7 | } 8 | #form_container{ 9 | width: 95vw; 10 | max-width: 600px; 11 | border: 1px solid gainsboro; 12 | height: auto; 13 | display: flex; 14 | flex-direction: column; 15 | justify-content: center; 16 | align-self: center; 17 | } 18 | h1{ 19 | text-align: center; 20 | margin: 30px 0; 21 | padding-bottom: 20px; 22 | } 23 | #log { 24 | resize: none; 25 | height: 50vh; 26 | border: none; 27 | font-size: 16px; 28 | outline: none; 29 | background-color: rgba(245, 245, 245); 30 | white-space: pre-wrap; 31 | padding: 2vh; 32 | } 33 | #account_input, #password_input{ 34 | width: 300px; 35 | } 36 | #password_form{ 37 | width: 380px; 38 | } 39 | .goods_input{ 40 | width: 300px; 41 | margin-right: 20px; 42 | } 43 | .num_select{ 44 | width: 120px; 45 | } 46 | #add_button{ 47 | margin-left: 150px; 48 | margin-bottom: 30px; 49 | } 50 | #run_button{ 51 | margin-left: 30px; 52 | } 53 | .el-dialog__body { 54 | width: 250px; 55 | height: 250px; 56 | } 57 | 58 | .el-image__inner { 59 | width: 250px; 60 | height: 250px; 61 | } -------------------------------------------------------------------------------- /Static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aa342138039/JD-SHOPPER/7baca21df4e2e1db53067a5813d8b31ba586df34/Static/favicon.ico -------------------------------------------------------------------------------- /Static/img/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aa342138039/JD-SHOPPER/7baca21df4e2e1db53067a5813d8b31ba586df34/Static/img/__init__.py -------------------------------------------------------------------------------- /Static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 订单自动提交 6 | 7 | 8 | 9 | 10 | 11 |
12 |

订单自动提交

13 | 14 | 15 | 16 | 有货自动下单 19 | 定时下单 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 47 | 54 | 55 | 56 | 62 | 63 | 64 | 70 | 71 | 72 | 78 | 79 | 重置选项 80 | 开始运行 86 | 87 | 91 | 92 | 93 | 94 | 95 | 96 | 100 | {{dialog}} 101 | 102 | 取 消 103 | 确 定 104 | 105 | 106 | 107 | 108 |
109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /Static/js/index.js: -------------------------------------------------------------------------------- 1 | new Vue({ 2 | el: "#form_container", 3 | data() { 4 | return { 5 | mode: "1", // 模式:定时下单/有货自动下单 6 | date: "", 7 | area: "", // 所在地区 8 | skurl: "", // 商品url 9 | count: "1", // 购买数量 10 | retry: "10", // 重试次数 11 | work_count: "1", // 启动线程数 12 | timeout: "30", // 超时时间 13 | eid: "", 14 | fp: "", 15 | timeSelectAble: true, 16 | dialogVisible: false, 17 | dialog: "", 18 | skuid: "", 19 | qrUrl: "./img/qr_code.png", 20 | qrVisible: false, 21 | qrReq: undefined, 22 | qrID: 0, 23 | qrReset: true, 24 | title: "错误", 25 | task: true 26 | }; 27 | }, 28 | mounted() { 29 | this.getEidFp() 30 | setTimeout(() => { 31 | this.main() 32 | }, 100) 33 | }, 34 | methods: { 35 | main() { 36 | }, 37 | upload() { 38 | if (!this.checkValid()) return 39 | let url = "0.0.0.0:12021/api/jd-shopper" 40 | let data = { 41 | mode: this.mode, 42 | date: this.date, 43 | area: this.area, 44 | skuid: this.skuid, 45 | count: this.count, 46 | retry: this.retry, 47 | work_count: this.work_count, 48 | timeout: this.timeout, 49 | eid: this.eid, 50 | fp: this.fp, 51 | }; 52 | fetch(url, { 53 | body: JSON.stringify(data), // must match 'Content-Type' header 54 | cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached 55 | credentials: 'same-origin', // include, same-origin, *omit 56 | headers: { 57 | 'user-agent': 'Mozilla/4.0 MDN Example', 58 | 'content-type': 'application/json' 59 | }, 60 | method: 'POST', // *GET, POST, PUT, DELETE, etc. 61 | mode: 'cors', // no-cors, cors, *same-origin 62 | redirect: 'follow', // manual, *follow, error 63 | referrer: 'no-referrer', // *client, no-referrer 64 | }).then(response => { 65 | return response.json() 66 | }).then(res => { 67 | console.log(res) 68 | setTimeout(() => { 69 | this.qrShow() 70 | this.loginCheck() 71 | }, 200) 72 | }) 73 | }, 74 | buyMode(value) { 75 | if (this.mode === "1" || this.mode === 1) { 76 | this.timeSelectAble = true; 77 | } else { 78 | this.timeSelectAble = false; 79 | } 80 | }, 81 | getEidFp() { 82 | let that = this 83 | setTimeout(() => { 84 | try { 85 | getJdEid(function (eid, fp, udfp) { 86 | that.eid = eid 87 | that.fp = fp 88 | }); 89 | } catch (e) { 90 | that.dialogShow("获取eid与fp失败,请手动获取。") 91 | } 92 | }, 0); 93 | }, 94 | reset() { 95 | this.mode = "1" // 模式:定时下单/有货自动下单 96 | this.date = "" 97 | this.area = "" // 所在地区 98 | this.skurl = "" // 商品url 99 | this.count = "1" // 购买数量 100 | this.retry = "10" // 重试次数 101 | this.work_count = "1" // 启动线程数 102 | this.timeout = "3" // 超时时间 103 | this.eid = "" 104 | this.fp = "" 105 | }, 106 | dialogShow(mes) { 107 | this.dialog = mes 108 | this.dialogVisible = true 109 | }, 110 | checkValid() { 111 | if (this.area == "" || this.skurl == "") { 112 | this.dialogShow("地区ID与商品链接不能为空") 113 | return false 114 | } 115 | else if (this.mode == "2" && this.date == "") { 116 | this.dialogShow("定时下单需设置时间") 117 | return false 118 | } 119 | let skuid = this.skurl.match(new RegExp(`https://item.jd.com/(.*?).html`)) 120 | skuid = skuid ? skuid[1] : null 121 | if (skuid == null) { 122 | skuid= this.skurl.replace(/[^0-9]/ig,"") 123 | reNum = /^[0-9]+.?[0-9]*/ 124 | if (!reNum.test(skuid)) { 125 | this.dialogShow("请输入正确的网址") 126 | return false 127 | } 128 | } 129 | this.skuid = skuid 130 | return true 131 | }, 132 | qrShow() { 133 | this.qrVisible = true 134 | this.qrID = 0 135 | 136 | 137 | this.qrReq = setInterval(function () { 138 | let imgDiv = document.getElementsByClassName("el-image")[0] 139 | imgDiv.removeChild(imgDiv.childNodes[0]) 140 | this.qrID++ 141 | this.qrUrl = './img/qr_code.png' 142 | this.qrReset = false 143 | }, 3000) 144 | }, 145 | 146 | loginCheck() { 147 | let url = './api/jd-login-status' 148 | let loginReq = setInterval(() => { 149 | let imgDiv = document.getElementsByClassName("el-image")[0] 150 | imgDiv.innerHTML = `` 151 | fetch(url) 152 | .then(response => { 153 | return response.json(); 154 | }) 155 | .then(res => { 156 | console.log(res); 157 | if (res.data) { 158 | this.qrVisible = false 159 | clearInterval(this.qrReq) 160 | clearInterval(loginReq) 161 | this.getLog() 162 | this.task = false 163 | } 164 | }); 165 | }, 1000) 166 | }, 167 | getLog() { 168 | let url = './api/log' 169 | 170 | fetch(url) 171 | .then(response => { 172 | return response.json(); 173 | }) 174 | .then(res => { 175 | console.log(res.data); 176 | document.getElementById('log').innerHTML = res.data 177 | }); 178 | 179 | setInterval(() => { 180 | fetch(url) 181 | .then(response => { 182 | return response.json(); 183 | }) 184 | .then(res => { 185 | console.log(res.data); 186 | document.getElementById('log').innerHTML = res.data 187 | }); 188 | }, 10000) 189 | } 190 | }, 191 | }); 192 | 193 | 194 | // confirm,e.prototype.$prompt=ya.prompt,e.prototype.$notify=tl,e.prototype.$message=ou}; 195 | //"undefined"!=typeof window&&window.Vue&&Ld(window.Vue);t.default={version:"2.15.0", 196 | //locale:j.use,i18n:j.i18n,install:Ld,CollapseTransition:ii,Loading:_l,Pagination:pt, 197 | //Dialog:gt,Autocomplete:kt,Dropdown:At,DropdownMenu:Bt,DropdownItem:Wt,Menu:ei, 198 | //Submenu:ai,MenuItem:di,MenuItemGroup:vi,Input:ne,InputNumber:_i,Radio:Si,RadioGroup:Mi, 199 | //RadioButton:Ii,Checkbox:Vi,CheckboxButton:Ri,CheckboxGroup:Yi,Switch:Xi,Select:ct, 200 | //Option:ht,OptionGroup:en,Button:Et,ButtonGroup:Pt,Table:Un,TableColumn:ir -------------------------------------------------------------------------------- /TEST/Seckill.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | import functools 4 | from lxml import etree 5 | from logger import logger 6 | from login import SpiderSession, QrLogin 7 | from message import sendMessage 8 | from timer import Timer 9 | from config import global_config 10 | from concurrent.futures import ProcessPoolExecutor 11 | from exception import SKException 12 | from util import ( 13 | parse_json, 14 | wait_some_time, 15 | ) 16 | 17 | 18 | 19 | 20 | class Seckiller(object): 21 | def __init__(self): 22 | self.spider_session = SpiderSession() 23 | self.spider_session.load_cookies_from_local() 24 | 25 | self.qrlogin = QrLogin(self.spider_session) 26 | 27 | # 初始化信息 28 | self.sku_id = global_config.getRaw('config', 'sku_id') 29 | self.seckill_num = eval(global_config.getRaw('settings', 'buy_amount')) 30 | self.seckill_init_info = dict() 31 | self.seckill_url = dict() 32 | self.seckill_order_data = dict() 33 | self.timers = Timer() 34 | 35 | self.session = self.spider_session.get_session() 36 | self.user_agent = self.spider_session.user_agent 37 | self.nick_name = None 38 | 39 | def login_by_qrcode(self): 40 | """ 41 | 二维码登陆 42 | :return: 43 | """ 44 | if self.qrlogin.is_login: 45 | logger.info('登录成功') 46 | return 47 | 48 | self.qrlogin.login_by_qrcode() 49 | 50 | if self.qrlogin.is_login: 51 | self.nick_name = self.get_username() 52 | self.spider_session.save_cookies_to_local(self.nick_name) 53 | else: 54 | raise SKException("二维码登录失败!") 55 | 56 | def check_login(func): 57 | """ 58 | 用户登陆态校验装饰器。若用户未登陆,则调用扫码登陆 59 | """ 60 | @functools.wraps(func) 61 | def new_func(self, *args, **kwargs): 62 | if not self.qrlogin.is_login: 63 | logger.info("{0} 需登陆后调用,开始扫码登陆".format(func.__name__)) 64 | self.login_by_qrcode() 65 | return func(self, *args, **kwargs) 66 | return new_func 67 | 68 | @check_login 69 | def reserve(self): 70 | """ 71 | 预约 72 | """ 73 | self._reserve() 74 | 75 | @check_login 76 | def seckill(self): 77 | """ 78 | 抢购 79 | """ 80 | self._seckill() 81 | 82 | @check_login 83 | def seckill_by_proc_pool(self): 84 | """ 85 | 多进程进行抢购 86 | work_count:进程数量 87 | """ 88 | work_count = eval(global_config.getRaw('settings', 'work_count')) 89 | with ProcessPoolExecutor(work_count) as pool: 90 | for i in range(work_count): 91 | pool.submit(self.seckill) 92 | 93 | def _reserve(self): 94 | """ 95 | 预约 96 | """ 97 | while True: 98 | try: 99 | self.make_reserve() 100 | break 101 | except Exception as e: 102 | logger.info('预约发生异常!', e) 103 | wait_some_time() 104 | 105 | def _seckill(self): 106 | """ 107 | 抢购 108 | """ 109 | while True: 110 | try: 111 | self.request_seckill_url() 112 | while True: 113 | self.request_seckill_checkout_page() 114 | self.submit_seckill_order() 115 | except Exception as e: 116 | logger.info('抢购发生异常,稍后继续执行!', e) 117 | wait_some_time() 118 | 119 | 120 | def make_reserve(self): 121 | """商品预约""" 122 | logger.info('商品名称:{}'.format(self.get_sku_title()[:40]+" ......")) 123 | url = 'https://yushou.jd.com/youshouinfo.action?' 124 | payload = { 125 | 'callback': 'fetchJSON', 126 | 'sku': self.sku_id, 127 | '_': str(int(time.time() * 1000)), 128 | } 129 | headers = { 130 | 'User-Agent': self.user_agent, 131 | 'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id), 132 | } 133 | resp = self.session.get(url=url, params=payload, headers=headers) 134 | resp_json = parse_json(resp.text) 135 | reserve_url = resp_json.get('url') 136 | self.timers.start() 137 | while True: 138 | try: 139 | self.session.get(url='https:' + reserve_url) 140 | logger.info('预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约') 141 | if global_config.getRaw('messenger', 'enable') == 'true': 142 | success_message = "预约成功,已获得抢购资格 / 您已成功预约过了,无需重复预约" 143 | sendMessage(success_message) 144 | break 145 | except Exception as e: 146 | logger.error('预约失败正在重试...') 147 | 148 | def get_username(self): 149 | """获取用户信息""" 150 | url = 'https://passport.jd.com/user/petName/getUserInfoForMiniJd.action' 151 | payload = { 152 | 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)), 153 | '_': str(int(time.time() * 1000)), 154 | } 155 | headers = { 156 | 'User-Agent': self.user_agent, 157 | 'Referer': 'https://order.jd.com/center/list.action', 158 | } 159 | 160 | resp = self.session.get(url=url, params=payload, headers=headers) 161 | 162 | try_count = 5 163 | while not resp.text.startswith("jQuery"): 164 | try_count = try_count - 1 165 | if try_count > 0: 166 | resp = self.session.get(url=url, params=payload, headers=headers) 167 | else: 168 | break 169 | wait_some_time() 170 | # 响应中包含了许多用户信息,现在在其中返回昵称 171 | # 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}}) 172 | return parse_json(resp.text).get('nickName') 173 | 174 | def get_sku_title(self): 175 | """获取商品名称""" 176 | url = 'https://item.jd.com/{}.html'.format(global_config.getRaw('config', 'sku_id')) 177 | resp = self.session.get(url).content 178 | x_data = etree.HTML(resp) 179 | sku_title = x_data.xpath('/html/head/title/text()') 180 | return sku_title[0] 181 | 182 | def get_seckill_url(self): 183 | """获取商品的抢购链接 184 | 点击"抢购"按钮后,会有两次302跳转,最后到达订单结算页面 185 | 这里返回第一次跳转后的页面url,作为商品的抢购链接 186 | :return: 商品的抢购链接 187 | """ 188 | url = 'https://itemko.jd.com/itemShowBtn' 189 | payload = { 190 | 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)), 191 | 'skuId': self.sku_id, 192 | 'from': 'pc', 193 | '_': str(int(time.time() * 1000)), 194 | } 195 | headers = { 196 | 'User-Agent': self.user_agent, 197 | 'Host': 'itemko.jd.com', 198 | 'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id), 199 | } 200 | while True: 201 | resp = self.session.get(url=url, headers=headers, params=payload) 202 | resp_json = parse_json(resp.text) 203 | if resp_json.get('url'): 204 | # https://divide.jd.com/user_routing?skuId=8654289&sn=c3f4ececd8461f0e4d7267e96a91e0e0&from=pc 205 | router_url = 'https:' + resp_json.get('url') 206 | # https://marathon.jd.com/captcha.html?skuId=8654289&sn=c3f4ececd8461f0e4d7267e96a91e0e0&from=pc 207 | seckill_url = router_url.replace( 208 | 'divide', 'marathon').replace( 209 | 'user_routing', 'captcha.html') 210 | logger.info("抢购链接获取成功: %s", seckill_url) 211 | return seckill_url 212 | else: 213 | logger.info("抢购链接获取失败,稍后自动重试") 214 | wait_some_time() 215 | 216 | def request_seckill_url(self): 217 | """访问商品的抢购链接(用于设置cookie等""" 218 | logger.info('用户:{}'.format(self.get_username())) 219 | logger.info('商品名称:{}'.format(self.get_sku_title())) 220 | self.timers.start() 221 | self.seckill_url[self.sku_id] = self.get_seckill_url() 222 | logger.info('访问商品的抢购连接...') 223 | headers = { 224 | 'User-Agent': self.user_agent, 225 | 'Host': 'marathon.jd.com', 226 | 'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id), 227 | } 228 | self.session.get( 229 | url=self.seckill_url.get( 230 | self.sku_id), 231 | headers=headers, 232 | allow_redirects=False) 233 | 234 | def request_seckill_checkout_page(self): 235 | """访问抢购订单结算页面""" 236 | logger.info('访问抢购订单结算页面...') 237 | url = 'https://marathon.jd.com/seckill/seckill.action' 238 | payload = { 239 | 'skuId': self.sku_id, 240 | 'num': self.seckill_num, 241 | 'rid': int(time.time()) 242 | } 243 | headers = { 244 | 'User-Agent': self.user_agent, 245 | 'Host': 'marathon.jd.com', 246 | 'Referer': 'https://item.jd.com/{}.html'.format(self.sku_id), 247 | } 248 | self.session.get(url=url, params=payload, headers=headers, allow_redirects=False) 249 | 250 | def _get_seckill_init_info(self): 251 | """获取秒杀初始化信息(包括:地址,发票,token) 252 | :return: 初始化信息组成的dict 253 | """ 254 | logger.info('获取秒杀初始化信息...') 255 | url = 'https://marathon.jd.com/seckillnew/orderService/pc/init.action' 256 | data = { 257 | 'sku': self.sku_id, 258 | 'num': self.seckill_num, 259 | 'isModifyAddress': 'false', 260 | } 261 | headers = { 262 | 'User-Agent': self.user_agent, 263 | 'Host': 'marathon.jd.com', 264 | } 265 | resp = self.session.post(url=url, data=data, headers=headers) 266 | 267 | resp_json = None 268 | try: 269 | resp_json = parse_json(resp.text) 270 | except Exception: 271 | raise SKException('抢购失败,返回信息:{}'.format(resp.text)) 272 | 273 | return resp_json 274 | 275 | def _get_seckill_order_data(self): 276 | """生成提交抢购订单所需的请求体参数 277 | :return: 请求体参数组成的dict 278 | """ 279 | logger.info('生成提交抢购订单所需参数...') 280 | # 获取用户秒杀初始化信息 281 | self.seckill_init_info[self.sku_id] = self._get_seckill_init_info() 282 | init_info = self.seckill_init_info.get(self.sku_id) 283 | default_address = init_info['addressList'][0] # 默认地址dict 284 | invoice_info = init_info.get('invoiceInfo', {}) # 默认发票信息dict, 有可能不返回 285 | token = init_info['token'] 286 | data = { 287 | 'skuId': self.sku_id, 288 | 'num': self.seckill_num, 289 | 'addressId': default_address['id'], 290 | 'yuShou': 'true', 291 | 'isModifyAddress': 'false', 292 | 'name': default_address['name'], 293 | 'provinceId': default_address['provinceId'], 294 | 'cityId': default_address['cityId'], 295 | 'countyId': default_address['countyId'], 296 | 'townId': default_address['townId'], 297 | 'addressDetail': default_address['addressDetail'], 298 | 'mobile': default_address['mobile'], 299 | 'mobileKey': default_address['mobileKey'], 300 | 'email': default_address.get('email', ''), 301 | 'postCode': '', 302 | 'invoiceTitle': invoice_info.get('invoiceTitle', -1), 303 | 'invoiceCompanyName': '', 304 | 'invoiceContent': invoice_info.get('invoiceContentType', 1), 305 | 'invoiceTaxpayerNO': '', 306 | 'invoiceEmail': '', 307 | 'invoicePhone': invoice_info.get('invoicePhone', ''), 308 | 'invoicePhoneKey': invoice_info.get('invoicePhoneKey', ''), 309 | 'invoice': 'true' if invoice_info else 'false', 310 | 'password': global_config.get('account', 'payment_pwd'), 311 | 'codTimeType': 3, 312 | 'paymentType': 4, 313 | 'areaCode': '', 314 | 'overseas': 0, 315 | 'phone': '', 316 | 'eid': global_config.getRaw('config', 'eid'), 317 | 'fp': global_config.getRaw('config', 'fp'), 318 | 'token': token, 319 | 'pru': '' 320 | } 321 | 322 | return data 323 | 324 | def submit_seckill_order(self): 325 | """提交抢购(秒杀)订单 326 | :return: 抢购结果 True/False 327 | """ 328 | url = 'https://marathon.jd.com/seckillnew/orderService/pc/submitOrder.action' 329 | payload = { 330 | 'skuId': self.sku_id, 331 | } 332 | try: 333 | self.seckill_order_data[self.sku_id] = self._get_seckill_order_data() 334 | except Exception as e: 335 | logger.info('抢购失败,无法获取生成订单的基本信息,接口返回:【{}】'.format(str(e))) 336 | return False 337 | 338 | logger.info('提交抢购订单...') 339 | headers = { 340 | 'User-Agent': self.user_agent, 341 | 'Host': 'marathon.jd.com', 342 | 'Referer': 'https://marathon.jd.com/seckill/seckill.action?skuId={0}&num={1}&rid={2}'.format( 343 | self.sku_id, self.seckill_num, int(time.time())), 344 | } 345 | resp = self.session.post( 346 | url=url, 347 | params=payload, 348 | data=self.seckill_order_data.get( 349 | self.sku_id), 350 | headers=headers) 351 | resp_json = None 352 | try: 353 | resp_json = parse_json(resp.text) 354 | except Exception as e: 355 | logger.info('抢购失败,返回信息:{}'.format(resp.text[0: 128])) 356 | return False 357 | # 返回信息 358 | # 抢购失败: 359 | # {'errorMessage': '很遗憾没有抢到,再接再厉哦。', 'orderId': 0, 'resultCode': 60074, 'skuId': 0, 'success': False} 360 | # {'errorMessage': '抱歉,您提交过快,请稍后再提交订单!', 'orderId': 0, 'resultCode': 60017, 'skuId': 0, 'success': False} 361 | # {'errorMessage': '系统正在开小差,请重试~~', 'orderId': 0, 'resultCode': 90013, 'skuId': 0, 'success': False} 362 | # 抢购成功: 363 | # {"appUrl":"xxxxx","orderId":820227xxxxx,"pcUrl":"xxxxx","resultCode":0,"skuId":0,"success":true,"totalMoney":"xxxxx"} 364 | if resp_json.get('success'): 365 | order_id = resp_json.get('orderId') 366 | total_money = resp_json.get('totalMoney') 367 | pay_url = 'https:' + resp_json.get('pcUrl') 368 | logger.info('抢购成功,订单号:{}, 总价:{}, 电脑端付款链接:{}'.format(order_id, total_money, pay_url)) 369 | if global_config.getRaw('messenger', 'enable') == 'true': 370 | success_message = "抢购成功,订单号:{}, 总价:{}, 电脑端付款链接:{}".format(order_id, total_money, pay_url) 371 | sendMessage(success_message) 372 | return True 373 | else: 374 | logger.info('抢购失败,返回信息:{}'.format(resp_json)) 375 | if global_config.getRaw('messenger', 'enable') == 'true': 376 | error_message = '抢购失败,返回信息:{}'.format(resp_json) 377 | sendMessage(error_message) 378 | return False 379 | 380 | 381 | -------------------------------------------------------------------------------- /TEST/area_id/1.北京.txt: -------------------------------------------------------------------------------- 1 | { 2 | '北京(1)': { 3 | '朝阳区(72)': { 4 | '定福庄(4211)': '1_72_4211', 5 | '三环以内(2799)': '1_72_2799', 6 | '四环到五环之间(2839)': '1_72_2839', 7 | '北苑(4139)': '1_72_4139', 8 | '三环到四环之间(2819)': '1_72_2819', 9 | '管庄(4137)': '1_72_4137', 10 | '五环到六环之间(2840)': '1_72_2840' 11 | }, 12 | '丰台区(2805)': { 13 | '六环之外(34545)': '1_2805_34545', 14 | '五环到六环之间(34544)': '1_2805_34544', 15 | '三环到四环之间(2855)': '1_2805_2855', 16 | '四环到五环之间(2832)': '1_2805_2832', 17 | '二环到三环(2854)': '1_2805_2854' 18 | }, 19 | '门头沟(2807)': { 20 | '龙泉镇(51553)': '1_2807_51553', 21 | '雁翅镇(51560)': '1_2807_51560', 22 | '永定镇(51554)': '1_2807_51554', 23 | '潭柘寺镇(51556)': '1_2807_51556', 24 | '王平镇(51557)': '1_2807_51557', 25 | '大台镇(51555)': '1_2807_51555', 26 | '斋堂镇(51561)': '1_2807_51561', 27 | '城区(51552)': '1_2807_51552', 28 | '妙峰山镇(51559)': '1_2807_51559', 29 | '军庄镇(51558)': '1_2807_51558', 30 | '清水镇(51562)': '1_2807_51562' 31 | }, 32 | '房山区(2808)': { 33 | '长沟镇(51549)': '1_2808_51549', 34 | '城区(51528)': '1_2808_51528', 35 | '周口店镇(51551)': '1_2808_51551', 36 | '蒲洼乡(51539)': '1_2808_51539', 37 | '青龙湖镇(51540)': '1_2808_51540', 38 | '窦店镇(51531)': '1_2808_51531', 39 | '石楼镇(51542)': '1_2808_51542', 40 | '良乡镇(51536)': '1_2808_51536', 41 | '阎村镇(51546)': '1_2808_51546', 42 | '霞云岭乡(51544)': '1_2808_51544', 43 | '大安山乡(51529)': '1_2808_51529', 44 | '十渡镇(51541)': '1_2808_51541', 45 | '燕山地区(51547)': '1_2808_51547', 46 | '琉璃河镇(51537)': '1_2808_51537', 47 | '张坊镇(51548)': '1_2808_51548', 48 | '韩村河镇(51534)': '1_2808_51534', 49 | '长阳镇(51550)': '1_2808_51550', 50 | '南窖乡(51538)': '1_2808_51538', 51 | '佛子庄乡(51532)': '1_2808_51532', 52 | '河北镇(51535)': '1_2808_51535', 53 | '新镇(51545)': '1_2808_51545', 54 | '大石窝镇(51530)': '1_2808_51530', 55 | '史家营乡(51543)': '1_2808_51543' 56 | }, 57 | '延庆县(3065)': { 58 | '香营乡(51519)': '1_3065_51519', 59 | '永宁镇(51509)': '1_3065_51509', 60 | '延庆镇(51505)': '1_3065_51505', 61 | '井庄镇(51516)': '1_3065_51516', 62 | '大榆树镇(51515)': '1_3065_51515', 63 | '四海镇(51512)': '1_3065_51512', 64 | '千家店镇(51513)': '1_3065_51513', 65 | '张山营镇(51511)': '1_3065_51511', 66 | '城区(51506)': '1_3065_51506', 67 | '刘斌堡乡(51518)': '1_3065_51518', 68 | '沈家营镇(51514)': '1_3065_51514', 69 | '旧县镇(51510)': '1_3065_51510', 70 | '八达岭镇(51508)': '1_3065_51508', 71 | '大庄科乡(51517)': '1_3065_51517', 72 | '康庄镇(51507)': '1_3065_51507', 73 | '珍珠泉乡(51520)': '1_3065_51520' 74 | }, 75 | '昌平区(2901)': { 76 | '城区以外(2906)': '1_2901_2906', 77 | '城区(4136)': '1_2901_4136', 78 | '六环以内(4135)': '1_2901_4135' 79 | }, 80 | '通州区(2809)': { 81 | '永顺镇(51218)': '1_2809_51218', 82 | '梨园镇(51219)': '1_2809_51219', 83 | '中仓街道(51228)': '1_2809_51228', 84 | '永乐店镇(51224)': '1_2809_51224', 85 | '玉桥街道(51230)': '1_2809_51230', 86 | '新华街道(51229)': '1_2809_51229', 87 | '宋庄镇(51220)': '1_2809_51220', 88 | '次渠镇(51232)': '1_2809_51232', 89 | '潞城镇(51225)': '1_2809_51225', 90 | '台湖镇(51226)': '1_2809_51226', 91 | '于家务乡(51227)': '1_2809_51227', 92 | '六环内(马驹桥镇)(51216)': '1_2809_51216', 93 | '西集镇(51223)': '1_2809_51223', 94 | '漷县镇(51221)': '1_2809_51221', 95 | '张家湾镇(51222)': '1_2809_51222', 96 | '北苑街道(51231)': '1_2809_51231', 97 | '六环外(马驹桥镇)(51217)': '1_2809_51217' 98 | }, 99 | '西城区(2801)': { 100 | '二环到三环(2853)': '1_2801_2853', 101 | '内环到二环里(2827)': '1_2801_2827' 102 | }, 103 | '海淀区(2800)': { 104 | '西三旗(4134)': '1_2800_4134', 105 | '四环到五环之间(2850)': '1_2800_2850', 106 | '三环到四环之间(2849)': '1_2800_2849', 107 | '五环到六环之间(2851)': '1_2800_2851', 108 | '六环以外(2852)': '1_2800_2852', 109 | '三环以内(2848)': '1_2800_2848', 110 | '西二旗(4209)': '1_2800_4209' 111 | }, 112 | '石景山区(2806)': { 113 | '八大处科技园区(4188)': '1_2806_4188', 114 | '四环到五环内(2831)': '1_2806_2831', 115 | '石景山城区(4187)': '1_2806_4187' 116 | }, 117 | '怀柔区(2814)': { 118 | '城区以内(6115)': '1_2814_6115', 119 | '郊区(2847)': '1_2814_2847' 120 | }, 121 | '顺义区(2812)': { 122 | '赵全营镇(51149)': '1_2812_51149', 123 | '北务镇(51126)': '1_2812_51126', 124 | '胜利街道(51142)': '1_2812_51142', 125 | '石园街道(51143)': '1_2812_51143', 126 | '高丽营镇(51129)': '1_2812_51129', 127 | '张镇(51148)': '1_2812_51148', 128 | '天竺地区(51145)': '1_2812_51145', 129 | '大孙各庄镇(51128)': '1_2812_51128', 130 | '南彩镇(51138)': '1_2812_51138', 131 | '李遂镇(51134)': '1_2812_51134', 132 | '仁和地区(51141)': '1_2812_51141', 133 | '后沙峪地区(51131)': '1_2812_51131', 134 | '木林镇(51137)': '1_2812_51137', 135 | '空港街道(51132)': '1_2812_51132', 136 | '北小营镇(51127)': '1_2812_51127', 137 | '旺泉街道(51146)': '1_2812_51146', 138 | '北石槽镇(51125)': '1_2812_51125', 139 | '南法信地区(51139)': '1_2812_51139', 140 | '双丰街道(51144)': '1_2812_51144', 141 | '龙湾屯镇(51135)': '1_2812_51135', 142 | '牛栏山地区(51140)': '1_2812_51140', 143 | '杨镇地区(51147)': '1_2812_51147', 144 | '李桥镇(51133)': '1_2812_51133', 145 | '光明街道(51130)': '1_2812_51130', 146 | '马坡地区(51136)': '1_2812_51136' 147 | }, 148 | '大兴区(2810)': { 149 | '亦庄经济开发区(51081)': '1_2810_51081', 150 | '五环至六环之间(6501)': '1_2810_6501', 151 | '四环至五环之间(4194)': '1_2810_4194', 152 | '六环以外(4205)': '1_2810_4205' 153 | }, 154 | '崇文区(2803)': { 155 | '二环到三环(2842)': '1_2803_2842', 156 | '一环到二环(2829)': '1_2803_2829' 157 | }, 158 | '东城区(2802)': { 159 | '内环到三环里(2821)': '1_2802_2821' 160 | }, 161 | '宣武区(2804)': { 162 | '内环到三环里(2828)': '1_2804_2828' 163 | }, 164 | '平谷区(2953)': { 165 | '城区(6666)': '1_2953_6666', 166 | '城区以外(2954)': '1_2953_2954' 167 | }, 168 | '密云区(2816)': { 169 | '城区(6667)': '1_2816_6667', 170 | '城区以外(2862)': '1_2816_2862' 171 | } 172 | } 173 | } -------------------------------------------------------------------------------- /TEST/area_id/2.上海.txt: -------------------------------------------------------------------------------- 1 | { 2 | '上海(2)': { 3 | '松江区(2834)': { 4 | '石湖荡镇(51990)': '2_2834_51990', 5 | '泖港镇(51989)': '2_2834_51989', 6 | '佘山镇(51984)': '2_2834_51984', 7 | '小昆山镇(51993)': '2_2834_51993', 8 | '新浜镇(51991)': '2_2834_51991', 9 | '城区(51982)': '2_2834_51982', 10 | '新桥镇(51986)': '2_2834_51986', 11 | '车墩镇(51985)': '2_2834_51985', 12 | '九亭镇(51988)': '2_2834_51988', 13 | '洞泾镇(51987)': '2_2834_51987', 14 | '叶榭镇(51992)': '2_2834_51992', 15 | '泗泾镇(51983)': '2_2834_51983' 16 | }, 17 | '杨浦区(2823)': { 18 | '城区(51974)': '2_2823_51974' 19 | }, 20 | '闵行区(2825)': { 21 | '虹桥镇(51936)': '2_2825_51936', 22 | '城区(51931)': '2_2825_51931', 23 | '莘庄镇(51932)': '2_2825_51932', 24 | '马桥镇(51937)': '2_2825_51937', 25 | '七宝镇(51933)': '2_2825_51933', 26 | '颛桥镇(51940)': '2_2825_51940', 27 | '浦江镇(51934)': '2_2825_51934', 28 | '梅陇镇(51935)': '2_2825_51935', 29 | '华漕镇(51939)': '2_2825_51939', 30 | '吴泾镇(51938)': '2_2825_51938' 31 | }, 32 | '徐汇区(2813)': { 33 | '城区(51976)': '2_2813_51976' 34 | }, 35 | '嘉定区(2826)': { 36 | '嘉定工业区(51950)': '2_2826_51950', 37 | '城区(51941)': '2_2826_51941', 38 | '江桥镇(51945)': '2_2826_51945', 39 | '华亭镇(51944)': '2_2826_51944', 40 | '徐行镇(51948)': '2_2826_51948', 41 | '菊园新区(51946)': '2_2826_51946', 42 | '马陆镇(51943)': '2_2826_51943', 43 | '外冈镇(51949)': '2_2826_51949', 44 | '南翔镇(51942)': '2_2826_51942', 45 | '安亭镇(51947)': '2_2826_51947' 46 | }, 47 | '崇明县(2919)': { 48 | '东平镇(50783)': '2_2919_50783', 49 | '新河镇(50792)': '2_2919_50792', 50 | '陈家镇(50781)': '2_2919_50781', 51 | '庙镇(50780)': '2_2919_50780', 52 | '港沿镇(50785)': '2_2919_50785', 53 | '港西镇(50784)': '2_2919_50784', 54 | '向化镇(50790)': '2_2919_50790', 55 | '绿华镇(50787)': '2_2919_50787', 56 | '中兴镇(50793)': '2_2919_50793', 57 | '堡镇(50779)': '2_2919_50779', 58 | '建设镇(50786)': '2_2919_50786', 59 | '新海镇(50791)': '2_2919_50791', 60 | '三星镇(50788)': '2_2919_50788', 61 | '长兴乡(50794)': '2_2919_50794', 62 | '新村乡(50796)': '2_2919_50796', 63 | '横沙乡(50795)': '2_2919_50795', 64 | '竖新镇(50789)': '2_2919_50789', 65 | '城桥镇(50782)': '2_2919_50782' 66 | }, 67 | '青浦区(2833)': { 68 | '徐泾镇(51953)': '2_2833_51953', 69 | '重固镇(51955)': '2_2833_51955', 70 | '朱家角镇(51951)': '2_2833_51951', 71 | '华新镇(51954)': '2_2833_51954', 72 | '赵巷镇(51952)': '2_2833_51952', 73 | '白鹤镇(51956)': '2_2833_51956', 74 | '练塘镇(51957)': '2_2833_51957', 75 | '城区(51959)': '2_2833_51959', 76 | '金泽镇(51958)': '2_2833_51958' 77 | }, 78 | '普陀区(2841)': { 79 | '城区(51980)': '2_2841_51980' 80 | }, 81 | '浦东新区(2830)': { 82 | '芦潮港镇(51830)': '2_2830_51830', 83 | '惠南镇(51824)': '2_2830_51824', 84 | '老港镇(51834)': '2_2830_51834', 85 | '高东镇(51809)': '2_2830_51809', 86 | '川沙新镇(51801)': '2_2830_51801', 87 | '航头镇(51832)': '2_2830_51832', 88 | '张江镇(51810)': '2_2830_51810', 89 | '泥城镇(51831)': '2_2830_51831', 90 | '康桥镇(51825)': '2_2830_51825', 91 | '合庆镇(51804)': '2_2830_51804', 92 | '周浦镇(51829)': '2_2830_51829', 93 | '书院镇(51827)': '2_2830_51827', 94 | '三林镇(51811)': '2_2830_51811', 95 | '万祥镇(51833)': '2_2830_51833', 96 | '大团镇(51828)': '2_2830_51828', 97 | '祝桥镇(51822)': '2_2830_51822', 98 | '高桥镇(51802)': '2_2830_51802', 99 | '金桥镇(51807)': '2_2830_51807', 100 | '南汇新城镇(51812)': '2_2830_51812', 101 | '北蔡镇(51803)': '2_2830_51803', 102 | '曹路镇(51806)': '2_2830_51806', 103 | '唐镇(51805)': '2_2830_51805', 104 | '新场镇(51823)': '2_2830_51823', 105 | '城区(51800)': '2_2830_51800', 106 | '高行镇(51808)': '2_2830_51808', 107 | '宣桥镇(51826)': '2_2830_51826' 108 | }, 109 | '闸北区(2820)': { 110 | '城区(51972)': '2_2820_51972' 111 | }, 112 | '奉贤区(2837)': { 113 | '金汇镇(51996)': '2_2837_51996', 114 | '奉城镇(51929)': '2_2837_51929', 115 | '海湾镇(51998)': '2_2837_51998', 116 | '柘林镇(51994)': '2_2837_51994', 117 | '南桥镇(51928)': '2_2837_51928', 118 | '庄行镇(51995)': '2_2837_51995', 119 | '青村镇(51997)': '2_2837_51997', 120 | '四团镇(51930)': '2_2837_51930' 121 | }, 122 | '宝山区(2824)': { 123 | '罗泾镇(51915)': '2_2824_51915', 124 | '高境镇(51917)': '2_2824_51917', 125 | '淞南镇(51919)': '2_2824_51919', 126 | '月浦镇(51914)': '2_2824_51914', 127 | '庙行镇(51918)': '2_2824_51918', 128 | '宝山城市工业园区(51920)': '2_2824_51920', 129 | '顾村镇(51916)': '2_2824_51916', 130 | '罗店镇(51911)': '2_2824_51911', 131 | '杨行镇(51913)': '2_2824_51913', 132 | '城区(51921)': '2_2824_51921', 133 | '大场镇(51912)': '2_2824_51912' 134 | }, 135 | '虹口区(2822)': { 136 | '城区(51979)': '2_2822_51979' 137 | }, 138 | '长宁区(2815)': { 139 | '城区(51975)': '2_2815_51975' 140 | }, 141 | '静安区(2817)': { 142 | '城区(51973)': '2_2817_51973' 143 | }, 144 | '黄浦区(78)': { 145 | '城区(51978)': '2_78_51978' 146 | }, 147 | '金山区(2835)': { 148 | '城区(51960)': '2_2835_51960', 149 | '朱泾镇(51962)': '2_2835_51962', 150 | '枫泾镇(51963)': '2_2835_51963', 151 | '金山卫镇(51968)': '2_2835_51968', 152 | '漕泾镇(51970)': '2_2835_51970', 153 | '金山工业区(51961)': '2_2835_51961', 154 | '吕巷镇(51966)': '2_2835_51966', 155 | '亭林镇(51965)': '2_2835_51965', 156 | '张堰镇(51964)': '2_2835_51964', 157 | '廊下镇(51967)': '2_2835_51967', 158 | '山阳镇(51971)': '2_2835_51971' 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /TEST/area_id/23.海南.txt: -------------------------------------------------------------------------------- 1 | { 2 | '海南(23)': { 3 | '三亚市(3690)': { 4 | '崖州区(4182)': { 5 | '崖城镇(53698)': '23_3690_4182_53698', 6 | '国营南滨农场(53697)': '23_3690_4182_53697' 7 | }, 8 | '海棠区(3693)': { 9 | '海棠湾镇(53696)': '23_3690_3693_53696', 10 | '国营南田农场(53695)': '23_3690_3693_53695' 11 | }, 12 | '吉阳区(3694)': { 13 | '吉阳镇(53734)': '23_3690_3694_53734', 14 | '国营南新农场(53700)': '23_3690_3694_53700', 15 | '河东区街道(53699)': '23_3690_3694_53699' 16 | }, 17 | '天涯区(3696)': { 18 | '凤凰镇(53701)': '23_3690_3696_53701', 19 | '育才镇(53703)': '23_3690_3696_53703', 20 | '国营南岛农场(53706)': '23_3690_3696_53706', 21 | '国营立才农场(53705)': '23_3690_3696_53705', 22 | '天涯镇(53702)': '23_3690_3696_53702', 23 | '河西区街道(53704)': '23_3690_3696_53704' 24 | } 25 | }, 26 | '定安县(3703)': { 27 | '龙门镇(3815)': '23_3703_3815', 28 | '富文镇(3819)': '23_3703_3819', 29 | '龙河镇(3816)': '23_3703_3816', 30 | '定城镇(3811)': '23_3703_3811', 31 | '金鸡岭农场(12782)': '23_3703_12782', 32 | '龙湖镇(3813)': '23_3703_3813', 33 | '黄竹镇(4498)': '23_3703_4498', 34 | '城区(39887)': '23_3703_39887', 35 | '新竹镇(3812)': '23_3703_3812', 36 | '翰林镇(3818)': '23_3703_3818', 37 | '中瑞农场(12783)': '23_3703_12783', 38 | '南海农场(12784)': '23_3703_12784', 39 | '雷鸣镇(3814)': '23_3703_3814', 40 | '岭口镇(3817)': '23_3703_3817' 41 | }, 42 | '乐东县(3710)': { 43 | '黄流镇(3854)': '23_3710_3854', 44 | '尖峰岭林业公司(12801)': '23_3710_12801', 45 | '利国镇(3853)': '23_3710_3853', 46 | '莺歌海盐场(12802)': '23_3710_12802', 47 | '抱由镇(3845)': '23_3710_3845', 48 | '城区(39897)': '23_3710_39897', 49 | '保国农场(12805)': '23_3710_12805', 50 | '佛罗镇(3855)': '23_3710_3855', 51 | '万冲镇(3846)': '23_3710_3846', 52 | '千家镇(3851)': '23_3710_3851', 53 | '莺歌海镇(3857)': '23_3710_3857', 54 | '尖峰镇(3856)': '23_3710_3856', 55 | '大安镇(3847)': '23_3710_3847', 56 | '乐光农场(12804)': '23_3710_12804', 57 | '志仲镇(3849)': '23_3710_3849', 58 | '九所镇(3852)': '23_3710_3852', 59 | '山荣农场(12803)': '23_3710_12803' 60 | }, 61 | '临高县(3701)': { 62 | '多文镇(3795)': '23_3701_3795', 63 | '加来农场(12792)': '23_3701_12792', 64 | '波莲镇(3791)': '23_3701_3791', 65 | '东英镇(3792)': '23_3701_3792', 66 | '城区(39884)': '23_3701_39884', 67 | '和舍镇(3796)': '23_3701_3796', 68 | '新盈镇(3798)': '23_3701_3798', 69 | '加来镇(3800)': '23_3701_3800', 70 | '博厚镇(3793)': '23_3701_3793', 71 | '南宝镇(3797)': '23_3701_3797', 72 | '临城镇(3790)': '23_3701_3790', 73 | '皇桐镇(3794)': '23_3701_3794', 74 | '红华农场(12791)': '23_3701_12791', 75 | '调楼镇(3799)': '23_3701_3799' 76 | }, 77 | '东方市(3173)': { 78 | '八所镇(3780)': '23_3173_3780', 79 | '天安乡(3788)': '23_3173_3788', 80 | '东河镇(3781)': '23_3173_3781', 81 | '感城镇(3783)': '23_3173_3783', 82 | '广坝农场(12780)': '23_3173_12780', 83 | '四更镇(3786)': '23_3173_3786', 84 | '江边乡(3789)': '23_3173_3789', 85 | '新龙镇(3787)': '23_3173_3787', 86 | '东方华侨农场(12781)': '23_3173_12781', 87 | '大田镇(3782)': '23_3173_3782', 88 | '板桥镇(3784)': '23_3173_3784', 89 | '三家镇(3785)': '23_3173_3785' 90 | }, 91 | '万宁市(3137)': { 92 | '山根镇(3776)': '23_3137_3776', 93 | '六连林场(12775)': '23_3137_12775', 94 | '三更罗镇(3779)': '23_3137_3779', 95 | '大茂镇(3772)': '23_3137_3772', 96 | '东兴农场(12776)': '23_3137_12776', 97 | '北大镇(3777)': '23_3137_3777', 98 | '和乐镇(3770)': '23_3137_3770', 99 | '兴隆华侨农场(12779)': '23_3137_12779', 100 | '礼纪镇(3774)': '23_3137_3774', 101 | '新中农场(12778)': '23_3137_12778', 102 | '东和农场(12777)': '23_3137_12777', 103 | '兴隆镇(53111)': '23_3137_53111', 104 | '南林农场(53112)': '23_3137_53112', 105 | '万城镇(3768)': '23_3137_3768', 106 | '南桥镇(3778)': '23_3137_3778', 107 | '后安镇(3771)': '23_3137_3771', 108 | '长丰镇(3775)': '23_3137_3775', 109 | '龙滚镇(3769)': '23_3137_3769', 110 | '东澳镇(3773)': '23_3137_3773' 111 | }, 112 | '海口市(2121)': { 113 | '美兰区(22469)': { 114 | '三江镇(22692)': '23_2121_22469_22692', 115 | '演丰镇(22691)': '23_2121_22469_22691', 116 | '罗牛山农场(22695)': '23_2121_22469_22695', 117 | '桂林洋高校区(22696)': '23_2121_22469_22696', 118 | '桂林洋农场(22694)': '23_2121_22469_22694', 119 | '灵山镇(22690)': '23_2121_22469_22690', 120 | '城区(51475)': '23_2121_22469_51475', 121 | '三江农场(22693)': '23_2121_22469_22693', 122 | '大致坡镇(22689)': '23_2121_22469_22689' 123 | }, 124 | '秀英区(22466)': { 125 | '西秀镇(22651)': '23_2121_22466_22651', 126 | '海秀镇(22652)': '23_2121_22466_22652', 127 | '城区(51472)': '23_2121_22466_51472', 128 | '石山镇(22653)': '23_2121_22466_22653', 129 | '东山镇(22655)': '23_2121_22466_22655', 130 | '永兴镇(22654)': '23_2121_22466_22654', 131 | '长流镇(22650)': '23_2121_22466_22650' 132 | }, 133 | '龙华区(22467)': { 134 | '新坡镇(22664)': '23_2121_22467_22664', 135 | '龙泉镇(22666)': '23_2121_22467_22666', 136 | '遵谭镇(22665)': '23_2121_22467_22665', 137 | '城西镇(22662)': '23_2121_22467_22662', 138 | '城区(51473)': '23_2121_22467_51473', 139 | '龙桥镇(22663)': '23_2121_22467_22663' 140 | }, 141 | '琼山区(22468)': { 142 | '城区(51474)': '23_2121_22468_51474', 143 | '省岭脚作物场(22679)': '23_2121_22468_22679', 144 | '府城镇(22669)': '23_2121_22468_22669', 145 | '红明农场(22676)': '23_2121_22468_22676', 146 | '旧州镇(22675)': '23_2121_22468_22675', 147 | '红旗镇(22672)': '23_2121_22468_22672', 148 | '甲子镇(22674)': '23_2121_22468_22674', 149 | '长昌煤矿(22678)': '23_2121_22468_22678', 150 | '云龙镇(22671)': '23_2121_22468_22671', 151 | '大坡镇(22673)': '23_2121_22468_22673', 152 | '东昌农场(22677)': '23_2121_22468_22677', 153 | '龙塘镇(22670)': '23_2121_22468_22670', 154 | '三门坡镇(22668)': '23_2121_22468_22668' 155 | } 156 | }, 157 | '琼中县(3707)': { 158 | '什运乡(3886)': '23_3707_3886', 159 | '长征农场(12818)': '23_3707_12818', 160 | '加钗农场(12817)': '23_3707_12817', 161 | '乌石农场(12816)': '23_3707_12816', 162 | '和平镇(3881)': '23_3707_3881', 163 | '城区(39893)': '23_3707_39893', 164 | '营根镇(3878)': '23_3707_3878', 165 | '黎母山林业公司(12814)': '23_3707_12814', 166 | '上安乡(3885)': '23_3707_3885', 167 | '阳江农场(12815)': '23_3707_12815', 168 | '湾岭镇(3879)': '23_3707_3879', 169 | '黎母山镇(3880)': '23_3707_3880', 170 | '吊罗山乡(12813)': '23_3707_12813', 171 | '长征镇(3882)': '23_3707_3882', 172 | '中平镇(3884)': '23_3707_3884', 173 | '红毛镇(3883)': '23_3707_3883' 174 | }, 175 | '白沙县(3706)': { 176 | '牙叉镇(3834)': '23_3706_3834', 177 | '金波乡(3843)': '23_3706_3843', 178 | '城区(39892)': '23_3706_39892', 179 | '元门乡(3839)': '23_3706_3839', 180 | '荣邦乡(3844)': '23_3706_3844', 181 | '邦溪镇(3836)': '23_3706_3836', 182 | '南开乡(3840)': '23_3706_3840', 183 | '阜龙乡(3841)': '23_3706_3841', 184 | '细水乡(3838)': '23_3706_3838', 185 | '白沙农场(12793)': '23_3706_12793', 186 | '龙江农场(12794)': '23_3706_12794', 187 | '打安镇(3837)': '23_3706_3837', 188 | '七坊镇(3835)': '23_3706_3835', 189 | '邦溪农场(12795)': '23_3706_12795', 190 | '青松乡(3842)': '23_3706_3842' 191 | }, 192 | '儋州市(3034)': { 193 | '和庆镇(3733)': '23_3034_3733', 194 | '海头镇(3740)': '23_3034_3740', 195 | '大成镇(3735)': '23_3034_3735', 196 | '木棠镇(3739)': '23_3034_3739', 197 | '富克镇(4214)': '23_3034_4214', 198 | '红岭农场(12761)': '23_3034_12761', 199 | '那大镇(3125)': '23_3034_3125', 200 | '洋浦经济开发区(3751)': '23_3034_3751', 201 | '兰洋镇(3737)': '23_3034_3737', 202 | '光村镇(3738)': '23_3034_3738', 203 | '西流农场(12758)': '23_3034_12758', 204 | '东成镇(3749)': '23_3034_3749', 205 | '龙山农场(12760)': '23_3034_12760', 206 | '三都镇(3744)': '23_3034_3744', 207 | '白马井镇(3746)': '23_3034_3746', 208 | '中和镇(3747)': '23_3034_3747', 209 | '西培农场(12752)': '23_3034_12752', 210 | '西庆农场(12757)': '23_3034_12757', 211 | '西联农场(12753)': '23_3034_12753', 212 | '新盈农场(12759)': '23_3034_12759', 213 | '南丰镇(3734)': '23_3034_3734', 214 | '西华农场(12756)': '23_3034_12756', 215 | '八一农场(12755)': '23_3034_12755', 216 | '峨蔓镇(3741)': '23_3034_3741', 217 | '王五镇(3745)': '23_3034_3745', 218 | '新州镇(3750)': '23_3034_3750', 219 | '雅星镇(3736)': '23_3034_3736', 220 | '蓝洋农场(12754)': '23_3034_12754', 221 | '排浦镇(3748)': '23_3034_3748', 222 | '热作学院(12824)': '23_3034_12824' 223 | }, 224 | '屯昌县(3704)': { 225 | '县城内(39889)': '23_3704_39889', 226 | '南吕镇(3824)': '23_3704_3824', 227 | '中建农场(12785)': '23_3704_12785', 228 | '西昌镇(3827)': '23_3704_3827', 229 | '枫木镇(3822)': '23_3704_3822', 230 | '屯城镇(3820)': '23_3704_3820', 231 | '乌坡镇(3823)': '23_3704_3823', 232 | '坡心镇(3826)': '23_3704_3826', 233 | '南坤镇(3825)': '23_3704_3825', 234 | '中坤农场(12786)': '23_3704_12786', 235 | '新兴镇(3821)': '23_3704_3821' 236 | }, 237 | '澄迈县(3702)': { 238 | '老城镇(3802)': '23_3702_3802', 239 | '文儒镇(3806)': '23_3702_3806', 240 | '西达农场(12789)': '23_3702_12789', 241 | '金江镇(3801)': '23_3702_3801', 242 | '仁兴镇(3808)': '23_3702_3808', 243 | '瑞溪镇(3803)': '23_3702_3803', 244 | '红光农场(12788)': '23_3702_12788', 245 | '城区(39886)': '23_3702_39886', 246 | '大丰镇(12787)': '23_3702_12787', 247 | '金安农场(12790)': '23_3702_12790', 248 | '中兴镇(3807)': '23_3702_3807', 249 | '加乐镇(3805)': '23_3702_3805', 250 | '福山镇(3809)': '23_3702_3809', 251 | '桥头镇(3810)': '23_3702_3810', 252 | '永发镇(3804)': '23_3702_3804' 253 | }, 254 | '昌江县(3705)': { 255 | '十月田镇(3830)': '23_3705_3830', 256 | '王下乡(12797)': '23_3705_12797', 257 | '乌烈镇(3831)': '23_3705_3831', 258 | '海尾镇(3833)': '23_3705_3833', 259 | '红林农场(12800)': '23_3705_12800', 260 | '石碌镇(3828)': '23_3705_3828', 261 | '霸王岭林场(12799)': '23_3705_12799', 262 | '城区(39890)': '23_3705_39890', 263 | '叉河镇(3829)': '23_3705_3829', 264 | '昌化镇(3832)': '23_3705_3832', 265 | '海南矿业公司(12798)': '23_3705_12798', 266 | '七叉镇(12796)': '23_3705_12796' 267 | }, 268 | '保亭县(3709)': { 269 | '保城镇(3869)': '23_3709_3869', 270 | '六弓乡(3875)': '23_3709_3875', 271 | '响水镇(3872)': '23_3709_3872', 272 | '毛感乡(3877)': '23_3709_3877', 273 | '金江农场(12811)': '23_3709_12811', 274 | '新政镇(3873)': '23_3709_3873', 275 | '南林乡(3876)': '23_3709_3876', 276 | '三道农场(12812)': '23_3709_12812', 277 | '新星农场(12810)': '23_3709_12810', 278 | '加茂镇(3871)': '23_3709_3871', 279 | '什玲镇(3870)': '23_3709_3870', 280 | '三道镇(3874)': '23_3709_3874', 281 | '保亭研究所(12809)': '23_3709_12809' 282 | }, 283 | '陵水县(3708)': { 284 | '新村镇(3865)': '23_3708_3865', 285 | '英州镇(3861)': '23_3708_3861', 286 | '提蒙乡(3867)': '23_3708_3867', 287 | '文罗镇(3863)': '23_3708_3863', 288 | '隆广镇(3862)': '23_3708_3862', 289 | '城区(39895)': '23_3708_39895', 290 | '椰林镇(3858)': '23_3708_3858', 291 | '岭门农场(12807)': '23_3708_12807', 292 | '光坡镇(3859)': '23_3708_3859', 293 | '南平农场(12808)': '23_3708_12808', 294 | '黎安镇(3866)': '23_3708_3866', 295 | '南平镇(53110)': '23_3708_53110', 296 | '三才镇(3860)': '23_3708_3860', 297 | '群英乡(3868)': '23_3708_3868', 298 | '本号镇(3864)': '23_3708_3864', 299 | '吊罗山林业公司(12806)': '23_3708_12806', 300 | '东华镇(53109)': '23_3708_53109' 301 | }, 302 | '五指山市(3699)': { 303 | '水满乡(3719)': '23_3699_3719', 304 | '毛道乡(3717)': '23_3699_3717', 305 | '番阳镇(3715)': '23_3699_3715', 306 | '毛阳镇(3714)': '23_3699_3714', 307 | '南圣镇(3713)': '23_3699_3713', 308 | '畅好农场(12746)': '23_3699_12746', 309 | '通什镇(3712)': '23_3699_3712', 310 | '畅好乡(3716)': '23_3699_3716' 311 | }, 312 | '琼海市(3115)': { 313 | '博鳌镇(3724)': '23_3115_3724', 314 | '大路镇(3731)': '23_3115_3731', 315 | '彬村山华侨农场(12747)': '23_3115_12747', 316 | '嘉积镇(3720)': '23_3115_3720', 317 | '会山镇(3732)': '23_3115_3732', 318 | '中原镇(3723)': '23_3115_3723', 319 | '长坡镇(3730)': '23_3115_3730', 320 | '东太农场(12748)': '23_3115_12748', 321 | '阳江镇(3725)': '23_3115_3725', 322 | '万泉镇(3721)': '23_3115_3721', 323 | '南俸农场(12751)': '23_3115_12751', 324 | '塔洋镇(3729)': '23_3115_3729', 325 | '东升农场(12750)': '23_3115_12750', 326 | '潭门镇(3728)': '23_3115_3728', 327 | '龙江镇(3727)': '23_3115_3727', 328 | '石壁镇(3722)': '23_3115_3722', 329 | '东红农场(12749)': '23_3115_12749' 330 | }, 331 | '三沙市(3711)': { 332 | '中沙群岛(12819)': { 333 | '黄岩岛(12823)': '23_3711_12819_12823', 334 | '中沙岛礁(12822)': '23_3711_12819_12822' 335 | }, 336 | '南沙群岛(3888)': { 337 | '永暑礁(12821)': '23_3711_3888_12821' 338 | }, 339 | '西沙群岛(3887)': { 340 | '永兴岛(12820)': '23_3711_3887_12820' 341 | } 342 | }, 343 | '文昌市(3698)': { 344 | '南阳农场(12772)': '23_3698_12772', 345 | '龙马乡(12769)': '23_3698_12769', 346 | '湖山乡(12770)': '23_3698_12770', 347 | '文教镇(3759)': '23_3698_3759', 348 | '东路农场(12771)': '23_3698_12771', 349 | '铺前镇(3767)': '23_3698_3767', 350 | '重兴镇(3753)': '23_3698_3753', 351 | '罗豆农场(12773)': '23_3698_12773', 352 | '宝芳乡(12768)': '23_3698_12768', 353 | '南阳镇(12765)': '23_3698_12765', 354 | '潭牛镇(3757)': '23_3698_3757', 355 | '东阁镇(3758)': '23_3698_3758', 356 | '蓬莱镇(3754)': '23_3698_3754', 357 | '冯坡镇(3765)': '23_3698_3765', 358 | '清谰镇(12764)': '23_3698_12764', 359 | '东路镇(3756)': '23_3698_3756', 360 | '昌洒镇(3762)': '23_3698_3762', 361 | '新桥镇(12766)': '23_3698_12766', 362 | '公坡镇(12762)': '23_3698_12762', 363 | '橡胶研究所(12774)': '23_3698_12774', 364 | '抱罗镇(3764)': '23_3698_3764', 365 | '东郊镇(3760)': '23_3698_3760', 366 | '锦山镇(3766)': '23_3698_3766', 367 | '头苑镇(12767)': '23_3698_12767', 368 | '翁田镇(3763)': '23_3698_3763', 369 | '文城镇(3752)': '23_3698_3752', 370 | '会文镇(3755)': '23_3698_3755', 371 | '龙楼镇(3761)': '23_3698_3761', 372 | '迈号镇(12763)': '23_3698_12763' 373 | } 374 | } 375 | } -------------------------------------------------------------------------------- /TEST/area_id/29.青海.txt: -------------------------------------------------------------------------------- 1 | { 2 | '青海(29)': { 3 | '果洛州(2605)': { 4 | '玛沁县(2606)': { 5 | '拉加镇(16717)': '29_2605_2606_16717', 6 | '下大武乡(16719)': '29_2605_2606_16719', 7 | '优云乡(16722)': '29_2605_2606_16722', 8 | '大武乡(16720)': '29_2605_2606_16720', 9 | '当洛乡(16723)': '29_2605_2606_16723', 10 | '雪山乡(16721)': '29_2605_2606_16721', 11 | '县城内(37730)': '29_2605_2606_37730', 12 | '东倾沟乡(16718)': '29_2605_2606_16718', 13 | '大武镇(16716)': '29_2605_2606_16716' 14 | }, 15 | '久治县(2610)': { 16 | '智青松多镇(16695)': '29_2605_2610_16695', 17 | '哇尔依乡(16697)': '29_2605_2610_16697', 18 | '县城内(37736)': '29_2605_2610_37736', 19 | '白玉乡(16700)': '29_2605_2610_16700', 20 | '索呼日麻乡(16696)': '29_2605_2610_16696', 21 | '门堂乡(16698)': '29_2605_2610_16698', 22 | '哇赛乡(16699)': '29_2605_2610_16699' 23 | }, 24 | '甘德县(2607)': { 25 | '江千乡(16694)': '29_2605_2607_16694', 26 | '下贡麻乡(16690)': '29_2605_2607_16690', 27 | '县城内(37731)': '29_2605_2607_37731', 28 | '柯曲镇(16688)': '29_2605_2607_16688', 29 | '青珍乡(16693)': '29_2605_2607_16693', 30 | '上贡麻乡(16689)': '29_2605_2607_16689', 31 | '岗龙乡(16692)': '29_2605_2607_16692', 32 | '下藏科乡(16691)': '29_2605_2607_16691' 33 | }, 34 | '班玛县(2609)': { 35 | '多贡麻乡(16670)': '29_2605_2609_16670', 36 | '达卡乡(16675)': '29_2605_2609_16675', 37 | '亚尔堂乡(16673)': '29_2605_2609_16673', 38 | '马可河乡(16671)': '29_2605_2609_16671', 39 | '灯塔乡(16677)': '29_2605_2609_16677', 40 | '县城内(37733)': '29_2605_2609_37733', 41 | '赛来塘镇(16669)': '29_2605_2609_16669', 42 | '吉卡乡(16674)': '29_2605_2609_16674', 43 | '知钦乡(16676)': '29_2605_2609_16676', 44 | '江日堂乡(16672)': '29_2605_2609_16672' 45 | }, 46 | '达日县(2608)': { 47 | '窝赛乡(16685)': '29_2605_2608_16685', 48 | '县城内(37732)': '29_2605_2608_37732', 49 | '下红科乡(16680)': '29_2605_2608_16680', 50 | '满掌乡(16683)': '29_2605_2608_16683', 51 | '上红科乡(16679)': '29_2605_2608_16679', 52 | '莫坝乡(16686)': '29_2605_2608_16686', 53 | '建设乡(16687)': '29_2605_2608_16687', 54 | '特合土乡(16682)': '29_2605_2608_16682', 55 | '吉迈镇(16678)': '29_2605_2608_16678', 56 | '德昂乡(16684)': '29_2605_2608_16684', 57 | '桑日麻乡(16681)': '29_2605_2608_16681' 58 | }, 59 | '玛多县(2611)': { 60 | '花石峡镇(16713)': '29_2605_2611_16713', 61 | '黄河乡(16715)': '29_2605_2611_16715', 62 | '县城内(37737)': '29_2605_2611_37737', 63 | '玛查理镇(16712)': '29_2605_2611_16712', 64 | '扎陵湖乡(16714)': '29_2605_2611_16714' 65 | } 66 | }, 67 | '海西州(2620)': { 68 | '格尔木市(3021)': { 69 | '格尔木农垦有限公司(18204)': '29_2620_3021_18204', 70 | '察尔汗工行委(18205)': '29_2620_3021_18205', 71 | '唐古拉镇(18201)': '29_2620_3021_18201', 72 | '大格勒乡(18203)': '29_2620_3021_18203', 73 | '郭勒木德镇(18200)': '29_2620_3021_18200', 74 | '城区(52772)': '29_2620_3021_52772', 75 | '乌图美仁乡(18202)': '29_2620_3021_18202' 76 | }, 77 | '茫崖行委(2627)': { 78 | '尕斯乡(18238)': '29_2620_2627_18238', 79 | '花土沟镇(18236)': '29_2620_2627_18236', 80 | '茫崖镇(18237)': '29_2620_2627_18237' 81 | }, 82 | '都兰县(2624)': { 83 | '柴达木监狱(18217)': '29_2620_2624_18217', 84 | '香加乡(18211)': '29_2620_2624_18211', 85 | '巴隆乡(18213)': '29_2620_2624_18213', 86 | '查查香卡农场(18214)': '29_2620_2624_18214', 87 | '香日德镇(18207)': '29_2620_2624_18207', 88 | '热水乡(18210)': '29_2620_2624_18210', 89 | '香巴管委会(18216)': '29_2620_2624_18216', 90 | '英德尔种羊场(18215)': '29_2620_2624_18215', 91 | '夏日哈镇(18208)': '29_2620_2624_18208', 92 | '察汉乌苏镇(18206)': '29_2620_2624_18206', 93 | '宗加镇(18209)': '29_2620_2624_18209', 94 | '沟里乡(18212)': '29_2620_2624_18212', 95 | '县城内(37904)': '29_2620_2624_37904' 96 | }, 97 | '天峻县(2623)': { 98 | '快尔玛乡(18221)': '29_2620_2623_18221', 99 | '苏里乡(18225)': '29_2620_2623_18225', 100 | '龙门乡(18223)': '29_2620_2623_18223', 101 | '县城内(37903)': '29_2620_2623_37903', 102 | '智合玛乡(18222)': '29_2620_2623_18222', 103 | '江河镇(18220)': '29_2620_2623_18220', 104 | '新源镇(18218)': '29_2620_2623_18218', 105 | '木里镇(18219)': '29_2620_2623_18219', 106 | '阳康乡(18227)': '29_2620_2623_18227', 107 | '生格乡(18226)': '29_2620_2623_18226', 108 | '舟群乡(18224)': '29_2620_2623_18224' 109 | }, 110 | '德令哈市(2621)': { 111 | '柴旦镇(18191)': '29_2620_2621_18191', 112 | '茫崖镇(18193)': '29_2620_2621_18193', 113 | '怀头他拉镇(18186)': '29_2620_2621_18186', 114 | '花土沟镇(18189)': '29_2620_2621_18189', 115 | '蓄集乡(18194)': '29_2620_2621_18194', 116 | '城区(52771)': '29_2620_2621_52771', 117 | '冷湖镇(18192)': '29_2620_2621_18192', 118 | '锡铁山镇(18188)': '29_2620_2621_18188', 119 | '柯鲁柯镇(18187)': '29_2620_2621_18187', 120 | '尕海镇(18190)': '29_2620_2621_18190' 121 | }, 122 | '大柴旦行委(2625)': { 123 | '大柴旦镇(18233)': '29_2620_2625_18233', 124 | '锡铁山镇(18234)': '29_2620_2625_18234' 125 | }, 126 | '冷湖行委(2626)': { 127 | '冷湖镇(18235)': '29_2620_2626_18235' 128 | }, 129 | '乌兰县(2622)': { 130 | '海西州莫河畜牧场(18232)': '29_2620_2622_18232', 131 | '县城内(37902)': '29_2620_2622_37902', 132 | '希里沟镇(18228)': '29_2620_2622_18228', 133 | '茶卡镇(18229)': '29_2620_2622_18229', 134 | '柯柯镇(18230)': '29_2620_2622_18230', 135 | '铜普镇(18231)': '29_2620_2622_18231' 136 | } 137 | }, 138 | '海南州(2603)': { 139 | '贵南县(4016)': { 140 | '贵南草业开发有限责任公司(18167)': '29_2603_4016_18167', 141 | '过马营镇(18161)': '29_2603_4016_18161', 142 | '茫曲镇(18162)': '29_2603_4016_18162', 143 | '茫拉乡(18165)': '29_2603_4016_18165', 144 | '森多乡(18164)': '29_2603_4016_18164', 145 | '沙沟乡(18163)': '29_2603_4016_18163', 146 | '县城内(37729)': '29_2603_4016_37729', 147 | '塔秀乡(18166)': '29_2603_4016_18166' 148 | }, 149 | '兴海县(4015)': { 150 | '河卡种羊场(18181)': '29_2603_4015_18181', 151 | '唐乃亥乡(18177)': '29_2603_4015_18177', 152 | '中铁乡(18180)': '29_2603_4015_18180', 153 | '曲什安镇(18175)': '29_2603_4015_18175', 154 | '河卡镇(18176)': '29_2603_4015_18176', 155 | '县城内(37728)': '29_2603_4015_37728', 156 | '子科滩镇(18174)': '29_2603_4015_18174', 157 | '赛什墉牧场(18182)': '29_2603_4015_18182', 158 | '温泉乡(18178)': '29_2603_4015_18178', 159 | '龙藏乡(18179)': '29_2603_4015_18179' 160 | }, 161 | '同德县(4013)': { 162 | '尕巴松多镇(18168)': '29_2603_4013_18168', 163 | '秀麻乡(18171)': '29_2603_4013_18171', 164 | '巴沟乡(18170)': '29_2603_4013_18170', 165 | '省牧草良种繁殖场(18173)': '29_2603_4013_18173', 166 | '县城内(37726)': '29_2603_4013_37726', 167 | '唐谷镇(18169)': '29_2603_4013_18169', 168 | '河北乡(18172)': '29_2603_4013_18172' 169 | }, 170 | '共和县(4012)': { 171 | '江西沟乡(18146)': '29_2603_4012_18146', 172 | '龙羊峡镇(18141)': '29_2603_4012_18141', 173 | '沙珠玉乡(18145)': '29_2603_4012_18145', 174 | '石乃亥乡(18144)': '29_2603_4012_18144', 175 | '巴卡台农场(18152)': '29_2603_4012_18152', 176 | '铁盖乡(18147)': '29_2603_4012_18147', 177 | '倒淌河镇(18140)': '29_2603_4012_18140', 178 | '黑马河乡(18143)': '29_2603_4012_18143', 179 | '廿地乡(18148)': '29_2603_4012_18148', 180 | '恰卜恰镇(18139)': '29_2603_4012_18139', 181 | '县城内(37725)': '29_2603_4012_37725', 182 | '安置农场(18153)': '29_2603_4012_18153', 183 | '切吉乡(18149)': '29_2603_4012_18149', 184 | '湖东种羊场(18151)': '29_2603_4012_18151', 185 | '塘格木镇(18142)': '29_2603_4012_18142', 186 | '铁卜加草改站(18150)': '29_2603_4012_18150' 187 | }, 188 | '贵德县(4014)': { 189 | '河东乡(18158)': '29_2603_4014_18158', 190 | '拉西瓦镇(18154)': '29_2603_4014_18154', 191 | '新街乡(18159)': '29_2603_4014_18159', 192 | '河西镇(18155)': '29_2603_4014_18155', 193 | '河阴镇(18156)': '29_2603_4014_18156', 194 | '县城内(37727)': '29_2603_4014_37727', 195 | '尕让乡(18160)': '29_2603_4014_18160', 196 | '常牧镇(18157)': '29_2603_4014_18157' 197 | } 198 | }, 199 | '海东地区(2585)': { 200 | '互助县(2589)': { 201 | '林川乡(16591)': '29_2585_2589_16591', 202 | '哈拉直沟乡(16581)': '29_2585_2589_16581', 203 | '县城内(37714)': '29_2585_2589_37714', 204 | '蔡家堡乡(16583)': '29_2585_2589_16583', 205 | '东和乡(16587)': '29_2585_2589_16587', 206 | '高寨镇(16576)': '29_2585_2589_16576', 207 | '西山乡(16589)': '29_2585_2589_16589', 208 | '红崖子沟乡(16582)': '29_2585_2589_16582', 209 | '塘川镇(16578)': '29_2585_2589_16578', 210 | '五峰镇(16580)': '29_2585_2589_16580', 211 | '松多乡(16585)': '29_2585_2589_16585', 212 | '五十镇(16579)': '29_2585_2589_16579', 213 | '东山乡(16586)': '29_2585_2589_16586', 214 | '东沟乡(16590)': '29_2585_2589_16590', 215 | '加定镇(16577)': '29_2585_2589_16577', 216 | '丹麻镇(16575)': '29_2585_2589_16575', 217 | '威远镇(16574)': '29_2585_2589_16574', 218 | '巴扎乡(16584)': '29_2585_2589_16584', 219 | '南门峡镇(16573)': '29_2585_2589_16573', 220 | '台子乡(16588)': '29_2585_2589_16588' 221 | }, 222 | '民和县(2588)': { 223 | '转导乡(16647)': '29_2585_2588_16647', 224 | '峡门镇(16637)': '29_2585_2588_16637', 225 | '总堡乡(16644)': '29_2585_2588_16644', 226 | '马场垣乡(16638)': '29_2585_2588_16638', 227 | '松树乡(16643)': '29_2585_2588_16643', 228 | '巴州镇(16635)': '29_2585_2588_16635', 229 | '北山乡(16642)': '29_2585_2588_16642', 230 | '川口镇(16631)': '29_2585_2588_16631', 231 | '杏儿乡(16651)': '29_2585_2588_16651', 232 | '前河乡(16648)': '29_2585_2588_16648', 233 | '大庄乡(16646)': '29_2585_2588_16646', 234 | '核桃庄乡(16639)': '29_2585_2588_16639', 235 | '隆治乡(16645)': '29_2585_2588_16645', 236 | '县城内(37713)': '29_2585_2588_37713', 237 | '古鄯镇(16632)': '29_2585_2588_16632', 238 | '李二堡镇(16630)': '29_2585_2588_16630', 239 | '官亭镇(16634)': '29_2585_2588_16634', 240 | '西沟乡(16640)': '29_2585_2588_16640', 241 | '马营镇(16633)': '29_2585_2588_16633', 242 | '甘沟乡(16649)': '29_2585_2588_16649', 243 | '中川乡(16650)': '29_2585_2588_16650', 244 | '新民乡(16641)': '29_2585_2588_16641', 245 | '满坪镇(16636)': '29_2585_2588_16636' 246 | }, 247 | '乐都县(2587)': { 248 | '峰堆乡(16627)': '29_2585_2587_16627', 249 | '达拉乡(16629)': '29_2585_2587_16629', 250 | '共和乡(16619)': '29_2585_2587_16619', 251 | '瞿昙镇(16617)': '29_2585_2587_16617', 252 | '洪水镇(16611)': '29_2585_2587_16611', 253 | '蒲台乡(16625)': '29_2585_2587_16625', 254 | '马厂乡(16624)': '29_2585_2587_16624', 255 | '高店镇(16616)': '29_2585_2587_16616', 256 | '李家乡(16621)': '29_2585_2587_16621', 257 | '下营乡(16622)': '29_2585_2587_16622', 258 | '中坝乡(16626)': '29_2585_2587_16626', 259 | '高庙镇(16615)': '29_2585_2587_16615', 260 | '城台乡(16628)': '29_2585_2587_16628', 261 | '中岭乡(16620)': '29_2585_2587_16620', 262 | '寿乐镇(16614)': '29_2585_2587_16614', 263 | '碾伯镇(16612)': '29_2585_2587_16612', 264 | '雨润镇(16613)': '29_2585_2587_16613', 265 | '县城内(37712)': '29_2585_2587_37712', 266 | '芦化乡(16623)': '29_2585_2587_16623', 267 | '马营乡(16618)': '29_2585_2587_16618' 268 | }, 269 | '化隆县(2590)': { 270 | '公伯峡管理委员会(16610)': '29_2585_2590_16610', 271 | '雄先乡(16603)': '29_2585_2590_16603', 272 | '德恒隆乡(16599)': '29_2585_2590_16599', 273 | '甘都镇(16596)': '29_2585_2590_16596', 274 | '查甫乡(16605)': '29_2585_2590_16605', 275 | '石大仓乡(16602)': '29_2585_2590_16602', 276 | '初麻乡(16604)': '29_2585_2590_16604', 277 | '扎巴镇(16597)': '29_2585_2590_16597', 278 | '昂思多镇(16593)': '29_2585_2590_16593', 279 | '群科镇(16595)': '29_2585_2590_16595', 280 | '县城内(37715)': '29_2585_2590_37715', 281 | '沙连堡乡(16600)': '29_2585_2590_16600', 282 | '李家峡管理委员会(16609)': '29_2585_2590_16609', 283 | '谢家滩乡(16598)': '29_2585_2590_16598', 284 | '塔加乡(16606)': '29_2585_2590_16606', 285 | '巴燕镇(16594)': '29_2585_2590_16594', 286 | '金源乡(16607)': '29_2585_2590_16607', 287 | '二塘乡(16608)': '29_2585_2590_16608', 288 | '牙什尕镇(16592)': '29_2585_2590_16592', 289 | '阿什奴乡(16601)': '29_2585_2590_16601' 290 | }, 291 | '平安县(2586)': { 292 | '平安镇(16652)': '29_2585_2586_16652', 293 | '县城内(37711)': '29_2585_2586_37711', 294 | '古城乡(16659)': '29_2585_2586_16659', 295 | '三合镇(16654)': '29_2585_2586_16654', 296 | '小峡镇(16653)': '29_2585_2586_16653', 297 | '石灰窑乡(16656)': '29_2585_2586_16656', 298 | '沙沟乡(16658)': '29_2585_2586_16658', 299 | '洪水泉乡(16655)': '29_2585_2586_16655', 300 | '巴藏沟乡(16657)': '29_2585_2586_16657' 301 | }, 302 | '循化县(2591)': { 303 | '清水乡(16665)': '29_2585_2591_16665', 304 | '查汗都斯乡(16661)': '29_2585_2591_16661', 305 | '积石镇(16660)': '29_2585_2591_16660', 306 | '街子乡(16663)': '29_2585_2591_16663', 307 | '道帏乡(16664)': '29_2585_2591_16664', 308 | '尕楞乡(16668)': '29_2585_2591_16668', 309 | '县城内(37716)': '29_2585_2591_37716', 310 | '岗察乡(16666)': '29_2585_2591_16666', 311 | '白庄乡(16662)': '29_2585_2591_16662', 312 | '文都乡(16667)': '29_2585_2591_16667' 313 | } 314 | }, 315 | '黄南州(2597)': { 316 | '同仁县(2599)': { 317 | '扎毛乡(18261)': '29_2597_2599_18261', 318 | '双朋西乡(18255)': '29_2597_2599_18255', 319 | '年都乎乡(18258)': '29_2597_2599_18258', 320 | '隆务镇(18253)': '29_2597_2599_18253', 321 | '黄乃亥乡(18256)': '29_2597_2599_18256', 322 | '加吾乡(18263)': '29_2597_2599_18263', 323 | '曲库乎乡(18257)': '29_2597_2599_18257', 324 | '保安镇(18254)': '29_2597_2599_18254', 325 | '瓜什则乡(18259)': '29_2597_2599_18259', 326 | '县城内(37722)': '29_2597_2599_37722', 327 | '多哇乡(18262)': '29_2597_2599_18262', 328 | '兰采乡(18260)': '29_2597_2599_18260' 329 | }, 330 | '河南县(2602)': { 331 | '多松乡(18242)': '29_2597_2602_18242', 332 | '宁木特乡(18240)': '29_2597_2602_18240', 333 | '优干宁镇(18239)': '29_2597_2602_18239', 334 | '赛尔龙乡(18241)': '29_2597_2602_18241', 335 | '柯生乡(18243)': '29_2597_2602_18243', 336 | '县城内(37724)': '29_2597_2602_37724' 337 | }, 338 | '尖扎县(2598)': { 339 | '县城内(37721)': '29_2597_2598_37721', 340 | '贾加乡(18248)': '29_2597_2598_18248', 341 | '尖扎滩乡(18247)': '29_2597_2598_18247', 342 | '昴拉乡(18250)': '29_2597_2598_18250', 343 | '马克堂镇(18244)': '29_2597_2598_18244', 344 | '措周乡(18249)': '29_2597_2598_18249', 345 | '当顺乡(18252)': '29_2597_2598_18252', 346 | '康扬镇(18246)': '29_2597_2598_18246', 347 | '坎布拉镇(18245)': '29_2597_2598_18245', 348 | '能科乡(18251)': '29_2597_2598_18251' 349 | }, 350 | '泽库县(2600)': { 351 | '县城内(37723)': '29_2597_2600_37723', 352 | '泽曲镇(18264)': '29_2597_2600_18264', 353 | '麦秀镇(18265)': '29_2597_2600_18265', 354 | '多禾茂乡(18267)': '29_2597_2600_18267', 355 | '和日乡(18269)': '29_2597_2600_18269', 356 | '宁秀乡(18268)': '29_2597_2600_18268', 357 | '王家乡(18270)': '29_2597_2600_18270', 358 | '西卜沙乡(18266)': '29_2597_2600_18266' 359 | } 360 | }, 361 | '海北州(2592)': { 362 | '海晏县(2593)': { 363 | '金滩乡(16737)': '29_2592_2593_16737', 364 | '三角城镇(16732)': '29_2592_2593_16732', 365 | '甘子河乡(16736)': '29_2592_2593_16736', 366 | '西海镇(16733)': '29_2592_2593_16733', 367 | '哈勒景乡(16734)': '29_2592_2593_16734', 368 | '县城内(37717)': '29_2592_2593_37717', 369 | '青海湖乡(16735)': '29_2592_2593_16735' 370 | }, 371 | '门源县(2596)': { 372 | '种马场(16937)': '29_2592_2596_16937', 373 | '珠固乡(16934)': '29_2592_2596_16934', 374 | '北山乡(16929)': '29_2592_2596_16929', 375 | '浩门镇(16925)': '29_2592_2596_16925', 376 | '仙米乡(16933)': '29_2592_2596_16933', 377 | '西滩乡(16931)': '29_2592_2596_16931', 378 | '麻莲乡(16930)': '29_2592_2596_16930', 379 | '门源监狱(16936)': '29_2592_2596_16936', 380 | '县城内(37720)': '29_2592_2596_37720', 381 | '泉口镇(16926)': '29_2592_2596_16926', 382 | '皇城乡(16935)': '29_2592_2596_16935', 383 | '苏吉滩乡(16928)': '29_2592_2596_16928', 384 | '青石咀镇(16924)': '29_2592_2596_16924', 385 | '东川镇(16927)': '29_2592_2596_16927', 386 | '阴田乡(16932)': '29_2592_2596_16932' 387 | }, 388 | '刚察县(2595)': { 389 | '哈尔盖镇(16725)': '29_2592_2595_16725', 390 | '青海湖农场(16730)': '29_2592_2595_16730', 391 | '黄玉农场(16731)': '29_2592_2595_16731', 392 | '沙柳河镇(16724)': '29_2592_2595_16724', 393 | '伊克乌兰乡(16726)': '29_2592_2595_16726', 394 | '三角城种羊场(16729)': '29_2592_2595_16729', 395 | '吉尔孟乡(16727)': '29_2592_2595_16727', 396 | '泉吉乡(16728)': '29_2592_2595_16728', 397 | '县城内(37719)': '29_2592_2595_37719' 398 | }, 399 | '祁连县(2594)': { 400 | '默勒镇(18134)': '29_2592_2594_18134', 401 | '县城内(37718)': '29_2592_2594_37718', 402 | '扎麻什乡(18135)': '29_2592_2594_18135', 403 | '野牛沟乡(18136)': '29_2592_2594_18136', 404 | '阿柔乡(18137)': '29_2592_2594_18137', 405 | '峨堡镇(18133)': '29_2592_2594_18133', 406 | '央隆乡(18138)': '29_2592_2594_18138', 407 | '八宝镇(18132)': '29_2592_2594_18132' 408 | } 409 | }, 410 | '西宁市(2580)': { 411 | '湟源县(2582)': { 412 | '申中乡(21965)': '29_2580_2582_21965', 413 | '县城内(37709)': '29_2580_2582_37709', 414 | '寺寨乡(21967)': '29_2580_2582_21967', 415 | '和平乡(21963)': '29_2580_2582_21963', 416 | '东峡乡(21961)': '29_2580_2582_21961', 417 | '日月乡(21962)': '29_2580_2582_21962', 418 | '大华镇(21960)': '29_2580_2582_21960', 419 | '波航乡(21964)': '29_2580_2582_21964', 420 | '巴燕乡(21966)': '29_2580_2582_21966', 421 | '城关镇(21959)': '29_2580_2582_21959' 422 | }, 423 | '湟中县(2581)': { 424 | '上新庄镇(21969)': '29_2580_2581_21969', 425 | '拦隆口镇(21972)': '29_2580_2581_21972', 426 | '海子沟乡(21978)': '29_2580_2581_21978', 427 | '田家寨镇(21968)': '29_2580_2581_21968', 428 | '县城内(37708)': '29_2580_2581_37708', 429 | '汉东乡(21981)': '29_2580_2581_21981', 430 | '李家山镇(21974)': '29_2580_2581_21974', 431 | '大才乡(21982)': '29_2580_2581_21982', 432 | '西堡镇(21975)': '29_2580_2581_21975', 433 | '共和镇(21976)': '29_2580_2581_21976', 434 | '多巴镇(21977)': '29_2580_2581_21977', 435 | '群加乡(21980)': '29_2580_2581_21980', 436 | '甘河滩镇(21971)': '29_2580_2581_21971', 437 | '上五庄镇(21973)': '29_2580_2581_21973', 438 | '鲁沙尔镇(21970)': '29_2580_2581_21970', 439 | '土门关乡(21979)': '29_2580_2581_21979' 440 | }, 441 | '大通县(2583)': { 442 | '元朔乡(21940)': '29_2580_2583_21940', 443 | '东峡乡(21949)': '29_2580_2583_21949', 444 | '良教乡(21943)': '29_2580_2583_21943', 445 | '县城内(37710)': '29_2580_2583_37710', 446 | '新庄乡(21942)': '29_2580_2583_21942', 447 | '黄家寨镇(21933)': '29_2580_2583_21933', 448 | '极乐乡(21957)': '29_2580_2583_21957', 449 | '塔尔乡(21951)': '29_2580_2583_21951', 450 | '桥头镇(21934)': '29_2580_2583_21934', 451 | '逊让乡(21956)': '29_2580_2583_21956', 452 | '向化乡(21950)': '29_2580_2583_21950', 453 | '新庄镇(21935)': '29_2580_2583_21935', 454 | '清平乡(21938)': '29_2580_2583_21938', 455 | '长宁乡(21944)': '29_2580_2583_21944', 456 | '青山乡(21954)': '29_2580_2583_21954', 457 | '多林乡(21955)': '29_2580_2583_21955', 458 | '桦林乡(21948)': '29_2580_2583_21948', 459 | '岗冲乡(21939)': '29_2580_2583_21939', 460 | '药草乡(21941)': '29_2580_2583_21941', 461 | '石山乡(21946)': '29_2580_2583_21946', 462 | '宝库乡(21952)': '29_2580_2583_21952', 463 | '景阳乡(21945)': '29_2580_2583_21945', 464 | '青林乡(21953)': '29_2580_2583_21953', 465 | '朔北乡(21947)': '29_2580_2583_21947', 466 | '斜沟乡(21958)': '29_2580_2583_21958', 467 | '城关镇(21936)': '29_2580_2583_21936', 468 | '后子河乡(21937)': '29_2580_2583_21937' 469 | }, 470 | '城西区(21654)': { 471 | '彭家寨乡(21924)': '29_2580_21654_21924', 472 | '城区(52769)': '29_2580_21654_52769' 473 | }, 474 | '城北区(21655)': { 475 | '廿里铺镇(21909)': '29_2580_21655_21909', 476 | '城区(52770)': '29_2580_21655_52770', 477 | '大堡子镇(21908)': '29_2580_21655_21908' 478 | }, 479 | '城东区(21653)': { 480 | '韵家口镇(21918)': '29_2580_21653_21918', 481 | '乐家湾镇(21917)': '29_2580_21653_21917', 482 | '城区(52768)': '29_2580_21653_52768' 483 | }, 484 | '城中区(21652)': { 485 | '总寨镇(21932)': '29_2580_21652_21932', 486 | '城区(51875)': '29_2580_21652_51875' 487 | } 488 | }, 489 | '玉树州(2612)': { 490 | '玉树县(2613)': { 491 | '结古镇(18295)': '29_2612_2613_18295', 492 | '小苏莽乡(18297)': '29_2612_2613_18297', 493 | '巴塘乡(18300)': '29_2612_2613_18300', 494 | '仲达乡(18299)': '29_2612_2613_18299', 495 | '县城内(37905)': '29_2612_2613_37905', 496 | '隆宝镇(18296)': '29_2612_2613_18296', 497 | '上拉秀乡(18298)': '29_2612_2613_18298', 498 | '下拉秀镇(18294)': '29_2612_2613_18294', 499 | '安冲乡(18302)': '29_2612_2613_18302', 500 | '哈秀乡(18301)': '29_2612_2613_18301' 501 | }, 502 | '曲麻莱县(2618)': { 503 | '县城内(37912)': '29_2612_2618_37912', 504 | '约改镇(18271)': '29_2612_2618_18271', 505 | '巴干乡(18273)': '29_2612_2618_18273', 506 | '叶格乡(18275)': '29_2612_2618_18275', 507 | '麻多乡(18276)': '29_2612_2618_18276', 508 | '秋智乡(18274)': '29_2612_2618_18274', 509 | '曲麻河乡(18272)': '29_2612_2618_18272' 510 | }, 511 | '囊谦县(2615)': { 512 | '吉曲乡(18287)': '29_2612_2615_18287', 513 | '觉拉乡(18290)': '29_2612_2615_18290', 514 | '吉尼赛乡(18285)': '29_2612_2615_18285', 515 | '东坝乡(18291)': '29_2612_2615_18291', 516 | '毛庄乡(18289)': '29_2612_2615_18289', 517 | '着晓乡(18293)': '29_2612_2615_18293', 518 | '癿扎乡(18286)': '29_2612_2615_18286', 519 | '香达镇(18284)': '29_2612_2615_18284', 520 | '娘拉乡(18288)': '29_2612_2615_18288', 521 | '县城内(37907)': '29_2612_2615_37907', 522 | '尕羊乡(18292)': '29_2612_2615_18292' 523 | }, 524 | '称多县(2614)': { 525 | '珍秦镇(18281)': '29_2612_2614_18281', 526 | '扎朵镇(18280)': '29_2612_2614_18280', 527 | '清水河镇(18277)': '29_2612_2614_18277', 528 | '尕朵乡(18282)': '29_2612_2614_18282', 529 | '县城内(37906)': '29_2612_2614_37906', 530 | '拉布乡(18283)': '29_2612_2614_18283', 531 | '称文镇(18278)': '29_2612_2614_18278', 532 | '歇武镇(18279)': '29_2612_2614_18279' 533 | }, 534 | '杂多县(2616)': { 535 | '萨呼腾镇(18303)': '29_2612_2616_18303', 536 | '昂赛乡(18304)': '29_2612_2616_18304', 537 | '县城内(37910)': '29_2612_2616_37910', 538 | '扎青乡(18310)': '29_2612_2616_18310', 539 | '阿多乡(18306)': '29_2612_2616_18306', 540 | '查旦乡(18308)': '29_2612_2616_18308', 541 | '苏鲁乡(18307)': '29_2612_2616_18307', 542 | '结多乡(18305)': '29_2612_2616_18305', 543 | '莫云乡(18309)': '29_2612_2616_18309' 544 | }, 545 | '治多县(2617)': { 546 | '立新乡(18316)': '29_2612_2617_18316', 547 | '治渠乡(18312)': '29_2612_2617_18312', 548 | '扎河乡(18314)': '29_2612_2617_18314', 549 | '加吉博洛格镇(18311)': '29_2612_2617_18311', 550 | '索加乡(18313)': '29_2612_2617_18313', 551 | '多彩乡(18315)': '29_2612_2617_18315', 552 | '县城内(37911)': '29_2612_2617_37911' 553 | } 554 | } 555 | } 556 | } -------------------------------------------------------------------------------- /TEST/area_id/3.天津.txt: -------------------------------------------------------------------------------- 1 | { 2 | '天津(3)': { 3 | '静海县(51042)': { 4 | '全境(36157)': '3_51042_36157' 5 | }, 6 | '大港区(51049)': { 7 | '主城区外(8547)': '3_51049_8547', 8 | '主城区内(8546)': '3_51049_8546', 9 | '大港油田(8545)': '3_51049_8545' 10 | }, 11 | '北辰区(51050)': { 12 | '外环外其它地区(36168)': '3_51050_36168', 13 | '外环外双街镇,河北工大新校,屈店工业园(36167)': '3_51050_36167', 14 | '外环内(6646)': '3_51050_6646' 15 | }, 16 | '河西区(51039)': { 17 | '全境(2985)': '3_51039_2985' 18 | }, 19 | '东丽区(51035)': { 20 | '全境(39620)': '3_51035_39620' 21 | }, 22 | '河北区(51037)': { 23 | '全境(2987)': '3_51037_2987' 24 | }, 25 | '津南区(51047)': { 26 | '双港,辛庄(36171)': '3_51047_36171', 27 | '其他地区(36172)': '3_51047_36172', 28 | '咸水沽镇、海河教育园,海河科技园(25704)': '3_51047_25704' 29 | }, 30 | '汉沽区(51048)': { 31 | '其它地区(23673)': '3_51048_23673', 32 | '汉沽区街里、汉沽开发区(23672)': '3_51048_23672' 33 | }, 34 | '武清区(51046)': { 35 | '全境(53669)': '3_51046_53669' 36 | }, 37 | '西青区(51045)': { 38 | '全境(53866)': '3_51045_53866' 39 | }, 40 | '宝坻区(51051)': { 41 | '其它地区(22849)': '3_51051_22849', 42 | '城关镇、马家店开发区、天宝工业园(22848)': '3_51051_22848' 43 | }, 44 | '红桥区(51040)': { 45 | '全境(2986)': '3_51040_2986' 46 | }, 47 | '宁河县(51052)': { 48 | '芦台镇、经济开发区、贸易开发区(23674)': '3_51052_23674', 49 | '其它地区(23675)': '3_51052_23675' 50 | }, 51 | '南开区(51043)': { 52 | '全境(2907)': '3_51043_2907' 53 | }, 54 | '和平区(51036)': { 55 | '全境(2984)': '3_51036_2984' 56 | }, 57 | '河东区(51038)': { 58 | '全境(3000)': '3_51038_3000' 59 | }, 60 | '塘沽区(51044)': { 61 | '全境(25708)': '3_51044_25708' 62 | }, 63 | '蓟县(51041)': { 64 | '全境(98)': '3_51041_98' 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /TEST/area_id/30.宁夏.txt: -------------------------------------------------------------------------------- 1 | { 2 | '宁夏(30)': { 3 | '中卫市(3071)': { 4 | '中宁县(3072)': { 5 | '鸣沙镇(16939)': '30_3071_3072_16939', 6 | '县城内(51156)': '30_3071_3072_51156', 7 | '大战场乡(16943)': '30_3071_3072_16943', 8 | '石空镇(16940)': '30_3071_3072_16940', 9 | '舟塔乡(16946)': '30_3071_3072_16946', 10 | '恩和镇(16942)': '30_3071_3072_16942', 11 | '白马乡(16945)': '30_3071_3072_16945', 12 | '新堡镇(16941)': '30_3071_3072_16941', 13 | '喊叫水乡(16944)': '30_3071_3072_16944', 14 | '余丁乡(16947)': '30_3071_3072_16947', 15 | '宁安镇(16938)': '30_3071_3072_16938', 16 | '长山头农场(16948)': '30_3071_3072_16948', 17 | '渠口农场(16949)': '30_3071_3072_16949' 18 | }, 19 | '沙坡头区(4020)': { 20 | '兴仁镇(16894)': '30_3071_4020_16894', 21 | '迎水桥镇(16885)': '30_3071_4020_16885', 22 | '常乐镇(16886)': '30_3071_4020_16886', 23 | '美利工业园区(16897)': '30_3071_4020_16897', 24 | '柔远镇(16890)': '30_3071_4020_16890', 25 | '镇罗镇(16891)': '30_3071_4020_16891', 26 | '滨河镇(16887)': '30_3071_4020_16887', 27 | '永康镇(16893)': '30_3071_4020_16893', 28 | '文昌镇(16888)': '30_3071_4020_16888', 29 | '东园镇(16889)': '30_3071_4020_16889', 30 | '宣和镇(16892)': '30_3071_4020_16892', 31 | '蒿川乡(16896)': '30_3071_4020_16896', 32 | '香山乡(16895)': '30_3071_4020_16895', 33 | '山羊选育场(16898)': '30_3071_4020_16898' 34 | }, 35 | '海原县(3148)': { 36 | '县城内(37707)': '30_3071_3148_37707', 37 | '树台乡(16906)': '30_3071_3148_16906', 38 | '南华山自然保护区管理处(16919)': '30_3071_3148_16919', 39 | '高崖乡(16910)': '30_3071_3148_16910', 40 | '老城管理办公室(16920)': '30_3071_3148_16920', 41 | '兴隆乡(16909)': '30_3071_3148_16909', 42 | '郑旗乡(16911)': '30_3071_3148_16911', 43 | '贾塘乡(16912)': '30_3071_3148_16912', 44 | '七营镇(16904)': '30_3071_3148_16904', 45 | '九彩乡(16914)': '30_3071_3148_16914', 46 | '李旺镇(16901)': '30_3071_3148_16901', 47 | '甘城乡(16918)': '30_3071_3148_16918', 48 | '史店乡(16905)': '30_3071_3148_16905', 49 | '涵养林总场(16922)': '30_3071_3148_16922', 50 | '海城镇(16900)': '30_3071_3148_16900', 51 | '徐套乡(16908)': '30_3071_3148_16908', 52 | '黑城镇(16903)': '30_3071_3148_16903', 53 | '李俊乡(16915)': '30_3071_3148_16915', 54 | '曹洼乡(16913)': '30_3071_3148_16913', 55 | '关庄乡(16917)': '30_3071_3148_16917', 56 | '盐池种羊场(16921)': '30_3071_3148_16921', 57 | '西安镇(16902)': '30_3071_3148_16902', 58 | '关桥乡(16907)': '30_3071_3148_16907', 59 | '红羊乡(16916)': '30_3071_3148_16916' 60 | } 61 | }, 62 | '固原市(2644)': { 63 | '西吉县(2647)': { 64 | '红耀乡(16820)': '30_2644_2647_16820', 65 | '吉强镇(16814)': '30_2644_2647_16814', 66 | '什字乡(16827)': '30_2644_2647_16827', 67 | '兴平乡(16824)': '30_2644_2647_16824', 68 | '震湖乡(16823)': '30_2644_2647_16823', 69 | '田坪乡(16821)': '30_2644_2647_16821', 70 | '沙沟乡(16818)': '30_2644_2647_16818', 71 | '硝河乡(16830)': '30_2644_2647_16830', 72 | '马建乡(16822)': '30_2644_2647_16822', 73 | '王民乡(16826)': '30_2644_2647_16826', 74 | '偏城乡(16831)': '30_2644_2647_16831', 75 | '平峰镇(16815)': '30_2644_2647_16815', 76 | '新营乡(16819)': '30_2644_2647_16819', 77 | '西滩乡(16825)': '30_2644_2647_16825', 78 | '将台乡(16829)': '30_2644_2647_16829', 79 | '马莲乡(16828)': '30_2644_2647_16828', 80 | '兴隆镇(16816)': '30_2644_2647_16816', 81 | '白崖乡(16832)': '30_2644_2647_16832', 82 | '火石寨乡(16817)': '30_2644_2647_16817' 83 | }, 84 | '泾源县(2649)': { 85 | '六盘山镇(16782)': '30_2644_2649_16782', 86 | '黄花乡(16786)': '30_2644_2649_16786', 87 | '泾河源镇(16781)': '30_2644_2649_16781', 88 | '兴盛乡(16785)': '30_2644_2649_16785', 89 | '大湾乡(16787)': '30_2644_2649_16787', 90 | '香水镇(16783)': '30_2644_2649_16783', 91 | '新民乡(16784)': '30_2644_2649_16784' 92 | }, 93 | '隆德县(2648)': { 94 | '奠安乡(16801)': '30_2644_2648_16801', 95 | '温堡乡(16800)': '30_2644_2648_16800', 96 | '观庄乡(16792)': '30_2644_2648_16792', 97 | '山河乡(16793)': '30_2644_2648_16793', 98 | '联财镇(16791)': '30_2644_2648_16791', 99 | '神林乡(16798)': '30_2644_2648_16798', 100 | '杨河乡(16795)': '30_2644_2648_16795', 101 | '沙塘镇(16790)': '30_2644_2648_16790', 102 | '县城内(52543)': '30_2644_2648_52543', 103 | '陈靳乡(16797)': '30_2644_2648_16797', 104 | '好水乡(16796)': '30_2644_2648_16796', 105 | '城关镇(16789)': '30_2644_2648_16789', 106 | '张程乡(16799)': '30_2644_2648_16799', 107 | '凤岭乡(16794)': '30_2644_2648_16794' 108 | }, 109 | '原州区(2651)': { 110 | '三营镇(16772)': '30_2644_2651_16772', 111 | '彭堡镇(16775)': '30_2644_2651_16775', 112 | '河川乡(16778)': '30_2644_2651_16778', 113 | '城区(51891)': '30_2644_2651_51891', 114 | '寨科乡(16780)': '30_2644_2651_16780', 115 | '开城镇(16773)': '30_2644_2651_16773', 116 | '黄铎堡镇(16770)': '30_2644_2651_16770', 117 | '头营镇(16776)': '30_2644_2651_16776', 118 | '张易镇(16774)': '30_2644_2651_16774', 119 | '官厅镇(16771)': '30_2644_2651_16771', 120 | '中河乡(16777)': '30_2644_2651_16777', 121 | '炭山乡(16779)': '30_2644_2651_16779' 122 | }, 123 | '彭阳县(2650)': { 124 | '草庙乡(16813)': '30_2644_2650_16813', 125 | '小岔乡(16809)': '30_2644_2650_16809', 126 | '白阳镇(16803)': '30_2644_2650_16803', 127 | '红河乡(16807)': '30_2644_2650_16807', 128 | '城阳乡(16806)': '30_2644_2650_16806', 129 | '古城镇(16802)': '30_2644_2650_16802', 130 | '新集乡(16805)': '30_2644_2650_16805', 131 | '孟塬乡(16810)': '30_2644_2650_16810', 132 | '交岔乡(16812)': '30_2644_2650_16812', 133 | '罗洼乡(16811)': '30_2644_2650_16811', 134 | '王洼镇(16804)': '30_2644_2650_16804', 135 | '冯庄乡(16808)': '30_2644_2650_16808' 136 | } 137 | }, 138 | '吴忠市(2637)': { 139 | '盐池县(2642)': { 140 | '冯记沟乡(16882)': '30_2637_2642_16882', 141 | '麻黄山乡(16883)': '30_2637_2642_16883', 142 | '大水坑镇(16878)': '30_2637_2642_16878', 143 | '惠安堡镇(16879)': '30_2637_2642_16879', 144 | '王乐井乡(16881)': '30_2637_2642_16881', 145 | '花马池镇(16877)': '30_2637_2642_16877', 146 | '高沙窝镇(16880)': '30_2637_2642_16880', 147 | '青山乡(16884)': '30_2637_2642_16884' 148 | }, 149 | '同心县(2641)': { 150 | '河西镇(16865)': '30_2637_2641_16865', 151 | '张家塬乡(16873)': '30_2637_2641_16873', 152 | '韦州镇(16867)': '30_2637_2641_16867', 153 | '石狮开发区管委会(16875)': '30_2637_2641_16875', 154 | '兴隆乡(16874)': '30_2637_2641_16874', 155 | '预旺镇(16868)': '30_2637_2641_16868', 156 | '窑山管委会(16876)': '30_2637_2641_16876', 157 | '田老庄乡(16871)': '30_2637_2641_16871', 158 | '豫海镇(16866)': '30_2637_2641_16866', 159 | '王团镇(16869)': '30_2637_2641_16869', 160 | '下马关镇(16864)': '30_2637_2641_16864', 161 | '丁塘镇(16870)': '30_2637_2641_16870', 162 | '马高庄乡(16872)': '30_2637_2641_16872' 163 | }, 164 | '红寺堡开发区(2643)': { 165 | '工业园区管委会(16837)': '30_2637_2643_16837', 166 | '太阳山镇(16834)': '30_2637_2643_16834', 167 | '南川乡(16836)': '30_2637_2643_16836', 168 | '红寺堡镇(16833)': '30_2637_2643_16833', 169 | '大河乡(16835)': '30_2637_2643_16835' 170 | }, 171 | '利通区(2966)': { 172 | '金银滩镇(16838)': '30_2637_2966_16838', 173 | '上桥镇(16842)': '30_2637_2966_16842', 174 | '金星镇(16845)': '30_2637_2966_16845', 175 | '孙家滩管委会(16850)': '30_2637_2966_16850', 176 | '太阳山开发区(16851)': '30_2637_2966_16851', 177 | '扁担沟镇(16839)': '30_2637_2966_16839', 178 | '高闸镇(16844)': '30_2637_2966_16844', 179 | '郭家桥乡(16848)': '30_2637_2966_16848', 180 | '板桥乡(16849)': '30_2637_2966_16849', 181 | '胜利镇(16840)': '30_2637_2966_16840', 182 | '古城镇(16841)': '30_2637_2966_16841', 183 | '马莲渠乡(16846)': '30_2637_2966_16846', 184 | '巴浪湖农场(16852)': '30_2637_2966_16852', 185 | '金积镇(16843)': '30_2637_2966_16843', 186 | '东塔寺乡(16847)': '30_2637_2966_16847' 187 | }, 188 | '青铜峡市(2638)': { 189 | '连湖农场(16862)': '30_2637_2638_16862', 190 | '小坝镇(16856)': '30_2637_2638_16856', 191 | '邵刚镇(16861)': '30_2637_2638_16861', 192 | '大坝镇(16857)': '30_2637_2638_16857', 193 | '陈袁滩镇(16855)': '30_2637_2638_16855', 194 | '城区(52527)': '30_2637_2638_52527', 195 | '树新林场(16863)': '30_2637_2638_16863', 196 | '青铜峡镇(16854)': '30_2637_2638_16854', 197 | '叶盛镇(16858)': '30_2637_2638_16858', 198 | '瞿靖镇(16859)': '30_2637_2638_16859', 199 | '峡口镇(16860)': '30_2637_2638_16860' 200 | } 201 | }, 202 | '石嘴山市(2632)': { 203 | '惠农区(2635)': { 204 | '农林牧场(16751)': '30_2632_2635_16751', 205 | '城区(52531)': '30_2632_2635_52531', 206 | '礼和乡(16749)': '30_2632_2635_16749', 207 | '园艺镇(16746)': '30_2632_2635_16746', 208 | '红果子镇(16744)': '30_2632_2635_16744', 209 | '庙台乡(16748)': '30_2632_2635_16748', 210 | '良种繁殖场(16750)': '30_2632_2635_16750', 211 | '尾闸镇(16745)': '30_2632_2635_16745', 212 | '简泉农场(16752)': '30_2632_2635_16752', 213 | '燕子墩乡(16747)': '30_2632_2635_16747' 214 | }, 215 | '大武口区(2636)': { 216 | '城区(52532)': '30_2632_2636_52532', 217 | '星海镇(16711)': '30_2632_2636_16711' 218 | }, 219 | '平罗县(2633)': { 220 | '姚伏镇(16757)': '30_2632_2633_16757', 221 | '红崖子乡(16760)': '30_2632_2633_16760', 222 | '通伏乡(16764)': '30_2632_2633_16764', 223 | '高仁乡(16765)': '30_2632_2633_16765', 224 | '前进农场(16766)': '30_2632_2633_16766', 225 | '县城内(37673)': '30_2632_2633_37673', 226 | '黄渠桥镇(16753)': '30_2632_2633_16753', 227 | '高庄乡(16761)': '30_2632_2633_16761', 228 | '崇岗镇(16758)': '30_2632_2633_16758', 229 | '宝丰镇(16755)': '30_2632_2633_16755', 230 | '灵沙乡(16762)': '30_2632_2633_16762', 231 | '渠口乡(16763)': '30_2632_2633_16763', 232 | '陶乐镇(16759)': '30_2632_2633_16759', 233 | '城关镇(16754)': '30_2632_2633_16754', 234 | '头闸镇(16756)': '30_2632_2633_16756' 235 | } 236 | }, 237 | '银川市(2628)': { 238 | '永宁县(2630)': { 239 | '黄羊滩农场(22041)': '30_2628_2630_22041', 240 | '李俊镇(22036)': '30_2628_2630_22036', 241 | '玉泉营农场(22042)': '30_2628_2630_22042', 242 | '闽宁镇(22039)': '30_2628_2630_22039', 243 | '城区(37671)': '30_2628_2630_37671', 244 | '望远镇(22037)': '30_2628_2630_22037', 245 | '望洪镇(22038)': '30_2628_2630_22038', 246 | '胜利乡(22040)': '30_2628_2630_22040' 247 | }, 248 | '灵武市(2629)': { 249 | '临河镇(22021)': '30_2628_2629_22021', 250 | '郝家桥镇(22016)': '30_2628_2629_22016', 251 | '马家滩镇(22017)': '30_2628_2629_22017', 252 | '白土岗乡(22023)': '30_2628_2629_22023', 253 | '宁东镇(22020)': '30_2628_2629_22020', 254 | '狼皮子梁管委会(22024)': '30_2628_2629_22024', 255 | '东塔镇(22018)': '30_2628_2629_22018', 256 | '城区(52516)': '30_2628_2629_52516', 257 | '梧桐树乡(22022)': '30_2628_2629_22022', 258 | '灵武农场(22025)': '30_2628_2629_22025', 259 | '崇兴镇(22019)': '30_2628_2629_22019' 260 | }, 261 | '兴庆区(21649)': { 262 | '月牙湖乡(22013)': '30_2628_21649_22013', 263 | '城区(51876)': '30_2628_21649_51876', 264 | '通贵乡(22014)': '30_2628_21649_22014', 265 | '掌政镇(22011)': '30_2628_21649_22011', 266 | '大新镇(22012)': '30_2628_21649_22012' 267 | }, 268 | '西夏区(21651)': { 269 | '平吉堡(53591)': '30_2628_21651_53591', 270 | '镇北堡镇(21998)': '30_2628_21651_21998', 271 | '城区(51877)': '30_2628_21651_51877', 272 | '兴泾镇(21999)': '30_2628_21651_21999' 273 | }, 274 | '贺兰县(2631)': { 275 | '暖泉农场(22034)': '30_2628_2631_22034', 276 | '南梁台子管委会(22031)': '30_2628_2631_22031', 277 | '金贵镇(22027)': '30_2628_2631_22027', 278 | '洪广镇(22029)': '30_2628_2631_22029', 279 | '宁夏原种场(22032)': '30_2628_2631_22032', 280 | '京星农牧场(22033)': '30_2628_2631_22033', 281 | '县城内(37672)': '30_2628_2631_37672', 282 | '常信乡(22030)': '30_2628_2631_22030', 283 | '立岗镇(22028)': '30_2628_2631_22028' 284 | }, 285 | '金凤区(21650)': { 286 | '丰登镇(21989)': '30_2628_21650_21989', 287 | '西湖农场(21990)': '30_2628_21650_21990', 288 | '良田镇(21988)': '30_2628_21650_21988', 289 | '城区(52518)': '30_2628_21650_52518', 290 | '银川林场(21991)': '30_2628_21650_21991' 291 | } 292 | } 293 | } 294 | } -------------------------------------------------------------------------------- /TEST/area_id/32.台湾.txt: -------------------------------------------------------------------------------- 1 | { 2 | '台湾(32)': { 3 | '台湾(2768)': { 4 | '云林(53515)': '32_2768_53515', 5 | '苗栗(53506)': '32_2768_53506', 6 | '南投(53507)': '32_2768_53507', 7 | '新北(53516)': '32_2768_53516', 8 | '台南(53512)': '32_2768_53512', 9 | '基隆(53503)': '32_2768_53503', 10 | '澎湖(53508)': '32_2768_53508', 11 | '新竹(53499)': '32_2768_53499', 12 | '金门(53504)': '32_2768_53504', 13 | '桃园(53514)': '32_2768_53514', 14 | '宜兰(53501)': '32_2768_53501', 15 | '连江(53505)': '32_2768_53505', 16 | '台北(53513)': '32_2768_53513', 17 | '台中(53511)': '32_2768_53511', 18 | '屏东(53509)': '32_2768_53509', 19 | '彰化(53497)': '32_2768_53497', 20 | '台东(53510)': '32_2768_53510', 21 | '花莲(53500)': '32_2768_53500', 22 | '高雄(53502)': '32_2768_53502', 23 | '嘉义(53498)': '32_2768_53498' 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /TEST/area_id/52993.港澳.txt: -------------------------------------------------------------------------------- 1 | { 2 | '港澳(52993)': { 3 | '香港特别行政区(52994)': { 4 | '葵青区(53005)': '52993_52994_53005', 5 | '黄大仙区(53002)': '52993_52994_53002', 6 | '新界(53015)': '52993_52994_53015', 7 | '九龙(53014)': '52993_52994_53014', 8 | '大埔区(53010)': '52993_52994_53010', 9 | '中西区(52996)': '52993_52994_52996', 10 | '离岛区(53004)': '52993_52994_53004', 11 | '油尖旺区(53003)': '52993_52994_53003', 12 | '元朗区(53012)': '52993_52994_53012', 13 | '东区(52997)': '52993_52994_52997', 14 | '北区(53006)': '52993_52994_53006', 15 | '沙田区(53008)': '52993_52994_53008', 16 | '湾仔区(53001)': '52993_52994_53001', 17 | '九龙城区(52998)': '52993_52994_52998', 18 | '荃湾区(53011)': '52993_52994_53011', 19 | '西贡区(53007)': '52993_52994_53007', 20 | '深水埗区(53000)': '52993_52994_53000', 21 | '观塘区(52999)': '52993_52994_52999', 22 | '香港(53013)': '52993_52994_53013', 23 | '屯门区(53009)': '52993_52994_53009' 24 | }, 25 | '澳门特别行政区(52995)': { 26 | '凼仔(53018)': '52993_52995_53018', 27 | '路凼城(53019)': '52993_52995_53019', 28 | '路环(53020)': '52993_52995_53020', 29 | '澳门特别行政区(53016)': '52993_52995_53016', 30 | '澳门半岛(53017)': '52993_52995_53017' 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /TEST/area_id/53283.海外.txt: -------------------------------------------------------------------------------- 1 | { 2 | '海外(53283)': { 3 | '马提尼克(53306)': '53283_53306', 4 | '南苏丹(53325)': '53283_53325', 5 | '利比亚(53484)': '53283_53484', 6 | '洪都拉斯(53426)': '53283_53426', 7 | '关岛(53417)': '53283_53417', 8 | '莱索托(53467)': '53283_53467', 9 | '阿塞拜疆(53315)': '53283_53315', 10 | '肯尼亚(53463)': '53283_53463', 11 | '加拿大(53440)': '53283_53440', 12 | '荷兰(53423)': '53283_53423', 13 | '挪威(53330)': '53283_53330', 14 | '科摩罗(53457)': '53283_53457', 15 | '塔吉克斯坦(53402)': '53283_53402', 16 | '澳大利亚(53347)': '53283_53347', 17 | '苏丹(53385)': '53283_53385', 18 | '立陶宛(53481)': '53283_53481', 19 | '柬埔寨(53445)': '53283_53445', 20 | '亚美尼亚(53470)': '53283_53470', 21 | '安圭拉(53343)': '53283_53343', 22 | '以色列(53474)': '53283_53474', 23 | '阿鲁巴(53302)': '53283_53302', 24 | '比利时(53367)': '53283_53367', 25 | '哈萨克斯坦(53420)': '53283_53420', 26 | '佛得角(53406)': '53283_53406', 27 | '委内瑞拉(53439)': '53283_53439', 28 | '新加坡(53456)': '53283_53456', 29 | '圭亚那(53419)': '53283_53419', 30 | '厄立特里亚(53397)': '53283_53397', 31 | '尼加拉瓜(53327)': '53283_53327', 32 | '马其顿(53305)': '53283_53305', 33 | '卡塔尔(53453)': '53283_53453', 34 | '加蓬(53443)': '53283_53443', 35 | '拉脱维亚(53466)': '53283_53466', 36 | '尼日尔(53328)': '53283_53328', 37 | '萨尔瓦多(53340)': '53283_53340', 38 | '德国(53389)': '53283_53389', 39 | '葡萄牙(53332)': '53283_53332', 40 | '阿尔巴尼亚(53285)': '53283_53285', 41 | '格陵兰(53412)': '53283_53412', 42 | '印度(53477)': '53283_53477', 43 | '巴基斯坦(53353)': '53283_53353', 44 | '百慕大三角(53361)': '53283_53361', 45 | '卢旺达(53493)': '53283_53493', 46 | '圣马力诺(53371)': '53283_53371', 47 | '尼泊尔(53326)': '53283_53326', 48 | '巴拉圭(53354)': '53283_53354', 49 | '塞舌尔(53360)': '53283_53360', 50 | '日本(53333)': '53283_53333', 51 | '叙利亚(53468)': '53283_53468', 52 | '马尔代夫(53298)': '53283_53298', 53 | '埃塞俄比亚(53336)': '53283_53336', 54 | '丹麦(53387)': '53283_53387', 55 | '芬兰(53405)': '53283_53405', 56 | '哥伦比亚(53409)': '53283_53409', 57 | '纳米比亚(53323)': '53283_53323', 58 | '新西兰(53462)': '53283_53462', 59 | '斯洛伐克(53378)': '53283_53378', 60 | '科特迪瓦(53458)': '53283_53458', 61 | '新喀里多尼亚(53459)': '53283_53459', 62 | '捷克(53447)': '53283_53447', 63 | '斯洛文尼亚(53381)': '53283_53381', 64 | '乍得(53489)': '53283_53489', 65 | '意大利(53475)': '53283_53475', 66 | '波多黎各(53370)': '53283_53370', 67 | '巴巴多斯(53349)': '53283_53349', 68 | '法罗群岛(53399)': '53283_53399', 69 | '塞浦路斯(53358)': '53283_53358', 70 | '布隆迪(53384)': '53283_53384', 71 | '波兰(53372)': '53283_53372', 72 | '爱尔兰(53337)': '53283_53337', 73 | '多哥(53392)': '53283_53392', 74 | '布基纳法索(53382)': '53283_53382', 75 | '毛里塔尼亚(53308)': '53283_53308', 76 | '圣基茨和尼维斯(53366)': '53283_53366', 77 | '希腊(53454)': '53283_53454', 78 | '赞比亚(53488)': '53283_53488', 79 | '伯利兹(53377)': '53283_53377', 80 | '尼日利亚(53329)': '53283_53329', 81 | '黎巴嫩(53479)': '53283_53479', 82 | '索马里(53393)': '53283_53393', 83 | '智利(53490)': '53283_53490', 84 | '贝宁(53365)': '53283_53365', 85 | '伊朗(53473)': '53283_53473', 86 | '几内亚(53434)': '53283_53434', 87 | '摩尔多瓦(53317)': '53283_53317', 88 | '格林纳达(53411)': '53283_53411', 89 | '东帝汶(53391)': '53283_53391', 90 | '塞拉利昂(53348)': '53283_53348', 91 | '泰国(53404)': '53283_53404', 92 | '帕劳(53331)': '53283_53331', 93 | '刚果(53408)': '53283_53408', 94 | '伊拉克(53472)': '53283_53472', 95 | '美属维尔京群岛(53310)': '53283_53310', 96 | '墨西哥(53322)': '53283_53322', 97 | '塞内加尔(53351)': '53283_53351', 98 | '乌拉圭(53448)': '53283_53448', 99 | '巴哈马(53352)': '53283_53352', 100 | '老挝(53476)': '53283_53476', 101 | '赤道几内亚(53386)': '53283_53386', 102 | '巴林(53355)': '53283_53355', 103 | '英国(53480)': '53283_53480', 104 | '爱沙尼亚(53338)': '53283_53338', 105 | '秘鲁(53314)': '53283_53314', 106 | '越南(53487)': '53283_53487', 107 | '俄罗斯(53395)': '53283_53395', 108 | '马来西亚(53303)': '53283_53303', 109 | '法国(53398)': '53283_53398', 110 | '毛里求斯(53307)': '53283_53307', 111 | '格鲁吉亚(53413)': '53283_53413', 112 | '玻利维亚(53375)': '53283_53375', 113 | '几内亚比绍(53436)': '53283_53436', 114 | '安提瓜和巴布达(53344)': '53283_53344', 115 | '卢森堡(53491)': '53283_53491', 116 | '古巴(53414)': '53283_53414', 117 | '阿曼(53495)': '53283_53495', 118 | '巴拿马(53356)': '53283_53356', 119 | '突尼斯(53431)': '53283_53431', 120 | '吉布提(53430)': '53283_53430', 121 | '斯里兰卡(53376)': '53283_53376', 122 | '印度尼西亚(53478)': '53283_53478', 123 | '塞尔维亚(53345)': '53283_53345', 124 | '西班牙(53452)': '53283_53452', 125 | '乌克兰(53446)': '53283_53446', 126 | '厄瓜多尔(53396)': '53283_53396', 127 | '孟加拉(53313)': '53283_53313', 128 | '阿根廷(53297)': '53283_53297', 129 | '瓜德罗普(53415)': '53283_53415', 130 | '马达加斯加(53496)': '53283_53496', 131 | '沙特阿拉伯(53363)': '53283_53363', 132 | '约旦(53485)': '53283_53485', 133 | '也门(53471)': '53283_53471', 134 | '美国(53309)': { 135 | 'NEWYORK(53881)': { 136 | 'MANHATTAN(53886)': '53283_53309_53881_53886', 137 | 'BOWLINGGREEN(53888)': '53283_53309_53881_53888', 138 | 'GPO(53883)': '53283_53309_53881_53883', 139 | 'EMPIRESTATE(53882)': '53283_53309_53881_53882', 140 | 'NEWYORKCITY(53887)': '53283_53309_53881_53887', 141 | 'GREELEYSQUARE(53884)': '53283_53309_53881_53884', 142 | 'MACYSFINANCE(53885)': '53283_53309_53881_53885' 143 | } 144 | }, 145 | '海地(53421)': '53283_53421', 146 | '匈牙利(53465)': '53283_53465', 147 | '喀麦隆(53451)': '53283_53451', 148 | '列支敦士登(53486)': '53283_53486', 149 | '埃及(53320)': '53283_53320', 150 | '巴布亚新几内亚(53350)': '53283_53350', 151 | '莫桑比克(53321)': '53283_53321', 152 | '萨摩亚(53342)': '53283_53342', 153 | '不丹(53380)': '53283_53380', 154 | '科威特(53460)': '53283_53460', 155 | '斐济群岛(53403)': '53283_53403', 156 | '黑山(53425)': '53283_53425', 157 | '摩纳哥(53319)': '53283_53319', 158 | '奥地利(53346)': '53283_53346', 159 | '汤加(53424)': '53283_53424', 160 | '利比里亚(53482)': '53283_53482', 161 | '加纳(53442)': '53283_53442', 162 | '土耳其(53435)': '53283_53435', 163 | '克罗地亚(53461)': '53283_53461', 164 | '白俄罗斯(53359)': '53283_53359', 165 | '巴西(53357)': '53283_53357', 166 | '韩国(53422)': '53283_53422', 167 | '多米尼加共和国(53394)': '53283_53394', 168 | '马里(53304)': '53283_53304', 169 | '苏里南(53388)': '53283_53388', 170 | '波斯尼亚和黑塞哥维那(53374)': '53283_53374', 171 | '圣卢西亚(53369)': '53283_53369', 172 | '瓦努阿图(53437)': '53283_53437', 173 | '密克罗尼西亚(53316)': '53283_53316', 174 | '法属圭亚那(53400)': '53283_53400', 175 | '特立尼达和多巴哥(53429)': '53283_53429', 176 | '博茨瓦纳(53379)': '53283_53379', 177 | '摩洛哥(53318)': '53283_53318', 178 | '特克斯和凯科斯群岛(53427)': '53283_53427', 179 | '坦桑尼亚(53418)': '53283_53418', 180 | '哥斯达黎加(53410)': '53283_53410', 181 | '图瓦卢(53433)': '53283_53433', 182 | '文莱(53441)': '53283_53441', 183 | '牙买加(53469)': '53283_53469', 184 | '库克群岛(53464)': '53283_53464', 185 | '圣多美和普林西比(53364)': '53283_53364', 186 | '乌兹别克斯坦(53450)': '53283_53450', 187 | '阿联酋(53300)': '53283_53300', 188 | '冰岛(53368)': '53283_53368', 189 | '罗马尼亚(53494)': '53283_53494', 190 | '安哥拉(53341)': '53283_53341', 191 | '开曼群岛(53455)': '53283_53455', 192 | '蒙特塞拉特(53312)': '53283_53312', 193 | '瑞士(53335)': '53283_53335', 194 | '美属萨摩亚(53286)': '53283_53286', 195 | '吉尔吉斯斯坦(53432)': '53283_53432', 196 | '安道尔(53339)': { 197 | 'CANILLO(53880)': '53283_53339_53880', 198 | 'ORDINO(53879)': '53283_53339_53879' 199 | }, 200 | '瑞典(53334)': '53283_53334', 201 | '马耳他(53299)': '53283_53299', 202 | '南非(53324)': '53283_53324', 203 | '津巴布韦(53449)': '53283_53449', 204 | '中非(53492)': '53283_53492', 205 | '所罗门群岛(53390)': '53283_53390', 206 | '蒙古(53311)': '53283_53311', 207 | '阿尔及利亚(53296)': '53283_53296', 208 | '危地马拉(53438)': '53283_53438', 209 | '圣文森特和格林纳丁斯(53373)': '53283_53373', 210 | '马拉维(53301)': '53283_53301', 211 | '保加利亚(53362)': '53283_53362', 212 | '菲律宾(53401)': '53283_53401', 213 | '阿富汗(53284)': '53283_53284', 214 | '冈比亚(53407)': '53283_53407', 215 | '斯威士兰(53383)': '53283_53383', 216 | '英属维尔京群岛(53483)': '53283_53483' 217 | } 218 | } -------------------------------------------------------------------------------- /TEST/area_id/84.钓鱼岛.txt: -------------------------------------------------------------------------------- 1 | { 2 | '钓鱼岛(84)': { 3 | '钓鱼岛(1310)': { 4 | '钓鱼岛全区(53281)': '84_1310_53281', 5 | '钓鱼岛县1(53263)': '84_1310_53263', 6 | '钓鱼岛县(53262)': '84_1310_53262' 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /TEST/area_id/README.md: -------------------------------------------------------------------------------- 1 | # 地址id如何获取? 2 | 3 | ## 方法一 4 | 5 | 在本文件夹中根据地址查询对应的文件。 6 | 7 | ## 方法二 8 | 9 | 在商品页面(例如 https://item.jd.com/1178879.html) 打开开发者工具,在 Console 中执行以下 Javascript 代码: 10 | 11 | ```js 12 | var el = document.getElementsByClassName("ui-area-text")[0] 13 | var area_name = el.getAttribute("title") 14 | var area_id = el.getAttribute("data-id").replace(/-/g, "_") 15 | console.log(area_name) 16 | console.log(area_id) 17 | ``` 18 | 19 | ## 方法三 20 | 21 | 运行本文件夹中的 Python 脚本,根据提示逐级选择区域。感谢 @6r6 提供脚本~ 22 | 23 | ```sh 24 | python get_area_id.py 25 | ``` 26 | -------------------------------------------------------------------------------- /TEST/area_id/get_area_id.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | 4 | """ 5 | area参数自助生成 6 | 运行脚本,根据提示逐级选择区域即可 7 | """ 8 | 9 | import json 10 | 11 | import requests 12 | 13 | provinces = [ 14 | {'name': '北京', 'id': 1}, {'name': '上海', 'id': 2}, {'name': '天津', 'id': 3}, 15 | {'name': '重庆', 'id': 4}, {'name': '河北', 'id': 5}, {'name': '山西', 'id': 6}, 16 | {'name': '河南', 'id': 7}, {'name': '辽宁', 'id': 8}, {'name': '吉林', 'id': 9}, 17 | {'name': '黑龙江', 'id': 10}, {'name': '内蒙古', 'id': 11}, {'name': '江苏', 'id': 12}, 18 | {'name': '山东', 'id': 13}, {'name': '安徽', 'id': 14}, {'name': '浙江', 'id': 15}, 19 | {'name': '福建', 'id': 16}, {'name': '湖北', 'id': 17}, {'name': '湖南', 'id': 18}, 20 | {'name': '广东', 'id': 19}, {'name': '广西', 'id': 20}, {'name': '江西', 'id': 21}, 21 | {'name': '四川', 'id': 22}, {'name': '海南', 'id': 23}, {'name': '贵州', 'id': 24}, 22 | {'name': '云南', 'id': 25}, {'name': '西藏', 'id': 26}, {'name': '陕西', 'id': 27}, 23 | {'name': '甘肃', 'id': 28}, {'name': '青海', 'id': 29}, {'name': '宁夏', 'id': 30}, 24 | {'name': '新疆', 'id': 31}, {'name': '台湾', 'id': 32}, {'name': '港澳', 'id': 52993}, 25 | {'name': '钓鱼岛', 'id': 84} 26 | ] 27 | 28 | 29 | def get_area_by_id(_id): 30 | base_uri = 'https://d.jd.com/area/get' 31 | payload = {'fid': _id} 32 | resp = requests.get(url=base_uri, params=payload) 33 | return json.loads(resp.text) 34 | 35 | 36 | def print_area(area_list): 37 | for area_item in area_list: 38 | print(area_item) 39 | print('【{}】 {}'.format(area_item['id'], area_item['name'])) 40 | print('-------------------------------------------------') 41 | 42 | 43 | def select_area(area_list): 44 | while True: 45 | user_input = input('请继续输入编号:').strip() 46 | selected_area = [area for area in area_list if str(area['id']) == user_input or area['name'] == user_input] 47 | if not selected_area: 48 | print('编号错误,请重新输入') 49 | continue 50 | print('已选择:{}'.format(selected_area[0]['name'])) 51 | return selected_area[0] 52 | 53 | 54 | def main(): 55 | print_area(provinces) 56 | province = select_area(provinces) 57 | 58 | cities = get_area_by_id(province['id']) 59 | print_area(cities) 60 | city = select_area(cities) 61 | 62 | districts = get_area_by_id(city['id']) 63 | print_area(districts) 64 | district = select_area(districts) 65 | 66 | streets = get_area_by_id(district['id']) 67 | if not streets: 68 | print( 69 | '您选择的区域为:{}-{}-{},id:{}_{}_{}'.format( 70 | province['name'], city['name'], district['name'], 71 | province['id'], city['id'], district['id'] 72 | ) 73 | ) 74 | return 75 | 76 | print_area(streets) 77 | street = select_area(streets) 78 | print( 79 | '您选择的区域为:{}-{}-{}-{},id:{}_{}_{}_{}'.format( 80 | province['name'], city['name'], district['name'], street['name'], 81 | province['id'], city['id'], district['id'], street['id'] 82 | ) 83 | ) 84 | 85 | 86 | if __name__ == '__main__': 87 | main() 88 | -------------------------------------------------------------------------------- /TEST/get_eid_fp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
获取中……
5 |
6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 26 | -------------------------------------------------------------------------------- /TEST/pack.py: -------------------------------------------------------------------------------- 1 | command = '' -------------------------------------------------------------------------------- /TEST/py2app_setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is an example of py2app py2app_setup.py script for freezing your pywebview 3 | application 4 | Usage: 5 | python py2app_setup.py py2app 6 | """ 7 | 8 | import os 9 | from setuptools import setup 10 | 11 | 12 | def tree(src): 13 | return [(root, map(lambda f: os.path.join(root, f), files)) 14 | for (root, dirs, files) in os.walk(os.path.normpath(src))] 15 | 16 | 17 | ENTRY_POINT = ['runserver.py'] 18 | 19 | DATA_FILES = tree('DATA_FILES_DIR') + tree('DATA_FILE_DIR2') 20 | OPTIONS = {'argv_emulation': False, 21 | 'strip': True, 22 | #'iconfile': 'icon.icns', # uncomment to include an icon 23 | 'includes': ['WebKit', 'Foundation', 'webview']} 24 | 25 | setup( 26 | app=ENTRY_POINT, 27 | data_files=DATA_FILES, 28 | options={'py2app': OPTIONS}, 29 | setup_requires=['py2app'], 30 | ) -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from Config.settings import config 3 | from Core.spider import Waiter 4 | 5 | if __name__ == '__main__': 6 | choiceList = """ 7 | ===== 注意 ===== 8 | 使用前请按要求填写config.ini中的信息 9 | 10 | 功能列表: 11 | 1.自动加购物车,缺货等待上架自动下单 12 | 2.自动定时加购物车下单(普通商品,非秒杀抢购) 13 | """ 14 | print(choiceList) 15 | choice_function = '' 16 | if choice_function == '': 17 | choice_function = input('请选择:') 18 | if choice_function == '1': 19 | waiter = Waiter() 20 | waiter.waitForSell() 21 | elif choice_function == '2': 22 | waiter = Waiter() 23 | waiter.waitTimeForSell() 24 | else: 25 | print('没有此功能') 26 | sys.exit(1) 27 | 28 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | lxml==4.5.1 2 | bs4==0.0.1 3 | requests==2.24.0 -------------------------------------------------------------------------------- /runserver.py: -------------------------------------------------------------------------------- 1 | from Core.core import main 2 | from Logger.logger import logger 3 | from Scheduler.scheduler import Timer 4 | from Config.settings import config 5 | from Server.server import server 6 | from threading import Thread 7 | from GUI.gui import gui 8 | from concurrent.futures import ProcessPoolExecutor 9 | 10 | PROCESS_MODEL = config.settings("Server", "PROCESS_MODEL") 11 | SCHEDULER = config.settings("Scheduler", "START_USING") 12 | SERVER = config.settings("Server", "START_USING") 13 | GUI = config.settings("GUI", "START_USING") 14 | 15 | 16 | def running(): 17 | if not SCHEDULER: 18 | thread_main = Thread(target=main) 19 | thread_main.start() 20 | else: # 调度器开启后main函数将被scheduler调度器代理,开启定时执行main 21 | startTime = config.settings("Scheduler", "START_TIME") 22 | skipWeekend = config.settings("Scheduler", "SKIP_WEEKEND") 23 | scheduler = Timer(task=main, startTime=startTime, skipWeekend=skipWeekend) 24 | thread_scheduler = Thread(target=scheduler.schedule) 25 | thread_scheduler.start() 26 | if SERVER: 27 | if PROCESS_MODEL: 28 | work_count = config.settings("Server", "PROCESS_COUNT") 29 | server_process(work_count) 30 | else: 31 | thread_server = Thread(target=server) 32 | thread_server.start() 33 | if GUI: 34 | gui() 35 | 36 | 37 | def server_process(work_count=4): 38 | with ProcessPoolExecutor(work_count) as pool: 39 | for i in range(work_count): 40 | pool.submit(server()) 41 | 42 | 43 | if __name__ == "__main__": 44 | DEBUG = config.settings("Debug", "DEBUG") 45 | if DEBUG: 46 | logger.info("\n===== DEBUG MODE =====") 47 | main() 48 | else: 49 | running() 50 | --------------------------------------------------------------------------------