├── Global_Config.py ├── Notice_Method.py ├── README.md └── SMZDM_Fin_JSON_Price.py /Global_Config.py: -------------------------------------------------------------------------------- 1 | #数据库配置 2 | db_host ="" 3 | db_port=3306 4 | db_user="root" 5 | db_password="" 6 | db_name ="SMZDM" #需要自己新建一个数据库 7 | db_table='SMZDM_Price' #自行命名,如果不存在。程序会自动创建 8 | 9 | 10 | #邮件提醒方式参数配置 11 | from_addr = '' #发送邮箱 12 | password = '' #发送密码 13 | to_addr = '' #接收邮箱 14 | smtp_server = 'smtp.qq.com' #发送邮件服务器 15 | Email_Header = '' #邮件标题 16 | #QQ提醒方式参数配置 17 | QQ_Name = '' #设置接收的QQ好友的昵称或者备注 18 | 19 | 20 | #监控关键词 21 | Monitor_Key=['男','饮料'] #可以设置多个关键词 22 | 23 | 24 | #设置爬取的页面参数 25 | header ={ 26 | 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36', 27 | 'Connection': 'close' 28 | } #一般情况下无需修改 29 | url = 'https://faxian.smzdm.com/json_more?' #一般情况下无需修改 30 | 31 | 32 | #设置提醒的方法 33 | Not_Met='Email' #可以选择Email或者QQ 34 | 35 | #设置循环爬取的时间间隔 36 | Cycle_time=30 #以秒计算,最好不要超过5分钟,否则可能会出现爬取不完整 -------------------------------------------------------------------------------- /Notice_Method.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | from email.header import Header 3 | from email.mime.text import MIMEText 4 | 5 | import win32clipboard as w 6 | import win32con 7 | import win32gui 8 | 9 | import Global_Config 10 | 11 | class Notice_Method: 12 | from_addr = Global_Config.from_addr 13 | password = Global_Config.password 14 | to_addr = Global_Config.to_addr 15 | smtp_server = Global_Config.smtp_server 16 | Email_Header=Global_Config.Email_Header 17 | 18 | QQ_Name=Global_Config.QQ_Name 19 | send_method=Global_Config.Not_Met 20 | 21 | def send_email(self,msg): 22 | msg = MIMEText(msg) 23 | msg['From'] = Header(self.from_addr) 24 | msg['To'] = Header(self.to_addr) 25 | msg['Subject'] = Header(self.Email_Header) 26 | server = smtplib.SMTP_SSL(host=self.smtp_server) 27 | server.connect(self.smtp_server, 465) 28 | 29 | server.login(self.from_addr, self.password) 30 | server.sendmail(self.from_addr, self.to_addr, msg.as_string()) 31 | server.quit() 32 | 33 | def send_QQmessage(self,msg): 34 | # 窗口名字,就是备注名 35 | name = self.QQ_Name 36 | # 将测试消息复制到剪切板中 37 | w.OpenClipboard() 38 | w.EmptyClipboard() 39 | w.SetClipboardData(win32con.CF_UNICODETEXT, msg) 40 | w.CloseClipboard() 41 | # 获取窗口句柄 42 | handle = win32gui.FindWindow(None, name) 43 | # 填充消息 44 | win32gui.SendMessage(handle, 770, 0, 0) 45 | # 回车发送消息 46 | win32gui.SendMessage(handle, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0) 47 | 48 | def Select_Method(self,msg): 49 | if self.send_method.upper() =='QQ': 50 | self.send_QQmessage(msg) 51 | elif self.send_method.upper() =='EMAIL': 52 | self.send_email(msg) 53 | else: 54 | print("输入错误,请重新启动程序") 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMZDM_Monitor_Parice 2 | python可以实时监控什么值得买最新更新的好价,并根据关键词推送到邮箱或者QQ 3 | 4 | 如需运行该脚本,需要以下几项准备工作: 5 | 6 | 1.安装运行以下支持库: 7 | pymysql 8 | requests 9 | pywin32 10 | 11 | 12 | Pywin32为windows下操作gui的库,用于QQ方式发送推送消息,如在linux系统下运行该脚本,则无法选择QQ推送方式,且可能需要在Notice_Method下注释掉相关的三个库 13 | import win32clipboard as w 14 | import win32con 15 | import win32gui 16 | 17 | 2.安装并创建一个用于该脚本的MySql数据库,具体的表可以不用创建,脚本会判断是否存在指定表,如果不存在则会自动创建,推荐使用docker生成数据库,方便快捷, 18 | 提供Docker运行MySql命令如下,也可以自行查看Docker Hub 19 | sudo docker run -d -p 3306:3306 -v /mysql:/var/lib/mysql --name mysql -e MYSQL_ROOT_PASSWORD=[自定义密码] mysql:[缺省或填写指定版本] 20 | 21 | 3.推荐采用邮件方式接收推送,发信邮箱需要开通stmp/pop发信服务,配置中的发送邮箱密码需要填写开通时申请到的密码,QQ邮箱开通服务在 设置——账户 下, 22 | QQ接收方式需要在windows电脑下安装QQ或者Tim来实现,且要把推送的人的聊天窗口打开并保证鼠标指针激活在该QQ或者TIM上 23 | 24 | 4.将三个脚本放在同一目录下,所有配置信息均在Global_Config下,根据实际需求,填写配置后即可运行SMZDM_Fin_JSON_Price脚本 25 | -------------------------------------------------------------------------------- /SMZDM_Fin_JSON_Price.py: -------------------------------------------------------------------------------- 1 | #导入安装库 2 | import pymysql 3 | import requests 4 | import re 5 | import time 6 | import Global_Config 7 | import Notice_Method 8 | 9 | 10 | class GetPrice: 11 | def __init__(self,new_Not_M): 12 | self.new_Not_M = new_Not_M 13 | #class属性 14 | header = Global_Config.header 15 | url = Global_Config.url 16 | 17 | db_table = Global_Config.db_table 18 | db_host = Global_Config.db_host 19 | db_port = Global_Config.db_port 20 | db_user = Global_Config.db_user 21 | db_password = Global_Config.db_password 22 | db_name = Global_Config.db_name 23 | 24 | db_connect = pymysql.connect(host=db_host, port=db_port, user=db_user, password=db_password, db=db_name) 25 | cursor = db_connect.cursor() 26 | Monitor_Key = Global_Config.Monitor_Key 27 | 28 | 29 | def Monitor_Price_WriteDB(self): 30 | if (self.table_exists(self.cursor,self.db_table) != 1): 31 | print("对应表在数据库中不存在,正在创建...") 32 | sql_Create_Table = 'create table ' + self.db_table + '(article_date varchar(255) not null,article_title varchar(255) not null PRIMARY KEY,article_price varchar(255) not null,article_url varchar(255) not null,article_mall varchar(255) not null,article_timesort int(255) not null)' 33 | #sql_Create_Table='create table '+self.db_table+'(time varchar(255) not null,title varchar(255) not null PRIMARY KEY,timesort int(255) not null)' 34 | self.cursor.execute(sql_Create_Table) 35 | self.db_connect.commit() 36 | print('{}创建完成,开始将数据写入表中...'.format(self.db_table)) 37 | else: 38 | print("{}在数据库中已存在,开始将数据写入表中...".format(self.db_table)) 39 | 40 | 41 | def table_exists(self,cursor, table_name): 42 | sql = "show tables;" 43 | cursor.execute(sql) 44 | tables = [cursor.fetchall()] 45 | table_list = re.findall('(\'.*?\')', str(tables)) 46 | table_list = [re.sub("'", '', each) for each in table_list] 47 | if table_name in table_list: 48 | return 1 49 | else: 50 | return 0 51 | 52 | 53 | def get_json(self,timesort, timesort2): 54 | sql_data=[] 55 | if timesort <= timesort2 - 300: 56 | print('程序已经爬取5分钟之内的全部数据,已暂时停止,等待下次爬取...') 57 | return 58 | else: 59 | 60 | params = { 61 | 'type': 'a', 62 | 'timesort': timesort 63 | } 64 | response = requests.get(self.url, headers=self.header, params=params) 65 | response_json_data = response.json() 66 | print('当前爬取的时间戳为{},当前时间的时间戳为{}'.format(timesort, timesort2)) 67 | try: 68 | 69 | for json_data in response_json_data: 70 | article_date = json_data.get('article_date') 71 | article_title = json_data.get('article_title') 72 | article_price = json_data.get('article_price') 73 | article_url = json_data.get('article_url') 74 | article_mall = json_data.get('article_mall') 75 | article_timesort = json_data.get('timesort') 76 | sql_data = [article_date, article_title, article_price,article_url,article_mall,article_timesort] 77 | sql = 'insert into ' +self.db_table+ ' values(%s, %s, %s, %s, %s, %s)' 78 | self.cursor.execute(sql, sql_data) 79 | self.db_connect.commit() 80 | print(sql_data) 81 | for key in self.Monitor_Key: 82 | if sql_data[1].find(key) > -1: 83 | #with open('test.txt','a+') as f: 84 | #f.write(str(sql_data)+'\n') 85 | self.new_Not_M.Select_Method(str(sql_data)) 86 | break 87 | self.get_json(sql_data[5], timesort2) 88 | except pymysql.err.IntegrityError as f: 89 | print('由于{}已经在数据库中存在,爬虫已暂时停止,等待下次爬取...'.format(sql_data[1])) 90 | return 91 | 92 | 93 | def main(): 94 | new_Not_M = Notice_Method.Notice_Method() 95 | m1 = GetPrice(new_Not_M) 96 | m1.Monitor_Price_WriteDB() 97 | try: 98 | con = 0 99 | while con >= 0: 100 | con += 1 101 | print('当前时间:{},循环第{}次,已经运行{}秒'.format(time.strftime('%Y-%m-%d %H:%M:%S'), con, (con - 1) * Global_Config.Cycle_time)) 102 | m1.get_json(int(time.time()), int(time.time())) 103 | time.sleep(Global_Config.Cycle_time) 104 | except Exception as f: 105 | new_Not_M.Select_Method("程序由于意外已停止运行,报错原因为{},{},{}".format(f,f.__traceback__.tb_lineno,f.__traceback__.tb_frame.f_globals['__file__'])) 106 | print(f) 107 | 108 | 109 | 110 | 111 | if __name__ == '__main__': 112 | main() --------------------------------------------------------------------------------