新浪微博自动登录与微博发送

话说Ele自从买了Kindle之后,阅读量蹭蹭的上去了。小K有个好处,就是可以摘录一些片段。然而,眼见着摘录越来越多,但是懒惰的Ele却从来没有打开回温一下,这是不对的!!于是乎,Ele决定把其推送到微博上,没事刷一刷。ε(┬┬﹏┬┬)3 为了战胜自己,也是够了!

自动登录微博

虽然新浪微博提供了Python API可以直接接入。但是,本着不想暴露过多个人信息,最后还是决定模拟登录之。
如果要模拟PC端的登录,过程相当复杂。需要根据用户名密码算出多个值,其过程还涉及到各种加密运算。网上已经有很多相关的内容描述这整个过程。但由于Google上找到的大部分相关信息都是比较久远的,对当前不再适用。此时,祭出黑招:

移动端的登录通常比PC端登录简单很多很多,因此,登录解析可以从移动端入手。

怎么找到移动端呢?有一个简单粗暴的方法:拿出手机,登录到网站上,就会自动跳到移动端啦。这里,新浪微博的移动端是:http://m.weibo.cn/。
模拟的思路可以参考之前写过的沪江部落自动登录打卡
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import requests
import re
class Weibo(object):
'''
新浪微博类
'''
def __init__(self, username, password):
'''
借助移动端进行登录
'''
#user,password用户名密码,使用自己注册的sina用户名密码
self.username = username
self.password = password
self.session = requests.Session()
self._login()
def _login(self):
#登录地址
url_login = r"https://passport.weibo.cn/sso/login" # 是的,这就是移动端的登录地址
headers = { # 这个请求头一定要有,否则会失败
"Host": "passport.weibo.cn",
"Connection": "keep-alive",
"Origin": "https://passport.weibo.cn",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "*/*",
"Referer": "https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F",
"Accept-Language": "zh-CN,zh;q=0.8",}
postdata = {
"username" : self.username,
"password" : self.password,
"savestate" : "1",
"ec" : "0",
"pagerefer" : "https%3A%2F%2Fpassport.weibo.cn%2Fsignin%2Fwelcome%3Fentry%3Dmweibo%26r%3Dhttp%253A%252F%252Fm.weibo.cn%252F%26wm%3D3349%26vt%3D4",
"entry" : "mweibo", #我猜,这里是mobile weibo的意思,表明登录是来自移动端
"wentry" : "",
"loginfrom" : "",
"client_id" : "",
"code" : "",
"qq" : "",
"hff" : "",
"hfp" : "",
}
resp = self.session.post(
url_login,
data=postdata,
headers=headers
).json()
self.uid = resp['data']['uid'] #保存用户id
for url in resp['data']['crossdomainlist'].values(): # 响应中返回的domainlist每个要请求一下,否则登录不完整
if not url.startswith("http:") and not url.startswith("https:"): url = "http:" + url
self.session.get(url)
self.session.get("http://m.weibo.cn/") # 最后这里如果能够正常看到响应结果,说明登录成功

微博发布

因为前面登录是在移动端登录的,所以下面的微博发布也需要在移动端进行。进入微博发布界面,打开Fiddler,随便发送一条微博,可以看到会POST新微博到http://m.weibo.cn/mblogDeal/addAMblog上。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def add_new(self, content):
'''
create a new weibo发布新微博方法
'''
addurl = "https://m.weibo.cn/mblogDeal/addAMblog"
st = re.findall(r'"st":"(\w+)"', self.session.get(r"http://m.weibo.cn/mblog").text)
# 如果发送数据中有一些值为数字字母等混合的长得像随机数的参数,
# 建议可以在页面源代码里找找,然后用正则表达式提取出来。就像这里的st
data = {'content':content, 'st':st[0],}
headers = { # headers也是必不可少的,否则会有什么安全问题导致发送失败
"Host": "m.weibo.cn",
"Connection": "keep-alive",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Origin": "http://m.weibo.cn",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Referer": "http://m.weibo.cn/mblog",
"Accept-Language": "zh-CN,zh;q=0.8"
}
respon = self.session.post(addurl, data, headers=headers).json()
return respon.get("msg", "Unknow Error") # 这里的msg是发布结果

编写摘抄

小K上的摘录都保存在一个My Clippings.txt文件中。之前出于某种目的已经把它们整合到sqlite数据库app.db里的clipping表里了。直接操作此表既可以获得摘录信息。
另外,因为是希望把它加进香蕉派的定时任务中定时发送的,所以每次获得的摘录当然不能重复。因此会把获得的下一个摘录的id保存在clipping_index文件里。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Clipping(object):
'''
片段摘抄
'''
def __init__(self, db=r"/home/ele/lab/myxixi/app.db"):
self.conn = sqlite3.connect(db)
self.conn.isolation_level = None
# 获得下一个摘录的id
tmp = os.popen("cat clipping_index").read()
self.index = int(tmp) if tmp else 1
# 保存最大的id值
self.max_index = int(self.conn.execute("select max(id) from clipping").fetchone()[0])

def get_one(self, topic=u"片段"):
while self.index <= self.max_index : # 避免死循环
result = self.conn.execute("select * from clipping where id=%d"%self.index).fetchone()
self.index += 1
if result: # 若获得的结果不为空,则保存下一个摘录的id,然后返回内容
os.popen("echo %d > clipping_index" % self.index)
return u"#{}##{}#{}".format(topic,result[1],result[2])

放在一起吧

现在,让我们把它们放在一起测试一下:

1
2
3
4
5
6
7
if __name__ == "__main__":
username = '*********'
password = '**********'
wb = Weibo(username, password)
cl = Clipping()
content = cl.get_one()
print wb.add_new(content)

最后

设个定时任务,每30min给Ele的微博推送一条摘录。这样,就可以好好利用碎片时间温故而知新啦♪(^∇^*)

请言小午吃个甜筒~~