夜间监测微博用户动态并发送短信提示该睡觉了

可以放在服务器上当作计划任务来运行,监测用户某一个时间段内的微博动态,并使用短信平台发送友情提示信息。

from datetime import datetime, timedelta
import requests
import sqlite3
from bs4 import BeautifulSoup
from functools import reduce
import re
from uuid import uuid3, NAMESPACE_DNS
import random
import math
import os
import json
# from selenium import webdriver
# from selenium.webdriver.common.keys import Keys
import time

# from selenium.webdriver.support.wait import WebDriverWait

"""
因为设置的自动运行的时间是从23点到次日的1点,所以方法中用存文件的方式来判断是否应该发短信有问题。
需要判断,当时间为0-1之间时,将日期-1再进行操作。
作者:i@unoiou.com
环境:python3, Windows 7 or later, server or personal version.
"""

cwd = 'E:\pytest'
os.chdir(cwd)
cwd = os.getcwd()
log_file = 'weibo_notification.log'

# [[uid, phone], [uid, phone], [uid, phone]]

weibo_id = [['your_weibo_id', 'your_relative_user_phone_number'], ['your_weibo_id', 'your_relative_user_phone_number']]
api = 'http://service.weibo.com/widget/widget_blog.php?uid='
db_path = os.path.join(cwd, 'weibo.db')
# ex: 123ac724f41234ccfa1234aec123432b (fake)
API_KEY = 'your_yunpian_sms_key'
# ex: 2124387 (fake)
TEMPLATE_ID = 'your_yunpian_int_template_id'
SMS_API = 'https://sms.yunpian.com/v2/sms/tpl_single_send.json'
# today = datetime.today()



# 时间间隔阈值
THRESHOLD = 5
EMOJI = ['(′ ▽〃)', '( ′o)', '(′ェ)', '(′ε )', '(=′ー)', '( ′θ)', '(′○)', '( ′-)', '(′∀=)', '(′▽)', '(′ノ0)', '( ′ロ )',
         '(′ ︿ )', '(′∇)', '(′m) ', '(′0ノ*)', '(@。ε。@)', '(=′∇=)  ', '(●′ω●)', '(′~●)', '(′へ、 )', '(〃′o)', '( ;′⌒`)',
         '(๑→ܫ←)', '(。・`ω´・)', '(σ`・д・)', '(o゚ω゚o)', '( ・ˍ・)', '(・ˍ・)', '(〃・o・〃)', '(^・・)', '(。・ˍ・。)', '(・0・)',
         '(・ε・●)', '(。・ω・)', '(。・д・。)', '(・∀・)', '(・□・、)', '(・-・。)', '(・▽・。)', '(・・。)', '(・O・。)', '(ノ△・。)', '(@・ˍ・)',
         '(・・)', '(・ε・;)', '(・ o ・)', '(・ェ・o)', '(′・∀・『)', '(ΘΘ)', '(Θ~Θ〃)', '(ΘoΘ)', '(ΘェΘ)', '(Θ∀Θ#)', '(ΘдΘ;)',
         '(Θ皿Θメ)', '(ΘーΘ*)', '(Θ0Θ●)', '(Θ▽Θ)', '(ΘεΘ◎)', '(Θ◇Θ。)', '(ΘへΘ)', '(ΘˍΘ=)', '(Θ、Θ)', '(Θ△Θ@)', '(Θ3Θ)',
         '(°ー°〃)', '(#°Д°)', '(。□。)', '(。∀。)', '(。∇^)', '( ^ー。)', '(@。ー。 @)', '(。∇^☆)', '(o。◇。)', '( 。 ▽ 。)', '(#。ε。#)',
         '(。∇^d)', '(。・。;)', '(。皿。メ)', '(* 。 3 ^)', '(〃。 o 。〃)', '( °◇ °)', '( 。⊿。)', '(°□°;)', '( ロ)', '(。Д。 ;)',
         '(*。ノO。)', '(; 。。)', '(><)', '(;><)', '(><)', '(>.<)',
         '(>o<)', '(>O<)', '(o>▽<)', '(>◇< )', '(>▽<)', '(;>△<)', '( >з<)', '(o>ェ<)', '(>д<)', '(>皿<)', '(><、)',
         '(/<。)', '(>。;)', '(>。ヘ)', '(ノ<)', '(>。☆)', '(>y<;)',
         '(-ω- )', '(+ω+)', '(ω )', '( ^ω^)', '(;ω;)', '(。ω。)', '(『ω′)', '(・ω・=)', '( ̄ω ̄)', '(〃ω〃)', '(≧ω≦)', '(。・ω・)',
         '(=・ω・=)', '( >ω<)', '(′・ω・`)', '(/ω\)', '(/ω・\)', '(/・ω・\)', '(ΘωΘ)', '(ΦωΦ)']


def log(s, t):
    """
    运行日志
    :param s:任意字符串
    :param t:1:INFO, 2:ERROR
    :return:
    """
    global log_file, cwd
    date_ = datetime.today().ctime()
    types = [' ', '[INFO]\t', '[ERROR]\t']
    with open(os.path.join(cwd, log_file), 'at') as f:
        info = '  -  '.join((types[int(t)], date_, s, '\n'))
        f.write(info)
        print(info)


def parse_weibo(uid, mobile):
    """
    前期使用Requests库,比较简单。
    根据微博WidgetURL解析微博数据,生成器
        repost: 转发理由
        type_: 0 原创, 1 转发
        0 -> False
        return: {id:id, content:content, repost:repost, time:time_, date:date_, type:type_, imgs:imgs}
    """
    global api
    url = api + uid
    log(url, 1)
    ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36'
    header = {'User-Agent': ua}
    res = requests.get(url, headers=header)
    res.encoding = 'utf-8'

    soup = BeautifulSoup(res.text, 'html5lib')
    weibos = soup(class_='wgtCell')
    nick_name = soup(class_='userNm')[0].text

    global THRESHOLD

    for weibo in weibos:
        plain_txt = weibo(class_='wgtCell_txt', limit=1)
        plain = reduce(lambda x, y: x.text + y.text, plain_txt)
        plains = str(plain).split('<br/>')

        imgs = weibo('img')
        t = weibo(class_='wgtCell_tm', limit=1)

        img = imgs[0]['src'] if len(imgs) > 0 else ''
        contents = plain_txt[0].contents
        type_ = 0 if str(contents[0]).startswith('转发了') else 1
        repost = '' if type_ else plains[-1]
        content = reduce(lambda x, y: str(x).strip() + str(y).strip(), contents) if type_ else ''.join(plains[:-1])
        t_span = str(t[0]('a')[0].text)
        if t_span.startswith('今天'):
            time_ = t_span.split(' ')[1]
            date_ = datetime.now().date().strftime('%Y-%m-%d')
        elif t_span.endswith('前'):
            if int(re.findall('(\d+)', t_span)[0]) < THRESHOLD:
                send_notification(mobile)
            else:
                log('Less than threshold.' + str(THRESHOLD) + '\t' + str(uid), 1)
            time_ = datetime.now() - timedelta(minutes=int(re.findall('(\d+)', t_span)[0]))
            time_ = str(time_.hour) + ':' + str(time_.minute)
            date_ = datetime.now().date().strftime('%Y-%m-%d')
        else:
            date_, time_ = t_span.split(' ')
            m, d, _ = re.split('\D', date_)
            date_ = (int(datetime.now().year), int(m), int(d))
            date_ = reduce(lambda x, y: str(x) + '-' + str(y), date_)

        wid = gid(date_ + time_ + content + str(uid)).__str__()

        result = (content, repost, time_, date_, type_, img, uid, wid, nick_name)

        yield result


def check_like(uid, mobile):
    """
    这里必须使用selenium库来解析微博用户点赞页面
    使用selenium需要按照报错信息下载对应的浏览器的webdriver并放到当前工作目录。
    :return:
    """
    global cwd, weibo_id

    log('ID: ' + uid + '=====>', 1)

    browser = webdriver.Firefox()

    retry = 3

    url = 'https://weibo.com/' + uid + '/like'

    browser.get(url)
    browser.implicitly_wait(40)

    while retry > 0:
        retry -= 1
        try:
            browser.find_element_by_class_name('WB_tab_a').send_keys(Keys.DOWN)
            source = browser.page_source
            log('Successfully get HTML', 1)
            break
        except Exception as e:
            log(e.__str__(), 2)

    time.sleep(2)

    now = datetime.now()
    year, month, day = now.year, now.month, now.day
    # 如果时间是0-3点,日期-1.
    hour = now.hour
    if 0 <= hour <= 4:
        day -= 1
    # ============================
    day = '0' + str(day) if day < 10 else day
    month = '0' + str(month) if month < 10 else month

    base_file = str(year) + str(month) + str(day) + '_' + uid + '_like.txt'
    # base_file = os.path.join(cwd, base_file)
    log(base_file, 1)

    # 获取当前点赞的微博数量
    finds = re.findall(r'共(\d+)条', source)
    try:
        like_num = int(finds.pop().strip())
        log('Success: HTML', 1)
    except Exception as e:
        log(e.__str__(), 2)
        log('获取HTML失败。', 2)
        log(source, 2)
        return 0
    log('Liked Number: ' + str(like_num), 1)

    # 存一个当天的base文件,记录从23点开始的点赞数量。
    # 当天第一次运行,存入点赞数量
    if os.path.exists(base_file):
        with open(base_file, 'rt') as f:
            last_like_num = f.readline().strip()
            log('Liked number: ' + str(like_num), 1)
    else:
        last_like_num = None
        log('No exist like num', 1)

    with open(base_file, 'wt') as f:
        log('Last like num: ' + str(last_like_num), 1)
        # 如果存了上一次的数字
        flag = False
        if last_like_num:
            last_like_num = int(last_like_num)
            if like_num > last_like_num:
                flag = True
            else:
                flag = False
                like_num = last_like_num
        f.write(str(like_num))
        log(str(like_num), 1)
        if flag:
            send_notification(mobile)
            log('Sent Msg to ' + mobile, 1)
        else:
            log('Not Send Msg.', 1)
    browser.close()


def deposit_weibo(item):
    """
    存储丢进来的微博
    CREATE TABLE weibo (
    id       INTEGER  PRIMARY KEY AUTOINCREMENT,
    nickname TEXT     NOT NULL,
    content  TEXT,
    repost   TEXT,
    time     DATETIME NOT NULL,
    date     DATE     NOT NULL,
    type     INT      NOT NULL,
    imgs     TEXT,
    uid      INT      NOT NULL,
    wid      TEXT     NOT NULL
                      UNIQUE
);
    :param item: 微博内容 tuple
    :return: 存储结果,True/False
    """
    conn = sqlite3.connect(db_path)
    c = conn.cursor()
    try:
        c.execute(
            'INSERT INTO weibo (content, repost, time, date, type, imgs, uid, wid, nickname) VALUES (?,?,?,?,?,?,?,?,?)',
            item)
    # 微博wid相同,与定义相悖,表示已经存储过了,就不再重复存储。
    except Exception as e:
        del e
        conn.commit()
        conn.close()
        return False
    conn.commit()
    conn.close()
    return True


def gid(s):
    """
    根据 微博的一些文本、时间戳、用户ID 生成微博UUID。这与SQLITE3 DDL中的wid unique一致。
    """
    return uuid3(NAMESPACE_DNS, s)


def send_notification(mobile):
    """
    给一个人发短信,这里使用的是云片网的模板单条发送的api
    """
    today = datetime.today().now()
    year, month, day, hour = today.year, today.month, today.day, today.hour
    if 3 >= hour >= 0:
        day -= 1
    fname = str(year) + str(month) + str(day) + '_' + mobile + '.txt'
    if os.path.exists(fname):
        log('Already sent sms, do nothing.', 1)
    else:
        with open(fname, 'at') as f:
            global EMOJI, API_KEY, TEMPLATE_ID, SMS_API
            url = SMS_API
            key = API_KEY
            tpl_id = TEMPLATE_ID
            # 这个要和你在短信平台上设置的模板一致。只需要组合成一段文本。最好的方式是urlencode()一下
            tpl_value = '#emoji#=' + EMOJI[math.floor(random.random() * len(EMOJI))] + '么么哒~😜'
            params = {'apikey': key, 'tpl_id': tpl_id, 'tpl_value': tpl_value, 'mobile': mobile}
            res = requests.post(url, data=params)
            log(json.dumps(res.json()), 1)
            print(res.json())
            log('Sent msg   to ' + mobile, 1)
            json.dump(res.json(), f)


def main():
    log('==========================================', 0)
    for u in weibo_id:
        uid = u[0]
        mobile = u[1]
        # if mobile != '13477692921':
        #     continue
        log('Mobile: ' + mobile + ' UID: ' + uid, 1)
        # check_like(uid, mobile)
        items = 0
        for weibo in parse_weibo(uid, mobile):
            if deposit_weibo(weibo):
                items += 1
        log('Found new items: ' + str(items), 1)


if __name__ == '__main__':
    main()

posted @ 2018-03-21 09:14  小鑫同学  阅读(211)  评论(0编辑  收藏  举报

© 2018 追梦网 | Created by Cai M$ | 博客园 | 推荐使用 Microsoft EdgeWindows 10