mirror of
https://git.oceanpay.cc/danial/kami_walmart_card.git
synced 2025-12-18 22:35:54 +00:00
first commit
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
14
.idea/deployment.xml
generated
Normal file
14
.idea/deployment.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PublishConfigData">
|
||||||
|
<serverData>
|
||||||
|
<paths name="root@120.24.83.163:22 password">
|
||||||
|
<serverdata>
|
||||||
|
<mappings>
|
||||||
|
<mapping local="$PROJECT_DIR$" web="/" />
|
||||||
|
</mappings>
|
||||||
|
</serverdata>
|
||||||
|
</paths>
|
||||||
|
</serverData>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
23
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
23
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<value>
|
||||||
|
<list size="9">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="uwsgi" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="opencv-python" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="opencv-python-headless" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="pyexecjs" />
|
||||||
|
<item index="4" class="java.lang.String" itemvalue="gevent" />
|
||||||
|
<item index="5" class="java.lang.String" itemvalue="gunicorn" />
|
||||||
|
<item index="6" class="java.lang.String" itemvalue="selenium" />
|
||||||
|
<item index="7" class="java.lang.String" itemvalue="urllib3" />
|
||||||
|
<item index="8" class="java.lang.String" itemvalue="redis" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptSettings">
|
||||||
|
<option name="languageLevel" value="ES6" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (walmart_bind) (2)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/walmart_bind.iml" filepath="$PROJECT_DIR$/.idea/walmart_bind.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
19
.idea/walmart_bind.iml
generated
Normal file
19
.idea/walmart_bind.iml
generated
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="Flask">
|
||||||
|
<option name="enabled" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.8 (walmart_bind) (2)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="TemplatesService">
|
||||||
|
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
|
||||||
|
<option name="TEMPLATE_FOLDERS">
|
||||||
|
<list>
|
||||||
|
<option value="$MODULE_DIR$/../walmart_bind\templates" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
26
Dockerfile
Normal file
26
Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# 基于 Python 3.8.6 镜像
|
||||||
|
FROM python:3.8.6
|
||||||
|
|
||||||
|
# 修改apt-get源地址为阿里云镜像
|
||||||
|
RUN echo "" > /etc/apt/sources.list && \
|
||||||
|
echo "deb http://mirrors.aliyun.com/debian buster main" >> /etc/apt/sources.list && \
|
||||||
|
echo "deb http://mirrors.aliyun.com/debian-security buster/updates main" >> /etc/apt/sources.list && \
|
||||||
|
echo "deb http://mirrors.aliyun.com/debian buster-updates main" >> /etc/apt/sources.list
|
||||||
|
|
||||||
|
# 复制项目
|
||||||
|
ADD . /app
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 暴露容器端口
|
||||||
|
EXPOSE 5009
|
||||||
|
|
||||||
|
# python环境包
|
||||||
|
RUN pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple gunicorn gevent
|
||||||
|
RUN pip3 install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
|
||||||
|
|
||||||
|
ENV FLASK_APP=app.py
|
||||||
|
|
||||||
|
# 运行Django应用
|
||||||
|
CMD ["gunicorn", "-c", "gun.conf", "app:app"]
|
||||||
1
README.md
Normal file
1
README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
docker run --name walmart_bind -p 5008:5008 -v /home/www/walmart_bind:/app -d walmart_bind
|
||||||
BIN
__pycache__/app.cpython-38.pyc
Normal file
BIN
__pycache__/app.cpython-38.pyc
Normal file
Binary file not shown.
BIN
__pycache__/gun.cpython-38.pyc
Normal file
BIN
__pycache__/gun.cpython-38.pyc
Normal file
Binary file not shown.
BIN
__pycache__/logger.cpython-38.pyc
Normal file
BIN
__pycache__/logger.cpython-38.pyc
Normal file
Binary file not shown.
BIN
__pycache__/spiders.cpython-38.pyc
Normal file
BIN
__pycache__/spiders.cpython-38.pyc
Normal file
Binary file not shown.
47
app.py
Normal file
47
app.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
|
from flask import Flask, request
|
||||||
|
from flask_cors import CORS
|
||||||
|
|
||||||
|
from spiders import WalMartCardSpider
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
# 跨域
|
||||||
|
CORS(app)
|
||||||
|
# 设置日志记录级别,可以根据需要调整
|
||||||
|
app.logger.setLevel(logging.INFO)
|
||||||
|
# 创建按天滚动的日志处理程序
|
||||||
|
log_file = 'walmart_bind.log'
|
||||||
|
handler = TimedRotatingFileHandler(log_file, when='midnight', interval=1, backupCount=7, encoding="utf-8")
|
||||||
|
handler.suffix = '%Y-%m-%d.log' # 日志文件名的后缀格式,这里按日期命名
|
||||||
|
handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
|
||||||
|
app.logger.addHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/walmart/bind/card', methods=['GET', 'POST'], strict_slashes=False)
|
||||||
|
def hello_world():
|
||||||
|
if request.method == 'GET':
|
||||||
|
return 'okk'
|
||||||
|
elif request.method == 'POST':
|
||||||
|
# 接收参数
|
||||||
|
data = json.loads(request.get_data())
|
||||||
|
card_num = data.get("card_num")
|
||||||
|
cookies = data.get("cookies")
|
||||||
|
card_pwd = data.get("card_pwd")
|
||||||
|
order_num = data.get("order_num")
|
||||||
|
# 日志打印
|
||||||
|
app.logger.info(f"订单ID:{order_num},请求参数:{data}")
|
||||||
|
res = WalMartCardSpider(
|
||||||
|
cookies=cookies,
|
||||||
|
card_num=card_num,
|
||||||
|
card_pwd=card_pwd,
|
||||||
|
order_num=order_num,
|
||||||
|
app=app
|
||||||
|
).run()
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host="0.0.0.0", port=5009)
|
||||||
70
check_money.py
Normal file
70
check_money.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class CheckMoney:
|
||||||
|
|
||||||
|
def __init__(self, cookies):
|
||||||
|
self.cookies = cookies
|
||||||
|
self.url = "https://apicard.swiftpass.cn/app/card/mem/more/consume.json"
|
||||||
|
|
||||||
|
def md5_encrypt(self, data):
|
||||||
|
# 创建 MD5 哈希对象
|
||||||
|
md5_hash = hashlib.md5()
|
||||||
|
# 更新哈希对象以包含数据
|
||||||
|
md5_hash.update(data.encode('utf-8')) # 确保数据为字节类型
|
||||||
|
# 返回十六进制表示的大写 MD5 值
|
||||||
|
return md5_hash.hexdigest().upper()
|
||||||
|
|
||||||
|
def query_balance(self):
|
||||||
|
timestamp = f"{int(time.time() * 1000)}"
|
||||||
|
data = '{"bizType":2,"currentPage":0,"pageSize":0,"sign":"%s"}%saab23c732038417795a0f5caf70899b7' % (
|
||||||
|
self.cookies, timestamp
|
||||||
|
)
|
||||||
|
signature = self.md5_encrypt(data)
|
||||||
|
headers = {
|
||||||
|
"Host": "apicard.swiftpass.cn",
|
||||||
|
"version": "16",
|
||||||
|
"timestamp": f"{timestamp}",
|
||||||
|
"signature": signature,
|
||||||
|
"xweb_xhr": "1",
|
||||||
|
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c2d)XWEB/11581",
|
||||||
|
"content-type": "application/json",
|
||||||
|
"accept": "*/*",
|
||||||
|
"sec-fetch-site": "cross-site",
|
||||||
|
"sec-fetch-mode": "cors",
|
||||||
|
"sec-fetch-dest": "empty",
|
||||||
|
"referer": "https://servicewechat.com/wx81d3e1fe4c2e11b4/158/page-frame.html",
|
||||||
|
"accept-language": "zh-CN,zh;q=0.9"
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
"bizType": 2,
|
||||||
|
"currentPage": 0,
|
||||||
|
"pageSize": 0,
|
||||||
|
"sign": self.cookies
|
||||||
|
}
|
||||||
|
data = json.dumps(data, separators=(',', ':'))
|
||||||
|
response = requests.post(self.url, headers=headers, data=data)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
res = self.query_balance()
|
||||||
|
print(res)
|
||||||
|
balance = res["data"]["balanceCnt"]
|
||||||
|
return {
|
||||||
|
"code": 100,
|
||||||
|
"data": {
|
||||||
|
"balance": balance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cookies = "5761d1dfeea1422990c7f0dace26dcd8@853540263801a118ff85ec686f23bbd7"
|
||||||
|
res = CheckMoney(
|
||||||
|
cookies=cookies
|
||||||
|
).run()
|
||||||
|
print(res)
|
||||||
19
gun.conf
Normal file
19
gun.conf
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# 并行工作进程数
|
||||||
|
workers = 2
|
||||||
|
# 指定每个工作者的线程数
|
||||||
|
threads = 4
|
||||||
|
# 监听内网端口80
|
||||||
|
bind = '0.0.0.0:5009'
|
||||||
|
# 工作模式协程
|
||||||
|
worker_class = 'gevent'
|
||||||
|
# 设置最大并发量
|
||||||
|
worker_connections = 2000
|
||||||
|
# 设置进程文件目录
|
||||||
|
pidfile = 'gunicorn.pid'
|
||||||
|
# 设置访问日志和错误信息日志路径
|
||||||
|
accesslog = '-'
|
||||||
|
errorlog = '-'
|
||||||
|
# 设置日志记录水平
|
||||||
|
loglevel = 'info'
|
||||||
|
# 代码发生变化是否自动重启
|
||||||
|
reload = True
|
||||||
1
gunicorn.pid
Normal file
1
gunicorn.pid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
29
logger.py
Normal file
29
logger.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from loguru import logger
|
||||||
|
import sys
|
||||||
|
import pytz
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# 移除默认的日志处理器
|
||||||
|
logger.remove()
|
||||||
|
|
||||||
|
# 设置中国时区
|
||||||
|
china_tz = pytz.timezone("Asia/Shanghai")
|
||||||
|
|
||||||
|
|
||||||
|
# 自定义时间格式函数
|
||||||
|
def custom_time():
|
||||||
|
return datetime.now(china_tz).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
|
||||||
|
# 添加新的处理器,输出到控制台
|
||||||
|
logger.add(sys.stdout, format="{time:YYYY-MM-DD HH:mm:ss} {level} {message}", level="INFO", serialize=False)
|
||||||
|
|
||||||
|
# 添加文件日志处理器
|
||||||
|
logger.add("walmart.log", rotation="20 MB", retention="30 days", format="{time:YYYY-MM-DD HH:mm:ss} {level} {message}")
|
||||||
|
|
||||||
|
# 创建一个带有时间信息的 Logger
|
||||||
|
logger = logger.bind(time=custom_time())
|
||||||
|
|
||||||
|
|
||||||
|
def get_logger():
|
||||||
|
return logger
|
||||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
flask==3.0.3
|
||||||
|
flask_cors==4.0.1
|
||||||
|
requests==2.32.3
|
||||||
|
urllib3==1.26.2
|
||||||
|
redis==5.0.7
|
||||||
387
spiders.py
Normal file
387
spiders.py
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
from json import JSONDecodeError
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import urllib3
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger()
|
||||||
|
|
||||||
|
|
||||||
|
ANDROID_USER_AGENT = [
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.5; en-us; sdk Build/CUPCAKE) AppleWebkit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.5; en-us; htc_bahamas Build/CRB17) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.1-update1; de-de; HTC Desire 1.19.161.5 Build/ERE27) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Sprint APA9292KT Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.5; de-ch; HTC Hero Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; ADR6300 Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.1; en-us; HTC Legend Build/cupcake) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.5; de-de; HTC Magic Build/PLAT-RC33) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1 FirePHP/0.3",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.6; en-us; HTC_TATTOO_A3288 Build/DRC79) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.5; en-us; T-Mobile G1 Build/CRB43) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari 525.20.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.5; en-gb; T-Mobile_G2_Touch Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.0; en-us; Droid Build/ESD20) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Droid Build/FRG22D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.0; en-us; Milestone Build/ SHOLS_U2_01.03.1) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.0.1; de-de; Milestone Build/SHOLS_U2_01.14.0) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 0.5; en-us) AppleWebKit/522 (KHTML, like Gecko) Safari/419.3",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.1; en-gb; dream) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.0; en-us; Droid Build/ESD20) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Sprint APA9292KT Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; ADR6300 Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.2; en-ca; GT-P1000M Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 3.0.1; fr-fr; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.6; es-es; SonyEricssonX10i Build/R1FA016) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 1.6; en-us; SonyEricssonX10i Build/R1AA056) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
# 忽略InsecureRequestWarning警告
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
|
||||||
|
class WalMartCardSpider:
|
||||||
|
|
||||||
|
def __init__(self, cookies, card_num, card_pwd, order_num, app):
|
||||||
|
self.app = app
|
||||||
|
# 接收参数
|
||||||
|
self.cookies = cookies
|
||||||
|
self.card_num = card_num
|
||||||
|
self.card_pwd = card_pwd
|
||||||
|
self.order_num = order_num
|
||||||
|
# 请求超时
|
||||||
|
self.timeout = 3
|
||||||
|
self.bind_timeout = 60
|
||||||
|
self.user_agent = random.choice(ANDROID_USER_AGENT)
|
||||||
|
# 初始化开始滑块验证时间
|
||||||
|
self.start_timestamp = int(time.time() * 1000)
|
||||||
|
current_time = datetime.datetime.utcnow()
|
||||||
|
self.start_formatted_time = current_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||||
|
# 日志存储
|
||||||
|
self.log_list = []
|
||||||
|
# 执行记录存储
|
||||||
|
self.record_list = []
|
||||||
|
# 备注
|
||||||
|
self.remarks = ""
|
||||||
|
self.session = requests.Session()
|
||||||
|
# 请求超时时间设置
|
||||||
|
self.timeout = 3.5
|
||||||
|
|
||||||
|
def format_number(self, number):
|
||||||
|
if not number:
|
||||||
|
number = 0
|
||||||
|
number = float(number)
|
||||||
|
formatted_number = "{:.2f}".format(number)
|
||||||
|
return formatted_number
|
||||||
|
|
||||||
|
def md5_encrypt(self, data):
|
||||||
|
# 创建 MD5 哈希对象
|
||||||
|
md5_hash = hashlib.md5()
|
||||||
|
# 更新哈希对象以包含数据
|
||||||
|
md5_hash.update(data.encode('utf-8')) # 确保数据为字节类型
|
||||||
|
# 返回十六进制表示的大写 MD5 值
|
||||||
|
return md5_hash.hexdigest().upper()
|
||||||
|
|
||||||
|
def walmart_bind_card(self):
|
||||||
|
"""
|
||||||
|
/* 绑定成功 {"logId":"qZXePbHl","status":true,"error":{"errorcode":1,"message":null,"redirect":null,"validators":null},"data":"59519706"}
|
||||||
|
* 错误密码 {"logId":"IQwPkfLT","status":false,"error":{"errorcode":19000,"message":"校验密码失败,密码错误次数:1","redirect":null,"validators":null},"data":null}
|
||||||
|
* 重复绑定 {"logId":"Dj6nJ4OM","status":false,"error":{"errorcode":10131,"message":"该电子卡已被其他用户绑定","redirect":null,"validators":null},"data":null}
|
||||||
|
* 错误卡号/卡密 {"logId":"hX6Ju6lD","status":false,"error":{"errorcode":19000,"message":"无权操作该卡","redirect":null,"validators":null},"data":null}
|
||||||
|
* 登录失效 {"logId":"V0Sw4Q2f","status":false,"error":{"errorcode":203,"message":"请先去登录","redirect":null,"validators":null},"data":null}
|
||||||
|
* 错误密码 {"logId":"Aavc0ch1","status":false,"error":{"errorcode":201,"message":"密码输入有误","redirect":null,"validators":null},"data":null}
|
||||||
|
* 密码输入错误次数过多 {"logId":"bchMZcyv","status":false,"error":{"errorcode":110136,"message":"密码输入错误次数过多","redirect":null,"validators":null},"data":null}
|
||||||
|
* 校验密码失败,密码错误次数:2 {"logId":"MDN05jVd","status":false,"error":{"errorcode":19000,"message":"校验密码失败,密码错误次数:2","redirect":null,"validators":null},"data":null}
|
||||||
|
* {"logId":"tP0TWGY8","status":false,"error":{"errorcode":110224,"message":"您绑卡已超过单日20张限制,请明天再试","redirect":null,"validators":null},"data":null}
|
||||||
|
* {'logId': 'Ff4eK7Bu', 'status': False, 'error': {'errorcode': 110134, 'message': '错误次数过多,请稍后再试', 'redirect': None, 'validators': None}, 'data': None}
|
||||||
|
*/
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
user_info = self.cookies.split("#")
|
||||||
|
if len(user_info) != 3:
|
||||||
|
self.set_record(f"ck失效,格式不正确")
|
||||||
|
return 113
|
||||||
|
sign = user_info[0]
|
||||||
|
user_key = user_info[1]
|
||||||
|
version = user_info[2]
|
||||||
|
timestamp = f"{int(time.time() * 1000)}"
|
||||||
|
data = '{"cardNo":"%s","cardPwd":"%s","currentPage":0,"pageSize":0,"sign":"%s","storeId":"","userPhone":""}%s%s' % (
|
||||||
|
self.card_num, self.card_pwd, sign, timestamp, user_key
|
||||||
|
)
|
||||||
|
signature = self.md5_encrypt(data)
|
||||||
|
headers = {
|
||||||
|
"authority": "apicard.swiftpass.cn",
|
||||||
|
"accept": "*/*",
|
||||||
|
"accept-language": "zh-CN,zh;q=0.9",
|
||||||
|
"content-type": "application/json",
|
||||||
|
"referer": "https://servicewechat.com/wx81d3e1fe4c2e11b4/159/page-frame.html",
|
||||||
|
"sec-fetch-dest": "empty",
|
||||||
|
"sec-fetch-mode": "cors",
|
||||||
|
"sec-fetch-site": "cross-site",
|
||||||
|
"signature": signature,
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) XWEB/8555",
|
||||||
|
"xweb_xhr": "1",
|
||||||
|
"version": version
|
||||||
|
}
|
||||||
|
url = "https://apicard.swiftpass.cn/app/card/mem/bind.json"
|
||||||
|
data = {
|
||||||
|
"cardNo": self.card_num,
|
||||||
|
"cardPwd": self.card_pwd,
|
||||||
|
"currentPage": 0,
|
||||||
|
"pageSize": 0,
|
||||||
|
"sign": sign,
|
||||||
|
"storeId": "",
|
||||||
|
"userPhone": ""
|
||||||
|
}
|
||||||
|
data = json.dumps(data, separators=(',', ':'))
|
||||||
|
response = requests.post(url, headers=headers, data=data)
|
||||||
|
res = response.json()
|
||||||
|
self.app.logger.info(f"订单ID: {self.order_num},请求绑卡返回:{res}")
|
||||||
|
logger.info(f"订单ID: {self.order_num},请求绑卡返回:{res}")
|
||||||
|
self.remarks = res.get("error", {}).get("message", "")
|
||||||
|
if res.get("error").get("errorcode") == 1:
|
||||||
|
self.set_record(f"绑定成功")
|
||||||
|
return 100
|
||||||
|
elif res.get("error").get("errorcode") == 203:
|
||||||
|
self.set_record(f"ck失效")
|
||||||
|
return 113
|
||||||
|
elif res.get("error").get("errorcode") == 5041:
|
||||||
|
self.set_record(f"请重新获取ck,开放平台迁移小程序跳转公众号同步unionid")
|
||||||
|
return 113
|
||||||
|
elif res.get("error").get("errorcode") == 10131:
|
||||||
|
self.set_record(f"该电子卡已被其他用户绑定")
|
||||||
|
return 104
|
||||||
|
elif res.get("error").get("errorcode") == 110400:
|
||||||
|
self.set_record("操作频繁,请稍后重试")
|
||||||
|
return 115
|
||||||
|
elif res.get("error").get("errorcode") == 19000 and "校验密码失败" in res.get("error").get("message"):
|
||||||
|
self.set_record(f"校验密码失败")
|
||||||
|
return 105
|
||||||
|
elif res.get("error").get("errorcode") == 19000 and "无权操作该卡" in res.get("error").get("message"):
|
||||||
|
self.set_record(f"无权操作该卡")
|
||||||
|
return 105
|
||||||
|
elif res.get("error").get("errorcode") == 19000 and "该卡已被绑定" in res.get("error").get("message"):
|
||||||
|
self.set_record(f"同一张实体卡只允许绑定到一个微信账户或山姆APP礼品卡账户,该卡已被绑定")
|
||||||
|
return 104
|
||||||
|
elif res.get("error").get("errorcode") == 19000 and "重复提交" in res.get("error").get("message"):
|
||||||
|
self.set_record(f"重复提交")
|
||||||
|
return 110
|
||||||
|
elif res.get("error").get("errorcode") == 201:
|
||||||
|
self.set_record(f"密码输入有误")
|
||||||
|
return 105
|
||||||
|
elif res.get("error").get("errorcode") == 110136:
|
||||||
|
self.set_record(f"密码输入错误次数过多")
|
||||||
|
return 105
|
||||||
|
elif res.get("error").get("errorcode") == 110224:
|
||||||
|
self.set_record(f"您绑卡已超过单日20张限制,请明天再试")
|
||||||
|
return 116
|
||||||
|
elif res.get("error").get("errorcode") == 110134:
|
||||||
|
self.set_record(f"错误次数过多,请稍后再试")
|
||||||
|
return 115
|
||||||
|
else:
|
||||||
|
self.set_record(f"存在未知状态")
|
||||||
|
return 111
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log_list.append(f"订单ID: {self.order_num},绑卡异常报错:{str(e)}")
|
||||||
|
return 111
|
||||||
|
|
||||||
|
def get_params(self):
|
||||||
|
headers = {
|
||||||
|
"language": "CN",
|
||||||
|
"system-language": "CN",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"device-type": "android",
|
||||||
|
"tpg": "1",
|
||||||
|
"app-version": "5.0.98",
|
||||||
|
"device-id": "",
|
||||||
|
"device-os-version": "9",
|
||||||
|
"device-name": "samsung_SM-N9760",
|
||||||
|
"treq-id": "35cbc67b3bdb42238e97214e4bb371ad.515.17270811912489411",
|
||||||
|
"auth-token": self.cookies,
|
||||||
|
"longitude": "113.412697",
|
||||||
|
"latitude": "23.10594",
|
||||||
|
"p": "1656120205",
|
||||||
|
"t": "1727080982681",
|
||||||
|
"n": "b233a136c3484043a022dc935684491a",
|
||||||
|
"sy": "0",
|
||||||
|
"st": "a2f3a3427f7391aa470bca22cfe52177",
|
||||||
|
"sny": "c",
|
||||||
|
# "rcs": "2",
|
||||||
|
"spv": "1.1",
|
||||||
|
"Local-Longitude": "0.0",
|
||||||
|
"Local-Latitude": "0.0",
|
||||||
|
"zoneType": "1",
|
||||||
|
"Host": "api-sams.walmartmobile.cn",
|
||||||
|
"User-Agent": "okhttp/4.8.1"
|
||||||
|
}
|
||||||
|
url = "https://api-sams.walmartmobile.cn/api/v1/sams/sams-user/tool/yinshang/request_param"
|
||||||
|
response = self.session.get(url, headers=headers, verify=False, timeout=self.timeout)
|
||||||
|
self.app.logger.info(f"订单ID: {self.order_num},获取app打开沃尔玛绑卡页面参数:{response.text}")
|
||||||
|
logger.info(f"订单ID: {self.order_num},获取app打开沃尔玛绑卡页面参数:{response.text}")
|
||||||
|
print(f"订单ID: {self.order_num},获取app打开沃尔玛绑卡页面参数:{response.text}")
|
||||||
|
res = response.json()
|
||||||
|
if res.get("code") != "Success":
|
||||||
|
return "01"
|
||||||
|
params = res["data"]["params"]
|
||||||
|
params = dict(item.split("=") for item in params.split("&"))
|
||||||
|
return params
|
||||||
|
|
||||||
|
def get_session_id(self, params):
|
||||||
|
url = "https://vpay.upcard.com.cn/vcweixin/mwalm/loginmwalmmenu"
|
||||||
|
headers = {
|
||||||
|
"Host": "vpay.upcard.com.cn",
|
||||||
|
"Upgrade-Insecure-Requests": "1",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Linux; Android 9; SM-N9760 Build/PQ3B.190801.06281543; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Mobile Safari/537.36SR-SAMS/5.0.98",
|
||||||
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
||||||
|
"X-Requested-With": "cn.samsclub.app",
|
||||||
|
"Sec-Fetch-Site": "none",
|
||||||
|
"Sec-Fetch-Mode": "navigate",
|
||||||
|
"Sec-Fetch-User": "?1",
|
||||||
|
"Sec-Fetch-Dest": "document",
|
||||||
|
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7"
|
||||||
|
}
|
||||||
|
res = self.session.get(url, headers=headers, params=params, verify=False, timeout=self.timeout)
|
||||||
|
cookies = res.cookies.get_dict()
|
||||||
|
print(f"订单ID: {self.order_num},获取JSESSIONID:{cookies}")
|
||||||
|
self.app.logger.info(f"订单ID: {self.order_num},获取JSESSIONID:{cookies}")
|
||||||
|
logger.info(f"订单ID: {self.order_num},获取JSESSIONID:{cookies}")
|
||||||
|
return cookies
|
||||||
|
|
||||||
|
def sanmu_bind_card(self):
|
||||||
|
for i in range(5):
|
||||||
|
try:
|
||||||
|
if len(self.cookies) > 50:
|
||||||
|
try:
|
||||||
|
params = self.get_params()
|
||||||
|
except JSONDecodeError as e:
|
||||||
|
self.remarks = "请求频繁,跳转至错误页面,请稍后重试"
|
||||||
|
self.set_record(f"请求频繁")
|
||||||
|
return 115
|
||||||
|
if params == "01":
|
||||||
|
self.set_record(f"ck失效")
|
||||||
|
return 113
|
||||||
|
cookies = self.get_session_id(params)
|
||||||
|
else:
|
||||||
|
cookies = {
|
||||||
|
"JSESSIONID": self.cookies,
|
||||||
|
"route": ""
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
"Host": "vpay.upcard.com.cn",
|
||||||
|
"Accept": "application/json, text/javascript, */*; q=0.01",
|
||||||
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Linux; Android 9; SM-N9760 Build/PQ3B.190801.06281543; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Mobile Safari/537.36SR-SAMS/5.0.98",
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
"Origin": "https://vpay.upcard.com.cn",
|
||||||
|
"Sec-Fetch-Site": "same-origin",
|
||||||
|
"Sec-Fetch-Mode": "cors",
|
||||||
|
"Sec-Fetch-Dest": "empty",
|
||||||
|
"Referer": "https://vpay.upcard.com.cn/vcweixin/mwalm/bind/card?company=mwalm&channel=App",
|
||||||
|
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||||
|
}
|
||||||
|
url = "https://vpay.upcard.com.cn/vcweixin/mwalm/bindCard"
|
||||||
|
data = {
|
||||||
|
"company": "mwalm",
|
||||||
|
"channel": "App",
|
||||||
|
"cardNo": self.card_num,
|
||||||
|
"cardPwd": self.card_pwd
|
||||||
|
}
|
||||||
|
response = self.session.post(
|
||||||
|
url=url,
|
||||||
|
headers=headers,
|
||||||
|
data=data,
|
||||||
|
cookies=cookies,
|
||||||
|
verify=False,
|
||||||
|
timeout=self.bind_timeout,
|
||||||
|
)
|
||||||
|
res = response.json()
|
||||||
|
print(f"订单ID: {self.order_num},app请求绑卡返回:{res}")
|
||||||
|
self.app.logger.info(f"订单ID: {self.order_num},app请求绑卡返回:{res}")
|
||||||
|
logger.info(f"订单ID: {self.order_num},app请求绑卡返回:{res}")
|
||||||
|
self.remarks = res.get("msg", "")
|
||||||
|
if res.get("code") == "00":
|
||||||
|
self.set_record(f"绑定成功")
|
||||||
|
return 100
|
||||||
|
elif res.get("code") == "11" and "该卡已被绑定" in self.remarks:
|
||||||
|
self.set_record(self.remarks)
|
||||||
|
return 104
|
||||||
|
elif res.get("code") == "11" and self.remarks == "线上密码无效":
|
||||||
|
self.set_record(self.remarks)
|
||||||
|
return 105
|
||||||
|
elif res.get("code") == "11" and "校验密码失败" in self.remarks:
|
||||||
|
self.set_record(self.remarks)
|
||||||
|
return 105
|
||||||
|
elif res.get("code") == "11" and self.remarks == "无法识别的卡号!":
|
||||||
|
self.set_record(self.remarks)
|
||||||
|
return 104
|
||||||
|
elif res.get("code") == "11" and self.remarks == "卡号不存在":
|
||||||
|
self.set_record(self.remarks)
|
||||||
|
return 104
|
||||||
|
elif res.get("code") == "01":
|
||||||
|
self.set_record(f"ck失效")
|
||||||
|
return 113
|
||||||
|
elif res.get("code") == "11" and "超过当天绑定上限" in self.remarks:
|
||||||
|
self.set_record(self.remarks)
|
||||||
|
return 116
|
||||||
|
else:
|
||||||
|
self.set_record(f"订单ID: {self.order_num},返回:{res}")
|
||||||
|
self.set_record(f"新的状态返回")
|
||||||
|
return 111
|
||||||
|
except Exception as e:
|
||||||
|
self.app.logger.info(f"订单ID: {self.order_num},报错:{traceback.format_exc()}")
|
||||||
|
logger.info(f"订单ID: {self.order_num},报错:{traceback.format_exc()}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
return 110
|
||||||
|
|
||||||
|
def get_time(self):
|
||||||
|
from datetime import datetime
|
||||||
|
# 获取当前时间
|
||||||
|
current_time = datetime.now()
|
||||||
|
# 将当前时间格式化为指定格式的字符串
|
||||||
|
formatted_date = current_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
return formatted_date
|
||||||
|
|
||||||
|
def set_record(self, text, log_only=False):
|
||||||
|
# 如果传入的是订单ID则不打印日志,只做记录存储
|
||||||
|
if text == self.order_num:
|
||||||
|
record = f"{self.get_time()}#订单ID: {self.order_num}"
|
||||||
|
else:
|
||||||
|
log_text = f"{self.get_time()}#订单ID:{self.order_num}, {text}"
|
||||||
|
self.log_list.append(log_text)
|
||||||
|
record = f"{self.get_time()}#{text}"
|
||||||
|
if not log_only:
|
||||||
|
self.record_list.append(record)
|
||||||
|
|
||||||
|
def get_record(self):
|
||||||
|
return "@".join(self.record_list) + "@"
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if "@" in self.cookies:
|
||||||
|
# 沃尔玛ck绑卡
|
||||||
|
code = self.walmart_bind_card()
|
||||||
|
else:
|
||||||
|
# 山姆app ck绑卡
|
||||||
|
code = self.sanmu_bind_card()
|
||||||
|
|
||||||
|
record = self.get_record()
|
||||||
|
return {
|
||||||
|
"code": code,
|
||||||
|
"record": record,
|
||||||
|
"log_list": self.log_list,
|
||||||
|
"remarks": self.remarks,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user