一个可以发微博的多功能脚本
===
更新于2018年6月23日20:32:59,添加了一些新功能,详情:
https://github.com/786662216/weibo
注意发图片是这样的,传进去以二进制的方式打开的图片文件对象即可。常用的jpg,gif,png都支持,大小小于5mb
pic = open('./pic/%s' % picture, 'rb')
client.statuses.share.post(pic=pic)
pic.close()
当然也可以:
with open('./pic/%s' % picture,'rb') as pic:
client.statuses.share.post(pic=pic)
以下为原文
最近一直在学python,想练个手,于是弄了个发微博的脚本。新浪微博的API更新了也不把旧的删掉,新的还没有给代码示例 ,官方微博两天才回复,不爽
东西比较杂,比较重点的有
1. oAuth2.0协议
1. python的datetime、random模块
1. Linux的进程问题
其他的东西比较杂,到时候会提到
# 0x00 oAuth2.0
一个很常见的情景:如果一个用户需要两项服务:一项服务是图片在线存储服务A,另一个是图片在线打印服务B。服务A在甲网站上,服务B在乙网站上。这时候有两种传统解决方案:法一:用户可能先将待打印的图片从服务A上下载下来并上传到服务B上打印,这种方式安全但处理比较繁琐,效率低下;法二:用户将在服务A上注册的用户名与密码提供给服务B,服务B使用用户的帐号再去服务A处下载待打印的图片,这种方式效率是提高了,但是安全性大大降低了。oAuth协议就是为了解决类似问题的而诞生的。
新浪微博就是你的家。偶尔你会想让一些人(第三方应用)去你的家里帮你做一些事,或取点东西。你可以复制一把钥匙(用户名和密码)给他们,但这里有三个问题:
* 别人拿了钥匙后可以去所有的房间
* 别人拿到你的钥匙后也许会不小心丢到,甚至故意送到它人手里。这样你都不知到谁有你家用钥匙
* 过一段时间你也许会想要回自己的钥匙,但别人不还怎么办?
**OAuth 是高级钥匙:**
* 你可以配置不同权限的钥匙。有些只能进大厅(读取你的微博流)。有些钥匙可以进储藏柜(读取你的相片)
* 钥匙上带着指纹验证的(指纹 = appkey)。 收到钥匙的人只能自己用,不能转让
* 你可以远程废除之前发出的钥匙
* 相对来说, OAuth比给出用户名密码安全
链接:https://www.zhihu.com/question/19781476/answer/13158282
OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。微博开放接口的调用,如发微博、关注等,都是需要获取用户身份认证的。不仅仅是新浪微博,主流互联网产品的API授权都是用的oauth协议。
在微博里:client第三方应用客户端或服务器(其实是自己开发的,后面会说),用户就是用户(还是我自己),第二方应用服务器指的是微博的授权服务器,要请求资源所在的服务器就是微博API服务器
接下来我们细说一下其过程:
* 首先A网站要在B网站上注册一个应用。在这个应用创立好之后就已经规定A网站只能获得或者访问B网站中用户的那些功能和信息,同时B网站给了A网站两个字符串:Key、secret作为凭证
* 当用户在A网站上需要用到B网站的功能时,A网站会吧key给用户,用户带着key到了B网站的认证网站或应用,B网站会询问用户是否同意并在网页或者手机应用上显示出A网站的权限,比如说只能访问照片。用户如果不同意,那么结束授权过程;如果同意,则B网站会返回一个code,来证明用户已经同意,并且会记录下这次认证。
* 这时候A网站拿到code后,A网站把key、secret、code一起发送给B网站,换取最后的通行证access_token,之后A网站就可以拿着access_token去访问B网站中它需要的用户信息和服务了。当然,access_token是有时间限制的,之前AB网站也已经约定好了只能访问哪些信息,而且用户可以随时废掉这个access_token。这个这里secret的主要目的是code可以被任何人持有,用secret来证明我是A网站。
(当然也有一些弱点,比如这里存在着一个容易被CRSF的弱点,以后总结CRSF时再提)
# 0x01微博API调用
我们是通过第三方应用调用API来对自己的微博进行操作,所以认证过程和上述的一样,只不过这个第三方应用和微博账号都是我们自己的。
1、首先申请新浪微博的第三方应用(微博开放平台里申请),并且在应用信息的测试信息里把自己的账号关联到测试账号里。同样的,如果你有多个账号想发送微博或者进行其他操作的话,也可以馆两道测试账号中去,最多十五个。再多就要审核应用了,很麻烦。
2、申请完第三方应用后他会给出APP_KEY和APP_SECTET两个字符串,把他们保存好。在应用信息的高级信息中编辑OAuth2.0的授权设置,更改授权回调页为https://api.weibo.com/oauth2/default.html,其他的我没试过,理论上应该都可以。主要目的就是拿到code。还要设置安全域名,安全域名在应用信息->基本信息->应用基本信息里编辑,发送的微博里一定要带这个域名下的一个URL,或者就是设置的URL本身也行,记得加上协议http(s)
3、安装python的微博SDK:sinaweibopy,由廖雪峰大神提供。直接pip install sinaweibopy就可以,直接从网站上下载也可以。
4.获取code:
from weibo import APIClient
APP_KEY = 'XXXXXXXXX' # app key
APP_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # app secret
CALLBACK_URL = 'https://api.weibo.com/oauth2/default.html' # callback url
client = APIClient(app_key=APP_KEY, app_secret=APP_SECRET, redirect_uri=CALLBACK_URL)
print client.get_anthorize_url()
访问打印出的URL,会重定向到会跳地址,地址栏后会有code参数,把code的值记下来
5、获取access_token:
r = client.request_access_token(code) #注意加引号,这是字符串
access_token = r.access_token
expires_in = r.expires_in #这个是有效期,单位是秒。有效期大概为3、4年
client.set_access_token(access_token, expires_in) #绑定access_token
6、直接使用API发送微博:
当然有很多API,都是通过client来调用的
# 0x02 python的datetime、random模块
现在可以使用API来对我的微博账号进行一系列的操作了,我想实现的是每天半夜三点发一条微博,而且每天微博的内容不能重复,这就要用到python关于时间和随机数的库了。
**datetime模块**
time类在模块中,而且和time模块有几乎相同的功能,所以没有用到time模块,暂且不提。
date类
静态方法和字段
date.max、date.min:date对象所能表示的最大、最小日期;
date.resolution:date对象表示日期的最小单位。这里是天。
date.today():返回一个表示当前本地日期的date对象;
date.fromtimestamp(timestamp):根据给定的时间戮,返回一个date对象;
**方法和属性**
datetime.date(year, month, day)
d1 = date(2011,06,03)#date对象
d1.year、date.month、date.day:年、月、日;
d1.replace(year, month, day):生成一个新的日期对象,用参数指定的年,月,日代替原有对象中的属性。(原有对象仍保持不变)
d1.timetuple():返回日期对应的time.struct_time对象;
d1.weekday():返回weekday,如果是星期一,返回0;如果是星期2,返回1,以此类推;
d1.isoweekday():返回weekday,如果是星期一,返回1;如果是星期2,返回2,以此类推;
d1.isocalendar():返回格式如(year,month,day)的元组;
d1.isoformat():返回格式如'YYYY-MM-DD’的字符串;
d1.strftime(fmt):和time模块format相同。
**datetime类**
datetime相当于date和time结合起来。
datetime.datetime (year, month, day[ , hour[ , minute[ , second[ , microsecond[ , tzinfo] ] ] ] ] )
**静态方法和字段**
datetime.today():返回一个表示当前本地时间的datetime对象;
datetime.now([tz]):返回一个表示当前本地时间的datetime对象,如果提供了参数tz,则获取tz参数所指时区的本地时间;
datetime.utcnow():返回一个当前utc时间的datetime对象;#格林威治时间
datetime.fromtimestamp(timestamp[, tz]):根据时间戮创建一个datetime对象,参数tz指定时区信息;
datetime.utcfromtimestamp(timestamp):根据时间戮创建一个datetime对象;
datetime.combine(date, time):根据date和time,创建一个datetime对象;
datetime.strptime(date_string, format):将格式字符串转换为datetime对象;
**方法和属性**
dt=datetime.now()#datetime对象
dt.year、month、day、hour、minute、second、microsecond、tzinfo:
dt.date():获取date对象;
dt.time():获取time对象;
dt. replace ([ year[ , month[ , day[ , hour[ , minute[ , second[ , microsecond[ , tzinfo] ] ] ] ] ] ] ]):
dt. timetuple ()
dt. utctimetuple ()
dt. toordinal ()
dt. weekday ()
dt. isocalendar ()
dt. isoformat ([ sep] )
dt. ctime ():返回一个日期时间的C格式字符串,等效于time.ctime(time.mktime(dt.timetuple()));
dt. strftime (format)
**timedelta类,时间加减**
使用timedelta可以很方便的在日期上做天days,小时hour,分钟,秒,毫秒,微妙的时间计算,如果要计算月份则需要另外的办法。
dt1 = dt + timedelta(days=-1)#昨天
dt2 = dt - timedelta(days=1)#昨天
dt3 = dt + timedelta(days=1)#明天
delta_obj = dt3-dt
print type(delta_obj),delta_obj#<type 'datetime.timedelta'> 1 day, 0:00:00
print delta_obj.days ,delta_obj.total_seconds()#1 86400.0
**random模块**
random.uniform()用于生成一个指定范围内的随机浮点数,两个参数其中一个是上限,一个是下限。上下限没有顺序
random.randint(a,b)用于生成一个指定范围内的整数。其中参数a是下限,参数b是上限。上下限是有顺序的,反了会报错
random.choice()参数是个元素全为字符串的列表,也可以是一个字符串。如果传入一个列表,从其中随机选取一个元素并返回;如果传入了一个字符串,则随机返回一个字符
random.sample()选取特定数量的字符;两个参数,第一个是个元素全为字符的列表,第二个参数是要选取的个数
random.shuffle()洗牌函数。顾名思义,接受一个列表,将其元素的顺序打乱并返回这个新列表
# 0x03 源代码
在这里我把各个文件各个函数拆开直接合在一起了,亲测可用,注释也写的还算明白(不
coding:utf-8
#注意每个微博API调用成功后会返回一个很长的列表,具体参数含义参考微博开放平台
from weibo import APIClient
import time #可忽略
import datetime
import random
APP_KEY = 'XXXXXXXX' # app key
APP_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # app secret
CALLBACK_URL = 'https://api.weibo.com/oauth2/default.html' # callback url
access_token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
expires_in = 1660225135
client = APIClient(app_key=APP_KEY, app_secret=APP_SECRET, redirect_uri=CALLBACK_URL)
client.set_access_token(access_token, expires_in)
schedtime = datetime.datetime(2017,8,15,3,3,3) # 要执行的时间
Frequency = datetime.timedelta(days=1) # 频率,这个函数的参数有days,months,seconds,years,minutes都是字面意思,也可以为负
while True:
time.sleep(1) # 循环执行的速度太快,虽然只有一秒但也会执行很多次,所以要等1s
TimeSecound = time.time() #这个确实没啥用
date = time.localtime(TimeSecound) # 现在的具体时间
DateMday = date[2]
DateHour = date[3]
str = u'''咚~咚~咚~我是老佘百分百的机器人%s,现在是%d年%d月%d日%d时%d分%d秒,
现在我只有报时功能哦~ http://www.google.com 404喽%s''' % (random.choice(
[u'(๑•̀ㅂ•́)و✧', u'ヾ(≧▽≦*)o ', u'o(*≧▽≦)ツ ', u'(o゜▽゜)o☆', u'<( ̄︶ ̄)>', u'o(* ̄▽ ̄*)o ', u'(。・∀・)ノ゙', u'ヾ(≧∇≦*)ゝ',
u'Hi~ o(* ̄▽ ̄*)ブ ', u'(≧∀≦)ゞ', u'ε(*´・∀・`)з゙', u'(~ ̄▽ ̄)~ ', u'︿( ̄︶ ̄)︿', u'(/≧▽≦)/', u'(ノ*・ω・)ノ',
u'o(〃\'▽\'〃)o ', u'o( ̄▽ ̄)d ', u'o(^▽^)o']), date[0], date[1], DateMday, DateHour, date[4], date[5],
random.choice([u'w(゚Д゚)w', u'O(≧口≦)O', u'Σ(`д′*ノ)ノ', u'ヽ(*。>Д<)o゜', u'┻━┻︵╰(‵□′)╯︵┻━┻', u'φ(-ω-*)', u'(σ`д′)σ', u'(#`O′)', u'( >ρ < ”)', u'o(一︿一+)o', u'(#`O′) ', u'(>﹏<)', u'(;′⌒`) ', u'(;´д`)ゞ', u'Σ( ° △ °|||)︴', u'(lll¬ω¬)', u'…(⊙_⊙;)…', u'_〆(´Д` ) ', u'Σ(っ °Д °;)っ', u'(・-・*)', u'(°ー°〃) ', u'(((φ(◎ロ◎;)φ)))', u'o((⊙﹏⊙))o.', u'ヽ(*。>Д<)o゜ ', ]))
now = datetime.datetime.now() # 返回值里面有微秒可把我坑惨了
if now.date() == schedtime.date(): # 先判断日期相不相同
if (now.hour == schedtime.hour) and (now.minute == schedtime.minute) and (now.second == schedtime.second): # 在判断时间相不相同,主要是为了避开微秒的比较
client.statuses.share.post(status=str) # 这是有返回值的
schedtime = schedtime + Frequency
print now, u"sent successfully"
else:
pass
else:
pass
# 0x04 Linux下执行
现在只要送到阿里云里运行就可以了
python SendWeiboAt3 >weibo.log &
最后&符号就是表示后台运行,运行输出的结果存在weibo.log文件里。但是不要直接关闭ssh客户端,要用exit退出。如果直接关闭ssh客户端则linux会关闭掉所有bash进程
现在的问题是我如何在Linux下运行多个python进程。再直接用python解释器直接运行的话上一个运行的脚本就会关闭换成这个新的脚本,能从PID号看出不是一个进程了。目前来说也用不到,以后再说吧
¥ 0x05 总结
解决时间比较长的问题:
1、API更新我不知道
2、新API发送的格式里要带URL,而且是必须是自己设置的安全域名下的URL3、datetime.datetime.now()还会返回微秒:datetime.datetime(2017, 8, 26, 23, 54, 43, 950000)最后那个参数,我设置的执行时间datetime.datetime(2017,8,15,3,3,3)里没有微秒,if比较的时候结果永远是False。即使加上微秒也没用,python的时间观念精确不到一秒的10^-6(用while尝试一下,发现微秒没那么精确,和操作系统本身处理速度有关),解决方法看代码
4、在Ubuntu下运行时直接退出xshell了,导致第一天没发送成功