想必大家一定还记得,我写过两个和人人网有关的脚本。一个是利用bash、w3m和wget制作的《人人网LINUX准客户端脚本》,主要功能为终端下的消息提醒和快速访问。(终端响铃很NB吧?)另一个则是其前身《人人消息提醒》,功能也不算完备,而且还需自行处理cookie。
现在看来,这种玩弄网页的方式实在是不稳定,万一人人改版,那就几乎要重写一个了。随着知识水平的提高,我也能够不断尝试新的方法。既然人人已经提供了api,那就干脆用一下好了。
在这里不得不再吐槽人人一下。其python版本的sdk简直不忍直视,而且万年无人更新。新版本的api干脆就没有python的sdk了。果然我小众群体是不被关注的。没办法,只能自力更生,从头写起。本人精力有限,仅试图实现了有限的功能(发状态和回复,无提醒),供大家参考。代码质量什么的就不要吐槽了。
程序说明
本程序由python3写成,使用了selenium控制浏览器(主要是为了获取各种token,已经假设你使用firefox),easygui提供图形界面和pil显示图片。使用前除了标准库之外,需要将这些库安装到位。安装方法请自行搜索。程序分三部分。freerr.py主要管理登陆和基本流程控制,也是用户直接运行的main部分。它能在主目录下创建配置文件夹以保存access_token、用户id和图片缓存。renrenobj.py则发挥了一个劣质sdk的作用。freerr.php放在你的服务器端以获取各种token并被本地读取。不要问我为什么需要这个文件,去问人人oauth机制为什么一定要是这样的。总之这样可以把secret_key保护好。
本人尝试了把客户端提交给人人,可是被退回了,理由是不能安装(是你不会用吧?为什么不看README?)。所以,本程序就不能作为第三方应用的制成品发布了。可以说这篇文章是面向开发者的(为什么现在才说!?)。各位试用时需要先获得人人开放平台的应用api_key和secret_key,修改好php文件后放在自己服务器上,然后改好本地文件的redirect_url和api_key之后方能使用。需要改的内容大多在字符串中,周围的注释有五个感叹号(=_=!),大家仔细寻找,不要漏掉。
代码
freerr.py
#!/usr/bin/env python3 # -*- coding:utf-8 -*- '''freerr.py
This is an attempt to make a client for Renren on Linux using Python3.
Author: Tianhu Zhang <zszth@126.com> (A beginner to Python3)
renren.py is the main file which includes the very basic auth work.
And all the editable constants are listed in this file for convenience.
More useful classes are in renrenobj.py and should be imported before use.
'''
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import urllib.request
import json
import renrenobj
import os
# import getpass
import easygui as g
# import glob
import webbrowser
REFRESH_TOKEN = ''
ACCESS_TOKEN = ''
USER_ID = 0
if os.path.isfile(os.path.join(os.path.expanduser('~/.freerr'), '.freerr')):
with open(os.path.join(os.path.expanduser('~/.freerr'), '.freerr'), mode='r', encoding='UTF-8') as conf_file:
ACCESS_TOKEN = conf_file.readline()
USER_ID = int(conf_file.readline())
else:
# user_email = input('Input Email to login: >')
# user_password = getpass.unix_getpass('Input password: >')
user_email = g.enterbox('Input Email to login:', 'Login')
user_password = g.passwordbox('Input password:', 'Login')
browser = webdriver.Firefox()
#!!!!!以下修改两处。client_id后面加上api_key,redirect_uri后加上php文件的url。
browser.get(
'https://graph.renren.com/oauth/authorize?client_id=&redirect_uri=&response_type=code')
username = browser.find_element_by_name('username')
username.send_keys(user_email)
password = browser.find_element_by_name('password')
password.send_keys(user_password)
password.send_keys(Keys.ENTER)
json_inputbox = browser.find_element_by_tag_name('textarea')
a_t_data = json.loads(json_inputbox.text, encoding='UTF-8')
browser.close()
# REFRESH_TOKEN = a_t_data['refresh_token']
ACCESS_TOKEN = a_t_data['access_token']
USER_ID = a_t_data['user']['id']
os.mkdir(os.path.expanduser('~/.freerr'))
with open(os.path.join(os.path.expanduser('~/.freerr'), '.freerr'), mode='w', encoding='utf-8') as conf_file:
conf_file.writelines(ACCESS_TOKEN + '\n')
conf_file.writelines(str(USER_ID))
renrenobj.A_T = ACCESS_TOKEN
renrenobj.USER_ID = USER_ID
f = renrenobj.Feed()
flag_exit = False
i = 0
while not flag_exit:
if f.feed_list[i]['attachment'] and f.feed_list[i]['attachment'][0]['orginalUrl']:
if not os.path.isfile(
os.path.join(os.path.expanduser('~/.freerr'), '.freerr_' + str(f.feed_list[i]['resource']['id']))):
img_data = urllib.request.urlopen(f.feed_list[i]['attachment'][0]['orginalUrl']).read()
with open(os.path.join(os.path.expanduser('~/.freerr'), '.freerr_' + str(f.feed_list[i]['resource']['id'])),
mode='wb') as img_file:
img_file.write(img_data)
choice = g.buttonbox('{0} {1} {2}\n {3}\n source: {4}\n {5}'.format(i, f.feed_list[i]['time'],
f.feed_list[i][
'sourceUser'][
'name'],
f.feed_list[i]['message'],
f.feed_list[i]['resource'][
'title'],
f.feed_list[i]['resource'][
'content']),
'Feed List ' + str(i),
['Next', 'Comment', 'Open Browser', 'Update Status', 'Refresh', 'Exit', 'Prev'],
image=os.path.join(os.path.expanduser('~/.freerr'),
'.freerr_' + str(f.feed_list[i]['resource']['id'])))
else:
choice = g.buttonbox('{0} {1} {2}\n {3}\n source: {4}\n {5}'.format(i, f.feed_list[i]['time'],
f.feed_list[i][
'sourceUser'][
'name'],
f.feed_list[i]['message'],
f.feed_list[i]['resource'][
'title'],
f.feed_list[i]['resource'][
'content']),
'Feed List ' + str(i),
['Next', 'Comment', 'Update Status', 'Refresh', 'Exit', 'Prev'])
if choice == 'Prev':
if i == 0:
g.msgbox('No Prev!', 'Error')
else:
i = i - 1
elif choice == 'Exit':
flag_exit = True
elif choice == 'Next':
if i == len(f.feed_list) - 1:
g.msgbox('No Next!', 'Error')
else:
i = i + 1
elif choice == 'Comment':
c = renrenobj.Comment(f.feed_list[i]['type'], f.feed_list[i]['sourceUser']['id'],
f.feed_list[i]['resource']['id'])
cl = []
for j in range(len(c.comment_list)):
cl.append('{0} {1} {2}\n {3}'.format(j, c.comment_list[j]['time'],
renrenobj.User.get_user_name(renrenobj.User,
c.comment_list[j]['authorId']),
c.comment_list[j]['content']))
cl.append('-1 Make a comment without replying to anyone')
reply_choice = g.choicebox('Choose a person to reply:', 'Comments List', cl)
if reply_choice:
c.make_comment(int(reply_choice.split(' ')[0]))
elif choice == 'Update Status':
renrenobj.status_pub()
elif choice == 'Refresh':
del f
f = renrenobj.Feed()
i = 0
elif choice == 'Open Browser':
webbrowser.open(f.feed_list[i]['attachment'][0]['url'])
renrenobj.py
#!/usr/bin/env python3 # -*- coding:utf-8 -*- '''renrenobj.py
Renren defines a lot of special types.
This is a library of types returned in JSON.
'''
import json
import urllib.request
import urllib.parse
import easygui as g
A_T = ''
USER_ID = 0
def get_json_get(url_in):
'''Function get_json_get
GET mode.
This returns an object loaded from json.
When error return None.
Make sure the end of url_in and the beginning of a_t are correct.
If url_in is with a parameter, add a '&' after it.
'''
data = urllib.request.urlopen(url_in + 'access_token=' + A_T).read()
json_data = data.decode('UTF-8')
obj = json.loads(json_data, encoding='UTF-8')
if 'error' in obj:
g.msgbox(str(obj), 'Error')
return None
else:
return obj['response']
def get_json_post(url_in, para):
'''Function get_json_post
POST mode.
para should be a dict.
This returns an object loaded from json.
When error return None.
Make sure the end of url_no_a_t and the beginning of a_t are correct.
'''
postdata = urllib.parse.urlencode(para).encode(encoding='UTF-8')
json_data = urllib.request.urlopen(url_in + 'access_token=' + A_T, postdata).read().decode('UTF-8')
obj = json.loads(json_data, encoding='UTF-8')
if 'error' in obj:
g.msgbox(str(obj), 'Error')
return None
else:
return obj['response']
class Feed:
'''Class Feed
This deals with the feed list.
a_t should be passed in when init.
'''
URL_GET_FEED_LIST = 'https://api.renren.com/v2/feed/list?'
def __init__(self):
self.feed_list = get_json_get(self.URL_GET_FEED_LIST)
if self.feed_list:
self.feed_list = list(self.feed_list)
# If the list is None then do nothing.
# Abandoned.
# if self.feed_list:
# for feed in self.feed_list['response']:
# self.simplist.append(
# {'id': feed['id'], 'time': feed['time'], 'resource_content': feed['resource']['content'],
# 'user': feed['sourceUser']['name'], 'message': feed['message'],
# 'resource_title': feed['resource']['title'], 'resource_id': feed['resource']['id'], 'type': feed['type'], 'user_id': feed['sourceUser']['id'],})
def show_feed_list(self):
for i in range(len(self.feed_list)):
print('{0} {1} {2}\n {3}\n source: {4}\n {5}'.format(i, self.feed_list[i]['time'],
self.feed_list[i]['sourceUser'][
'name'],
self.feed_list[i]['message'],
self.feed_list[i]['resource'][
'title'],
self.feed_list[i]['resource'][
'content']),
end='\n\n\n')
def show_comment(self):
i = int(input('Input id to enter: >'))
c = Comment(self.feed_list[i]['type'],
self.feed_list[i]['sourceUser']['id'],
self.feed_list[i]['resource']['id'])
cl = c.comment_list
print('|{0} {1} {2}\n| {3}\n| source: {4}\n| {5}'.format(i, self.feed_list[i]['time'],
self.feed_list[i]['sourceUser'][
'name'],
self.feed_list[i]['message'],
self.feed_list[i]['resource'][
'title'],
self.feed_list[i]['resource'][
'content']),
end='\n\n\n')
for j in range(len(cl)):
print('{0} {1} {2}\n {3}'.format(j, cl[j]['time'], User.get_user_name(User, cl[j]['authorId']),
cl[j]['content']), end='\n\n')
# ctrl = int(input('Input a positive number to comment: >'))
# if ctrl > 0:
# c.make_comment()
class Comment:
URL_GET_COMMENT_LIST = 'https://api.renren.com/v2/comment/list?'
def __init__(self, commentType, entryOwnerId, entryId):
'''Function __init__
entryId should be resource id.
'''
self.entryOwnerId = entryOwnerId
self.entryId = entryId
if 'SHARE' in commentType:
self.commentType = 'SHARE'
if 'UPDATE' in commentType:
self.commentType = commentType[7:]
para = 'commentType={0}&entryOwnerId={1}&entryId={2}&'.format(self.commentType, self.entryOwnerId, self.entryId)
self.comment_list = get_json_get(self.URL_GET_COMMENT_LIST + para)
if self.comment_list:
self.comment_list = list(self.comment_list)
def make_comment(self, i):
URL_MAKE_COMMENT = 'https://api.renren.com/v2/comment/put?'
# i = int(input('Input id to send to,\nNegative number not to specify: >'))
if i >= 0 and self.comment_list[i]['authorId'] == USER_ID:
g.msgbox('Cannot reply to yourself!', 'Error')
return
text = g.enterbox('Input the content:', 'Comment')
if text:
if i < 0:
para = {'content': text,
'commentType': self.commentType,
'entryOwnerId': self.entryOwnerId,
'entryId': self.entryId}
obj = get_json_post(URL_MAKE_COMMENT, para)
if obj:
g.msgbox('Done.', 'Comment')
else:
para = {'content': text,
'commentType': self.commentType,
'entryOwnerId': self.entryOwnerId,
'entryId': self.entryId,
'targetUserId': self.comment_list[i]['authorId']}
obj = get_json_post(URL_MAKE_COMMENT, para)
if obj:
g.msgbox('Done.', 'Comment')
def status_pub():
text = g.enterbox('Input status:', 'Update Status')
if text:
URL_STATUS_PUB = 'https://api.renren.com/v2/status/put?'
obj = get_json_post(URL_STATUS_PUB, {'content': text})
if obj:
g.msgbox('Done.', 'Update Status')
class User:
'''Class User
This deals with user info.
'''
user_id = 0
user_name = ''
def __init__(self, user_id):
self.user_id = user_id
def get_user_name(self, user_id):
URL_GET_USER_NAME = 'https://api.renren.com/v2/user/get?'
obj = get_json_get(URL_GET_USER_NAME + 'userId={0}'.format(user_id) + '&')
if obj:
self.user_name = obj['name']
return self.user_name
freerr.php
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <?php $code = $_GET["code"]; //!!!!!以下修改三处。client_id后面加上api_key,redirect_uri后加上php文件的url,client_secret后加上secret_key。 $url = "https://graph.renren.com/oauth/token?grant_type=authorization_code&client_id=&redirect_uri=&client_secret=&code=".$code; $json = file_get_contents($url); ?> <textarea name="json"> <?php echo $json; ?> </textarea> </body> </html>