liuyuqi-dellpc 11 months ago
commit
5685b11584

+ 16 - 0
README.md

@@ -0,0 +1,16 @@
+# taobao_roder_robot
+
+淘宝订单自动发货
+
+* csdn下载链接自动发送
+* 无需人工干预
+
+## Usage
+
+客户下单,留言:
+
+邮箱 https://download.csdn.net/xx
+
+商家:
+
+运行 start.bat,安装 firefox+geckodriver。

+ 51 - 0
config/conf.ini

@@ -0,0 +1,51 @@
+[taobao]
+username = zhangsan
+password = 1346
+shelve_url = https://taobao.com/xx
+
+[csdn]
+username = zhangsan
+password = 1346
+
+[qiniu]
+ak = zhangsan
+sk = 1346
+bucket = https://taobao.com/xx
+domain = https://taobao.com/xx
+
+[mail]
+username = zhangsan
+password = 1346
+password2 = https://taobao.com/xx
+authorization_code = https://taobao.com/xx
+master_mail = https://taobao.com/xx
+
+[other]
+dir = C://xx
+password = 1346
+server = https://taobao.com/xx
+
+
+; # 本地保存下载文件地址
+; local_dir = "c://Robot_Download/"
+
+; # 自带文件下载服务器地址前缀,类似http://xx.com/(可选,mail_send_type=0有效)
+; server_file_url = "http://example.com:8080/"
+
+; # 下载次数总数
+; download_total = 20
+
+; # 爬虫每次检查待发货订单间隔/秒
+; check_order_period = 30
+
+; # 爬虫每次检查存在退款订单间隔/秒
+; check_refunding_period = 60
+
+; # 页面加载等待最长时间(推荐10秒以上,视网速而定)
+; delay_wait = 15
+
+; # 邮件发送方式 0=仅仅发送资源新的下载链接(需自带服务器) 1=发送邮件附件(50M以上问题发送有问题) 2=浏览器方式发送(推荐)
+; mail_send_type = 0
+
+; # 是否开启邮件提醒
+; is_mail_notice = False

+ 171 - 0
main.py

@@ -0,0 +1,171 @@
+import configparser
+import os,sys,re,shutil
+import subprocess
+import time
+import datetime
+import argparse
+import logging
+from selenium import webdriver
+from selenium.webdriver import ActionChains
+from taobao.mail.mail_sender import MailSender
+from util.qiniuUtil import *
+from __init__ import *
+from taobao.csdn.download import CsdnDownloader
+from mail.mail_message import *
+from mail.mail_sender import *
+from mail.mail_sender_browser import MailSenderBrowser
+from taobao.order_bot import TaobaoClimber
+from util.str_util import print_msg, send_mail
+
+
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
+
+# 再创建一个handler,用于输出到控制台
+ch = logging.StreamHandler()
+ch.setLevel(logging.DEBUG)
+# 定义handler的输出格式
+formatter = logging.Formatter(
+    '%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
+ch.setFormatter(formatter)
+# 给logger添加handler
+logger.addHandler(ch)
+
+def set_args():
+    '''
+    write config to config/conf.ini
+    '''
+    cf = configparser.ConfigParser()
+    cf.read('config/conf.ini')
+    if not cf.has_section("taobao"):
+        cf.add_section("taobao")
+    cf.set("taobao","username","zhangsan")
+    cf.set("taobao","password","1346")
+    cf.set("taobao","shelve_url","https://taobao.com/xx")
+    # save
+    with open('config/conf.ini', 'w') as f:
+        cf.write(f)
+
+def get_args()->configparser.ConfigParser:
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-c', '--conf', help='config file path', default='config/conf.ini')
+    args = parser.parse_args()
+    conf_file = args.conf
+    if not os.path.exists(conf_file):
+        print("配置文件不存在!")
+        sys.exit(1)
+    cf = configparser.ConfigParser.ConfigParser()
+    cf.read(conf_file)
+    return cf
+
+if __name__ == '__main__':
+    cf=get_args()
+    climber = TaobaoClimber(cf.get("taobao","username"), cf.get("taobao","password"))
+    downloader = CsdnDownloader(cf.get("csdn","usernmae"), cf.get("csdn","password"))
+    sender = MailSender(cf.get("mail","username"), cf.get("mail","authorcode"))
+    sender_browser = MailSenderBrowser(cf.get("mail","username"), cf.get("mail","password"), cf.get("mail","password2"))
+
+    # 2.实例化driver
+    driver = webdriver.Firefox()  # 应将浏览器驱动放于python根目录下,且python已配置path环境变量
+    action = ActionChains(driver)
+    driver.maximize_window()  # 浏览器最大化
+    driver.set_page_load_timeout(cf.get("other","delay_wait"))  # 设定页面加载限制时间
+
+    TaobaoClimber.driver = CsdnDownloader.driver = MailSenderBrowser.driver = driver
+    TaobaoClimber.action = CsdnDownloader.action = MailSenderBrowser.action = action
+
+    # 3.建立邮箱标签页
+    if mail_send_type == 2:
+        driver.execute_script("window.open('')")
+
+    # 正则:解析留言内容
+    re_note = re.compile(
+        ur"留言:\s*([\w.-]+@[\w.-]+\.\w+)\s+((?:https?://)?[-A-Za-z0-9+&@#/%?=~_|!,.;]+)\s*")
+    # 休眠总时间
+    sleep_total_time = 0
+    # 存在未留言订单
+    exists_no_note_order = False
+
+    # 2.1上架宝贝
+    climber.shelve()
+    is_running = True
+    while is_running:
+        # 2.2爬取订单
+        orders = climber.climb()
+        orders_len = len(orders)
+        for order in orders:
+
+            if downloader.download_count >= download_total:
+                send_mail(sender, message_over_download_total, orders_len)
+                is_running = False
+                break
+
+            note_array = re.findall(re_note, order[3])
+            if len(note_array) != 1:
+                exists_no_note_order = True
+                continue
+
+            order_info = "【淘宝】已产生可操作订单:订单号:%s\t订单日期:%s \t买家:%s\t备注:%s" % order
+            print_msg(order_info)
+
+            user_to = note_array[0][0]
+            remote_url = note_array[0][1]
+
+            # 2.3下载资源
+            local_path = downloader.download(remote_url, local_dir)
+            if local_path is None:
+                send_mail(sender, message_download_false, order[0])
+                continue
+            else:
+                print_msg("【CSDN】" + user_to + "的文件下载成功,本地路径:" + local_path)
+            orders_len -= 1
+
+            # 2.4进行下架判断
+            if downloader.download_count == download_total - 1:
+                if climber.unshelve() is False:
+                    send_mail(sender, message_unshelve_false, downloader.download_count)
+
+            # 2.5 发送邮件
+            if mail_send_type == 0:
+                download_url_text = server_file_url + os.path.basename(local_path)
+                if sender.send(Mail(user_to, download_url_text)):
+                    print_msg("【邮件】" + user_to + "的邮件发送成功")
+                else:
+                    send_mail(sender, message_send_false, order[0])
+                    continue
+            elif mail_send_type == 1:
+                if sender.send(Mail(user_to, local_path, 2)):
+                    print_msg("【邮件】" + user_to + "的邮件发送成功")
+                else:
+                    send_mail(sender, message_send_false, order[0])
+                    continue
+            elif mail_send_type == 2:
+                ret = sender_browser.send(user_to, local_path)
+
+                if ret is None:
+                    print_msg("【邮件-浏览器】" + user_to + "的邮件发送成功")
+                else:  # 发送失败
+                    send_mail(sender, message_send_mail_error, order[0], ret)
+                    continue
+            else:
+                download_url_text = upload_file(local_path)
+                if sender.send(Mail(user_to, download_url_text)):
+                    print_msg("【七牛】" + user_to + "的邮件发送成功")
+                else:
+                    send_mail(sender, message_send_false, order[0])
+                    continue
+
+            # 2.6 订单改为已发货
+            if climber.delivered(order[0]) is False:
+                send_mail(sender, message_delivered_false, order[0])
+
+        time.sleep(check_order_period)  # 每指定时间抓一次
+        sleep_total_time += check_order_period
+
+        if sleep_total_time >= check_refunding_period:  # 每指定时间检查一次退款和未留言订单
+            if climber.exists_refunding():
+                send_mail(sender, message_exists_refunding)
+            if exists_no_note_order:
+                send_mail(sender, message_notice_for_no_note)
+                exists_no_note_order = False
+            sleep_total_time = 0

+ 4 - 0
requirements.txt

@@ -0,0 +1,4 @@
+selenium
+
+
+

+ 14 - 0
start.bat

@@ -0,0 +1,14 @@
+@echo off
+REM ***************************************************************************
+REM @Desc    :   start bot
+REM %1 - ext_name
+REM %2 - characters replaced
+REM %3 - new characters
+REM @Contact :   liuyuqi.gov@msn.cn
+REM @Time    :   2023/05/17 12:43:37
+REM @Version :   1.0
+REM @License :   (C)Copyright 2019 liuyuqi.
+REM ***************************************************************************
+set PYTHONPATH=.
+@REM taskkill /f /im geckodriver.exe
+python main.py gbk

+ 0 - 0
taobao/__init__.py


+ 7 - 0
taobao/api.py

@@ -0,0 +1,7 @@
+
+_host="https://www.taobao.com"
+
+
+
+
+

+ 0 - 0
taobao/csdn/__init__.py


+ 109 - 0
taobao/csdn/download.py

@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+'''
+@Contact :   liuyuqi.gov@msn.cn
+@Time    :   2023/05/17 12:45:38
+@License :   Copyright © 2017-2022 liuyuqi. All Rights Reserved.
+@Desc    :   download csdn file
+'''
+from bs4 import BeautifulSoup
+import time
+import re
+import os
+import requests
+import sys
+from  import print_msg
+
+reload(sys)
+sys.setdefaultencoding('utf8')  # 对于py2,将ascii改为utf8
+
+class CsdnDownloader:
+    def __init__(self, username, password):
+        self.__username = username
+        self.__password = password
+
+    driver = None
+    action = None
+
+    # CSDN账号
+    __username = ""
+    # 登录密码
+    __password = ""
+    # 会话
+    __session = requests.session()
+    # 下载次数
+    download_count = 0
+    # 是否登录
+    __is_logined = False
+    __login_url = "https://passport.csdn.net/account/login"
+
+    def download(self, remote_url, local_dir):
+
+        # 1.是否登录
+        if not self.__is_logined:
+            self.__login()
+
+        # 下载次数+1
+        self.download_count += 1
+
+        count = 0
+        while count < 3:
+            count += 1
+
+            # 2.解析真实下载URL
+            html_text = self.__session.get(remote_url).text
+            html = BeautifulSoup(html_text, "html5lib")
+            real_url = html.find("a", id="vip_btn").attrs["href"]
+
+            # 3.下载
+            source = self.__session.get(real_url)
+
+            # 3.1获取下载名
+            filename = re.findall(r".*\"(.*)\"$", source.headers.get("Content-Disposition", "\"None\""))[0]
+            if filename == "None":
+                continue
+            filename = re.sub("\s", "_", filename)
+
+            # 3.2创建本地文件
+            if not os.path.exists(local_dir):
+                os.makedirs(local_dir)
+            _local_path = local_dir + filename
+
+            # 3.3分段下载
+            local_file = open(_local_path.encode("gbk"), "wb")
+            for file_buffer in source.iter_content(chunk_size=512):
+                if file_buffer:
+                    local_file.write(file_buffer)
+            return _local_path
+
+        return None
+
+    def __login(self):
+        # 1.请求登录页面,获取登录前的必要参数
+        html_text = requests.get(self.__login_url).text
+        html = BeautifulSoup(html_text, "html5lib")
+        form = html.find("form", id="fm1")
+        location = form.attrs["action"]  # 每次表单action后面有个随机数
+        lt = form.select("input[name=lt]")[0].attrs["value"]
+        execution = form.select("input[name=execution]")[0].attrs["value"]
+        _eventId = form.select("input[name=_eventId]")[0].attrs["value"]
+        params = {"username": self.__username, "password": self.__password, "lt": lt, "execution": execution,
+                  "_eventId": _eventId}
+
+        time.sleep(1)  # CSDN貌似判断机器人,睡眠一下,增加成功率
+
+        # 2.进行登录
+        response = requests.post(location, params)
+
+        # 3.保存cookies
+        self.__session.cookies = response.cookies
+        self.__is_logined = True
+
+
+if __name__ == '__main__':
+    down_loader = CsdnDownloader("test", "123456")
+    local_path = down_loader.download('http://download.csdn.net/download/lqkitten/10113904', "c://Robot_Download/")
+    if local_path is not None:
+        print_msg("CSDN下载完成,本地路径:" + local_path)
+    else:
+        print_msg("CSDN下载失败")

+ 23 - 0
taobao/mail/__init__.py

@@ -0,0 +1,23 @@
+import chardet
+import sys
+
+def send_mail(sender, model, insert=None, insert2=None):
+    '''
+    发送邮件
+    :param sender: 发送者
+    :param model: 模板
+    :param insert: 插入值
+    :param insert2: 插入值2
+    :return:
+    '''
+    if not is_mail_notice:
+        return
+
+    if insert is None:
+        sender.send(Mail(master_mail, model, 0))
+    elif insert2 is None:
+        message = model.replace("$1", str(insert))
+        sender.send(Mail(master_mail, message, 0))
+    else:
+        message = model.replace("$1", str(insert)).replace("$2", str(insert2))
+        sender.send(Mail(master_mail, message, 0))

+ 23 - 0
taobao/mail/mail_message.py

@@ -0,0 +1,23 @@
+# encoding: utf-8
+
+# 报警邮件,如果内容为空则不会发送邮件报警
+# 邮件下架失败
+message_unshelve_false = "请注意:资源下载数已达到$1次,但下架失败!"
+
+# 超出下载次数总数
+message_over_download_total = "请注意:资源下载数已达到20次,但仍有$1个订单需发货!"
+
+# 修改订单发货状态为已发货失败
+message_delivered_false = "请注意:订单ID【$1】已发货,但修改订单发货状态失败!"
+
+# 资源下载失败
+message_download_false = "请注意:订单ID【$1】相关资源下载失败!"
+
+# 资源发送失败
+message_send_false = "请注意:订单ID【$1】相关资源发送失败!"
+
+# 存在退款订单
+message_exists_refunding = "请注意:存在退款订单,请主人处理!"
+# 订单未留言
+message_notice_for_no_note = "请注意:产生了无法操作的订单,请主人处理!"
+message_send_mail_error = "请注意:订单ID【$1】相关资源发送失败,原因:$2"

+ 124 - 0
taobao/mail/mail_sender.py

@@ -0,0 +1,124 @@
+# -*- coding:utf-8 -*-
+import smtplib
+from email.mime.text import MIMEText
+from email.mime.multipart import MIMEMultipart
+from email import Utils
+import zipfile
+import os.path
+import re
+import sys
+from taobao import mail
+
+class MailSender:
+    def __init__(self, username, mail_authorization_code):
+        '''
+        :param username: 发送邮件的邮箱账号
+        :param mail_authorization_code: 发送邮件的邮箱授权码
+        '''
+        self.__username = username
+        self.__mail_authorization_code = mail_authorization_code
+
+    __username = ""
+    __mail_authorization_code = ""
+
+    def send(self, mail: mail) -> bool:
+        '''
+        :param mail: 邮件对象
+        :return: 是否发送成功
+        '''
+
+        if mail.text is "":
+            return False
+
+        server = smtplib.SMTP_SSL(mail.server, mail.port)
+        server.login(self.__username, self.__mail_authorization_code)  # 仅smtp服务器需要验证时
+
+        # 构造MIMEMultipart对象做为邮件主体
+        body_mail = MIMEMultipart()
+
+        # 构造显示内容并添加到邮件主体
+        if mail.mode == 0:
+            text_mail = MIMEText(mail.text, _charset="utf-8")
+        elif mail.mode == 1:
+            text_mail = MIMEText(mail.file_path.decode("gbk") + "\r\n\r\n\r\n" + mail.text, _charset="utf-8")
+        else:  # 构造附件并添加到邮件主体
+            text_mail = MIMEText(mail.text, _charset="utf-8")
+
+            file_mail = MIMEText(open(mail.file_path, 'rb').read(), 'base64', 'utf-8')
+            file_mail["Content-Type"] = 'application/x-zip-compressed'
+            basename = os.path.basename(mail.file_path)
+            file_mail["Content-Disposition"] = 'attachment; filename=' + basename
+            body_mail.attach(file_mail)
+
+        body_mail.attach(text_mail)
+
+        # 设置邮件主体属性
+        body_mail['From'] = mail.Special_Form
+        body_mail['To'] = mail.Special_To
+        body_mail['Reply-to'] = mail.Special_Reply
+        body_mail['Subject'] = mail.subject
+        body_mail['Date'] = Utils.formatdate()
+
+        # 得到格式化后的完整文本
+        full_text = body_mail.as_string()
+
+        # 用smtp发送邮件
+        try:
+            server.sendmail(self.__username, mail.to_user, full_text)
+            return True
+        except smtplib.SMTPDataError:
+            return False
+        finally:
+            server.quit()
+
+
+class Mail:
+    '''
+    邮件类
+    '''
+    # 发送模式
+    mode = 1
+
+    # 邮箱个性显示
+    Special_Form = "自动发货机器人"
+    Special_To = "【尊贵的淘宝买家】"
+    Special_Reply = "请旺旺联系!"
+
+    # 邮箱内容及附件
+    subject = "CSDN资源文件"
+    text = "\r\nTrip:如果有任何问题,请旺旺上联系客服,客服将每天定时处理【问题订单】,谢谢支持!"
+    file_path = ""  # 附件地址(本地路径 | 网上路径)
+
+    # 邮箱配置
+    server = "smtp.qq.com"
+    port = 465
+    to_user = ""
+
+    def __init__(self, to_user, file_path, mode=1):
+        '''
+        :param to_user: 收件人
+        :param file_path: 文件路径
+        :param mode: 发送模式,0=邮件提醒,1=仅发送资源地址,2=发送资源附件
+        '''
+        self.to_user = to_user
+        self.file_path = file_path.encode("gbk")
+        self.mode = mode
+        if mode == 0:
+            self.subject = "邮件提醒"
+            self.Special_To = "【尊贵的主人】"
+            self.Special_Reply = "不用回复!"
+            self.text = "\r\n" + file_path
+        elif mode == 1:
+            self.subject += "-下载地址"
+        else:
+            # 如果不是压缩文件,则打包成zip
+            if re.match(r"^[\s\S]+\.((?!(RAR|ZIP|TAR|ARJ|CAB|LZH|ACE|GZ|UUE|BZ2|JAR|ISO)).)+$", self.file_path.upper()):
+                zip_path = re.sub(r"\.((?!\.).)+$", ".zip", self.file_path, 1)
+                zip_file = zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED)
+                zip_file.write(self.file_path)
+                self.file_path = zip_path
+
+
+if __name__ == "__main__":
+    mail_sender = MailSender("test@qq.com", "123456")
+    mail_sender.send(Mail("test02@qq.com", "c://Robot_Download/大话Oracle_RAC:集群、高可用性、备份与恢复.pdf", 2))

+ 161 - 0
taobao/mail/sender.py

@@ -0,0 +1,161 @@
+# -*- coding: utf-8 -*-
+from selenium.common import exceptions
+from selenium import webdriver
+from selenium.webdriver import ActionChains
+import time
+import sys
+import os
+from cn.localhost01.util.str_util import print_msg
+
+reload(sys)
+sys.setdefaultencoding('utf8')  # 对于py2,将ascii改为utf8
+
+is_cmd_run = False
+if len(sys.argv) == 2:  # cmd启动
+    is_cmd_run = True
+
+
+class MailSenderBrowser:
+    def __init__(self, username, password, password2):
+        self.__username = username
+        self.__password = password
+        self.__password2 = password2
+
+    driver = None
+    action = None
+
+    # 是否登录
+    __is_logined = False
+
+    # 淘宝账户
+    __username = ""
+    # 登录密码
+    __password = ""
+    # 独立密码
+    __password2 = ""
+    # 登陆URL
+    __login_path = "https://mail.qq.com/"
+
+    def __login(self):
+        # 1.进入登陆页面
+        try:
+            self.driver.get(self.__login_path)
+        except exceptions.TimeoutException:  # 当页面加载时间超过设定时间,JS来停止加载
+            self.driver.execute_script('window.stop()')
+
+        # 2.切换到登录iframe
+        login_frame = self.driver.find_element_by_id("login_frame")
+        self.driver.switch_to_frame(login_frame)
+
+        switch_login_a = self.driver.find_element_by_id("switcher_plogin")
+        switch_login_a.click()
+        username_input = self.driver.find_element_by_id("u")
+        username_input.clear()
+        # 点击,防止腾讯判断机器人操作而网络繁忙
+        username_input.click()
+        username_input.send_keys(self.__username)
+        password_input = self.driver.find_element_by_id("p")
+        password_input.clear()
+        # 点击,防止腾讯判断机器人操作而网络繁忙
+        password_input.click();
+        password_input.send_keys(self.__password)
+        self.driver.find_element_by_id("login_button").click()
+        time.sleep(2)
+        self.driver.switch_to_default_content()
+        try:
+            password2_input = self.driver.find_element_by_id("pp")
+            if self.__password2 is "":
+                print_msg("【邮件】邮箱登录失败,请在__init__.py中配置邮箱独立密码!")
+                return False
+            password2_input.send_keys(self.__password2)
+            self.driver.find_element_by_id("btlogin").submit()
+        except exceptions.NoSuchElementException:
+            pass
+        time.sleep(2)
+        try:
+            self.driver.find_element_by_id("useralias")
+            return True
+        except exceptions.NoSuchElementException:
+            return False
+
+    def send(self, user_to, local_path):
+        # 切换回窗口
+        self.driver.switch_to_window(self.driver.window_handles[1])
+
+        if self.__is_logined is False:
+            count = 0
+            while count < 7:
+                count += 1
+                if self.__login() is False:
+                    continue
+                else:
+                    self.__is_logined = True
+                    break
+            if count >= 7:
+                return "邮箱登录失败!"
+
+        # 1.写信
+        write_a = self.driver.find_element_by_id("composebtn")
+        write_a.click()
+        time.sleep(1)
+        # 2.输入邮件信息
+        form_iframe = self.driver.find_element_by_id("mainFrame")
+        self.driver.switch_to_frame(form_iframe)
+        user_to_input = self.driver.find_element_by_xpath("//*[@id='toAreaCtrl']/div[2]/input")
+        user_to_input.send_keys(user_to)
+        subjuct_input = self.driver.find_element_by_id("subject")
+        subjuct_input.send_keys(u"【尊贵的淘宝买家】")
+
+        # 3.超大附件上传
+        self.driver.find_element_by_class_name("ico_attbig").click()
+        time.sleep(2)
+        self.driver.switch_to_default_content()
+        upload_iframe = self.driver.find_element_by_id("ftnupload_attach_QMDialog__dlgiframe_")
+        self.driver.switch_to_frame(upload_iframe)
+        upload_input = self.driver.find_element_by_xpath("//*[@class='upload_btn_center']/a")
+        upload_input.click()
+
+        time.sleep(2)
+        local_path = local_path.replace("//", "\\").replace("/", "\\").decode('utf-8').encode('gbk')
+        if is_cmd_run:  # 外部cmd批处理文件启动该程序
+            os.system(os.path.abspath("./cn/localhost01/mail/upload.exe") + r' "firefox" "' + local_path + '"')
+        else:  # 开发软件启动该程序
+            if os.path.exists(os.path.abspath("./mail/upload.exe")):  # main.py位置启动
+                os.system(os.path.abspath("./mail/upload.exe") + r' "firefox" "' + local_path + '"')
+            else:  # 本文件的main方法位置启动
+                os.system(os.path.abspath("upload.exe") + r' "firefox" "' + local_path + '"')
+        # 4.等待上传
+        while True:
+            try:
+                speed = self.driver.find_element_by_xpath("//*[@class='probar_tips']/span[2]/span[1]").text
+                if speed == "完成":
+                    break
+            except exceptions.NoSuchElementException:
+                pass
+            time.sleep(3)
+        self.driver.find_element_by_xpath("//*[@id='operate']/a[1]").click()
+
+        # 6.发送
+        self.driver.switch_to_default_content()
+        form_iframe = self.driver.find_element_by_id("mainFrame")
+        self.driver.switch_to_frame(form_iframe)
+        self.driver.find_element_by_xpath("//*[@id='toolbar']/div/a[1]").click()
+
+        time.sleep(1)
+        self.driver.switch_to_default_content()
+        try:
+            error = self.driver.find_element_by_class_name("errmsg").text
+            return error
+        except exceptions.NoSuchElementException:
+            return None
+
+
+if __name__ == "__main__":
+    # 初始化
+    MailSenderBrowser.driver = webdriver.Firefox()  # 应将浏览器驱动放于python根目录下,且python已配置path环境变量
+    MailSenderBrowser.action = ActionChains(MailSenderBrowser.driver)
+    MailSenderBrowser.driver.maximize_window()  # 浏览器最大化
+    MailSenderBrowser.driver.execute_script("window.open('')")
+
+    sender_browser = MailSenderBrowser("test@qq.com", "123456", "111111")
+    sender_browser.send("test02@qq.com", r"C:\Robot_Download\openssh-7.6.rpm.tar")

+ 296 - 0
taobao/order_bot.py

@@ -0,0 +1,296 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+'''
+@Contact :   liuyuqi.gov@msn.cn
+@Time    :   2023/05/17 12:38:45
+@License :   Copyright © 2017-2022 liuyuqi. All Rights Reserved.
+@Desc    :   order bot
+'''
+from selenium.common import exceptions
+from selenium import webdriver
+from selenium.webdriver import ActionChains
+import time
+import re
+import requests
+import json
+import sys
+from bs4 import BeautifulSoup
+
+class TaobaoClimber:
+    '''
+    淘宝爬虫
+    '''
+    def __init__(self, username, password):
+        ''' 初始化 '''
+        self.__session = requests.Session()
+        self.__username = username
+        self.__password = password
+
+    driver = None
+    action = None
+
+    # 是否登录
+    __is_logined = False
+
+    # 淘宝账户
+    __username = ""
+    # 登录密码
+    __password = ""
+    # 登陆URL
+    __login_url = "https://login.taobao.com/member/login.jhtml"
+    # 卖家待发货订单URL
+    __orders_url = "https://trade.taobao.com/trade/itemlist/list_sold_items.htm?action=itemlist/SoldQueryAction&event_submit_do_query=1&auctionStatus=PAID&tabCode=waitSend"
+    # 卖家正出售宝贝URL
+    __auction_url = "https://sell.taobao.com/auction/merchandise/auction_list.htm"
+    # 卖家仓库中宝贝URL
+    __repository_url = "https://sell.taobao.com/auction/merchandise/auction_list.htm?type=1"
+    # 卖家确认发货URL
+    __deliver_url = "https://wuliu.taobao.com/user/consign.htm?trade_id="
+    # 卖家退款URL
+    __refunding_url = "https://trade.taobao.com/trade/itemlist/list_sold_items.htm?action=itemlist/SoldQueryAction&event_submit_do_query=1&auctionStatus=REFUNDING&tabCode=refunding"
+    # 请求留言URL
+    __message_url = "https://trade.taobao.com/trade/json/getMessage.htm?archive=false&biz_order_id="
+    # requests会话
+    __session = None
+
+    def __login(self):
+        # 1.登陆
+        try:
+            self.driver.get(self.__login_url)
+        except exceptions.TimeoutException:  # 当页面加载时间超过设定时间,JS来停止加载
+            self.driver.execute_script('window.stop()')
+
+        count = 0
+        while count < 5:  # 重试5次
+            count += 1
+            if self.__login_one() is True:
+                break
+        if count == 5:
+            return False
+
+        # 2.保存cookies
+        # driver.switch_to_default_content() #需要返回主页面,不然获取的cookies不是登陆后cookies
+        list_cookies = self.driver.get_cookies()
+        cookies = {}
+        for s in list_cookies:
+            cookies[s['name']] = s['value']
+            requests.utils.add_dict_to_cookiejar(self.__session.cookies, cookies)  # 将获取的cookies设置到session
+        return True
+
+    def __login_one(self):
+        try:
+            # 1.点击密码登录,切换到密码登录模式 默认是二维码登录
+            username_login_btn = self.driver.find_element_by_xpath("//a[@class='forget-pwd J_Quick2Static']")
+            if username_login_btn.is_displayed() is True:
+                username_login_btn.click()
+        except exceptions.ElementNotInteractableException:
+            pass
+
+        # 2.获取账户、密码输入框
+        username_input = self.driver.find_element_by_id("TPL_username_1")
+        password_input = self.driver.find_element_by_id("TPL_password_1")
+        # 3.为账户、密码赋值
+        username_input.clear()
+        username_input.send_keys(self.__username)
+        password_input.send_keys(self.__password)
+
+        # 4.滑块判断
+        self.__slide_login()
+
+        # 5.获取登陆按钮,并点击登录
+        submit_btn = self.driver.find_element_by_id("J_SubmitStatic")
+        submit_btn.click()
+        # 6.根据提示判断是否登录成功
+        try:
+            message = self.driver.find_element_by_id("J_Message").find_element_by_class_name("error")
+            if message.text == u"为了你的账户安全,请拖动滑块完成验证":
+                self.driver.execute_script(
+                    "document.getElementById('J_Message').children[1].innerText='发货机器人:请滑动滑块,协助完成验证!';")
+                return False
+        except exceptions.NoSuchElementException:
+            pass
+
+        # 7.有时检测当前环境是否异常,此时休眠一段时间让它检测
+        while True:
+            try:
+                self.driver.find_element_by_id("J_SiteNav")
+                break
+            except exceptions.NoSuchElementException:
+                time.sleep(1)
+
+        return True
+
+    def __slide_login(self):
+        # 取得滑块所在div,判断是否display 一般首次登陆不需要滑块验证
+        slide_div = self.driver.find_element_by_id("nocaptcha")
+        if slide_div.is_displayed() is True:
+            self.driver.execute_script(
+                "document.getElementById('J_Message').children[1].innerText='发货机器人:请滑动滑块,协助完成验证!';")
+            while True:
+                try:
+                    text = self.driver.find_element_by_id("nc_1__scale_text").text
+                    if text == '验证通过':
+                        break
+                    time.sleep(0.5)
+                except exceptions.NoSuchElementException:  # 此时处于刷新按钮状态
+                    pass
+
+    def __get_orders_page(self):
+        # 1.bs4将资源转html
+        html = BeautifulSoup(self.driver.page_source, "html5lib")
+        # 2.取得所有的订单div
+        order_div_list = html.find_all("div", {"class": "item-mod__trade-order___2LnGB trade-order-main"})
+        # 3.遍历每个订单div,获取数据
+        data_array = []
+        for index, order_div in enumerate(order_div_list):
+            order_id = order_div.find("input", attrs={"name": "orderid"}).attrs["value"]
+            order_date = order_div.find("span",
+                                        attrs={"data-reactid": re.compile(r"\.0\.5\.3:.+\.0\.1\.0\.0\.0\.6")}).text
+            order_buyer = order_div.find("a", attrs={"class": "buyer-mod__name___S9vit"}).text
+            # 4.根据订单id组合url,请求订单对应留言
+            order_message = json.loads(self.__session.get(self.__message_url + order_id).text)['tip']
+            data_array.append((order_id, order_date, order_buyer, order_message))
+        return data_array
+
+    def climb(self):
+        # FIXME 没有真实订单的模拟测试,生产环境注释即可
+        # order_test = [("Test_1548615412315", "2018-08-07 15:00:03", "疯狂的石头",
+                       u"留言: test@qq.com  http://download.csdn.net/download/lqkitten/10113904")]
+        # return order_test
+
+        # 切换回淘宝窗口
+        self.driver.switch_to_window(self.driver.window_handles[0])
+
+        result = []
+
+        if self.__is_logined is False:
+            if self.__login() is False:
+                return result
+            else:
+                self.__is_logined = True
+
+        # 1.进入待发货订单页面
+        self.driver.get(self.__orders_url)
+        while True:
+            # 2.获取当前页面的订单信息
+            time.sleep(2)  # 两秒等待页面加载
+            _orders = self.__get_orders_page()
+            result.extend(_orders)
+            try:
+                # 3.获取下一页按钮
+                next_page_li = self.driver.find_element_by_class_name("pagination-next")
+                # 4.判断按钮是否可点击,否则退出循环
+                next_page_li.get_attribute("class").index("pagination-disabled")
+                # 到达最后一页
+                break
+            except ValueError:
+                # 跳转到下一页
+                print(next_page_li.find_element_by_tag_name("a").text)
+                next_page_li.click()
+                time.sleep(1)
+            except exceptions.NoSuchElementException:
+                pass
+        return result
+
+    def unshelve(self):
+        # 切换回淘宝窗口
+        self.driver.switch_to_window(self.driver.window_handles[0])
+
+        if self.__is_logined is False:
+            if self.__login() is False:
+                return False
+            else:
+                self.__is_logined = True
+
+        try:
+            # 1.进入正出售宝贝页面
+            self.driver.get(self.__auction_url)
+            # 2.点击下架
+            choose_checkbox = self.driver.find_element_by_xpath(
+                "//*[@id='J_DataTable']/table/tbody[1]/tr[1]/td/input[1]")
+            choose_checkbox.click()
+            unshelve_btn = self.driver.find_element_by_xpath(
+                "//*[@id='J_DataTable']/div[2]/table/thead/tr[2]/td/div/button[2]")
+            unshelve_btn.click()
+            return True
+        except:
+            return False
+
+    def shelve(self):
+        # 切换回淘宝窗口
+        try:
+            self.driver.switch_to_window(self.driver.window_handles[0])
+        except exceptions:
+            print exceptions
+
+        if self.__is_logined is False:
+            if self.__login() is False:
+                return False
+            else:
+                self.__is_logined = True
+
+        # 1.进入仓库宝贝页面
+        self.driver.get(self.__repository_url)
+        # 2.点击上架
+        try:
+            choose_checkbox = self.driver.find_element_by_xpath("//*[@id='J_DataTable']/table/tbody[1]/tr[1]/td/input")
+            choose_checkbox.click()
+            shelve_btn = self.driver.find_element_by_xpath(
+                "//*[@id='J_DataTable']/div[3]/table/tbody/tr/td/div/button[2]")
+            shelve_btn.click()
+        except exceptions.NoSuchElementException:
+            pass
+
+    def delivered(self, orderId):
+        # 切换回淘宝窗口
+        self.driver.switch_to_window(self.driver.window_handles[0])
+
+        if self.__is_logined is False:
+            if self.__login() is False:
+                return False
+            else:
+                self.__is_logined = True
+        try:
+            # 1.进入确认发货页面
+            self.driver.get(self.__deliver_url + orderId)
+            no_need_logistics_a = self.driver.find_element_by_xpath("//*[@id='dummyTab']/a")
+            no_need_logistics_a.click()
+            self.driver.find_element_by_id("logis:noLogis").click()
+            time.sleep(1)
+            return True
+        except:
+            return False
+
+    def exists_refunding(self):
+        # 切换回淘宝窗口
+        self.driver.switch_to_window(self.driver.window_handles[0])
+
+        if self.__is_logined is False:
+            if self.__login() is False:
+                return False
+            else:
+                self.__is_logined = True
+        try:
+            # 1.进入退款页面
+            self.driver.get(self.__refunding_url)
+            self.driver.find_element_by_class_name("item-mod__trade-order___2LnGB trade-order-main")
+            return True
+        except exceptions.NoSuchElementException:
+            return False
+
+
+if __name__ == '__main__':
+    # 初始化
+    TaobaoClimber.driver = webdriver.Firefox()  # 应将浏览器驱动放于python根目录下,且python已配置path环境变量
+    TaobaoClimber.action = ActionChains(TaobaoClimber.driver)
+    TaobaoClimber.driver.maximize_window()  # 浏览器最大化
+    TaobaoClimber.driver.execute_script("window.open('')")
+
+    climber = TaobaoClimber(u"test", "123456")
+    while True:
+        # 循环爬取订单
+        orders = climber.climb()
+        for order in orders:
+            print_msg("淘宝订单产生:订单号:%s\t订单日期:%s \t买家:%s\t备注:%s" % order)
+        # 每30秒抓一次
+        time.sleep(30)

+ 0 - 0
taobao/utils/__init__.py


+ 20 - 0
taobao/utils/qiniu.py

@@ -0,0 +1,20 @@
+# encoding: utf-8
+from cn.localhost01.__init__ import qiniu_ak, qiniu_sk, qiniu_bucket, qiniu_domain
+from qiniu import Auth, put_file
+import os
+
+# 构建鉴权对象
+auth = Auth(qiniu_ak, qiniu_sk)
+
+
+def upload_file(local_path):
+    # 0.获取文件名
+    new_file_name = os.path.basename(local_path)
+    # 1.获取上传token
+    token = auth.upload_token(qiniu_bucket, new_file_name)
+    # 2.上传
+    ret, info = put_file(token, new_file_name, local_path)
+
+    if info.status_code == 200:
+        return qiniu_domain + "/" + new_file_name + "?attname="
+    return None