forked from lzjun567/zhihu-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
286 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
## 项目简介 | ||
|
||
Zhihu API For Humans Based on Python3 | ||
|
||
## 前置条件 | ||
|
||
* Python3.x | ||
* Requests2 | ||
* BeautifulSoup4 | ||
|
||
## 安装 | ||
|
||
```python | ||
git@github.com:lzjun567/zhihu-api.git | ||
cd zhihu-api | ||
pip install -r requirement.txt | ||
``` | ||
|
||
## API使用 | ||
|
||
### 获取用户信息: zhihu.user(id) | ||
|
||
获取用户信息时,接收的参数可以是用户的主页地址 profile_url,也可以是用户的个性化名字 user_slug | ||
|
||
``` | ||
>>> from zhihu import Zhihu | ||
>>> zhihu = Zhihu() | ||
>>> zhihu.user(profile_url="https://www.zhihu.com/people/xiaoxiaodouzi") | ||
>>> zhihu.user(user_slug="xiaoxiaodouzi") | ||
{'avatar_url_template': 'https://pic1.zhimg.com/v2-ca13758626bd7367febde704c66249ec_{size}.jpg', 'badge': [], 'name': '我是小号', 'headline': '程序员', 'gender': -1, 'user_type': 'people', 'is_advertiser': False, 'avatar_url': 'https://pic1.zhimg.com/v2-ca13758626bd7367febde704c66249ec_is.jpg', 'url': 'http://www.zhihu.com/api/v4/people/1da75b85900e00adb072e91c56fd9149', 'type': 'people', 'url_token': 'xiaoxiaodouzi', 'id': '1da75b85900e00adb072e91c56fd9149', 'is_org': False} | ||
``` | ||
|
||
### 发私信: zhihu.send_message(id) | ||
|
||
获取用户信息时,接收的参数可以是用户的主页地址 profile_url,也可以是用户的个性化名字 user_slug, 还可以是用户的id | ||
|
||
|
||
```python | ||
>>> zhihu.send_message("你好,问候1", profile_url="https://www.zhihu.com/people/xiaoxiaodouzi") | ||
{'sender': {'avatar_url_template': 'https://pic3.zhimg.com/ca6bf4bb82d9cf152b618edcbda01606_{size}.jpg', 'badge': [], 'name': 'zhijun liu', 'is_advertiser': False, 'url': 'http://www.zhihu.com/api/v4/people/ba286d02fd9701a446d68ba85d0755c0', 'url_token': 'zhijun-liu', 'user_type': 'people', 'headline': '公众号:Python之禅', 'avatar_url': 'https://pic3.zhimg.com/ca6bf4bb82d9cf152b618edcbda01606_is.jpg', 'is_org': False, 'gender': 1, 'type': 'people', 'id': 'ba286d02fd9701a446d68ba85d0755c0'}, 'url': '', 'has_read': False, 'content': '你好,问候1', 'receiver': {'avatar_url_template': 'https://pic1.zhimg.com/v2-ca13758626bd7367febde704c66249ec_{size}.jpg', 'badge': [], 'name': '我是小号', 'is_advertiser': False, 'url': 'http://www.zhihu.com/api/v4/people/1da75b85900e00adb072e91c56fd9149', 'url_token': 'xiaoxiaodouzi', 'user_type': 'people', 'headline': '程序员', 'avatar_url': 'https://pic1.zhimg.com/v2-ca13758626bd7367febde704c66249ec_is.jpg', 'is_org': False, 'gender': -1, 'type': 'people', 'id': '1da75b85900e00adb072e91c56fd9149'}, 'created_time': 1492318772, 'type': 'message', 'id': '2143862420'} | ||
|
||
>>> zhihu.send_message("你好,问候2", user_slug="xiaoxiaodouzi") | ||
{'sender': {'avatar_url_template': 'https://pic3.zhimg.com/ca6bf4bb82d9cf152b618edcbda01606_{size}.jpg', 'name': 'zhijun liu', 'is_advertiser': False, 'url': 'http://www.zhihu.com/api/v4/people/ba286d02fd9701a446d68ba85d0755c0', 'badge': [], 'user_type': 'people', 'url_token': 'zhijun-liu', 'headline': '公众号:Python之禅', 'avatar_url': 'https://pic3.zhimg.com/ca6bf4bb82d9cf152b618edcbda01606_is.jpg', 'is_org': False, 'gender': 1, 'type': 'people', 'id': 'ba286d02fd9701a446d68ba85d0755c0'}, 'url': '', 'has_read': False, 'content': '你好,问候2', 'receiver': {'avatar_url_template': 'https://pic1.zhimg.com/v2-ca13758626bd7367febde704c66249ec_{size}.jpg', 'name': '我是小号', 'is_advertiser': False, 'url': 'http://www.zhihu.com/api/v4/people/1da75b85900e00adb072e91c56fd9149', 'badge': [], 'user_type': 'people', 'url_token': 'xiaoxiaodouzi', 'headline': '程序员', 'avatar_url': 'https://pic1.zhimg.com/v2-ca13758626bd7367febde704c66249ec_is.jpg', 'is_org': False, 'gender': -1, 'type': 'people', 'id': '1da75b85900e00adb072e91c56fd9149'}, 'created_time': 1492318780, 'type': 'message', 'id': '6343862420'} | ||
|
||
>>> zhihu.send_message("你好,问候3", user_id="1da75b85900e00adb072e91c56fd9149") | ||
{'sender': {'avatar_url_template': 'https://pic3.zhimg.com/ca6bf4bb82d9cf152b618edcbda01606_{size}.jpg', 'name': 'zhijun liu', 'is_advertiser': False, 'url': 'http://www.zhihu.com/api/v4/people/ba286d02fd9701a446d68ba85d0755c0', 'badge': [], 'user_type': 'people', 'url_token': 'zhijun-liu', 'headline': '公众号:Python之禅', 'avatar_url': 'https://pic3.zhimg.com/ca6bf4bb82d9cf152b618edcbda01606_is.jpg', 'is_org': False, 'gender': 1, 'type': 'people', 'id': 'ba286d02fd9701a446d68ba85d0755c0'}, 'url': '', 'has_read': False, 'content': '你好,问候3', 'receiver': {'avatar_url_template': 'https://pic1.zhimg.com/v2-ca13758626bd7367febde704c66249ec_{size}.jpg', 'name': '我是小号', 'is_advertiser': False, 'url': 'http://www.zhihu.com/api/v4/people/1da75b85900e00adb072e91c56fd9149', 'badge': [], 'user_type': 'people', 'url_token': 'xiaoxiaodouzi', 'headline': '程序员', 'avatar_url': 'https://pic1.zhimg.com/v2-ca13758626bd7367febde704c66249ec_is.jpg', 'is_org': False, 'gender': -1, 'type': 'people', 'id': '1da75b85900e00adb072e91c56fd9149'}, 'created_time': 1492318799, 'type': 'message', 'id': '3244862420'} | ||
>>> | ||
``` | ||
|
||
## TODO | ||
|
||
* 关注 | ||
* 文章点赞 | ||
* ... | ||
|
||
## 贡献者 | ||
欢迎 PR, 所有贡献者都将出现在这里 | ||
|
||
## 交流 | ||
|
||
![公众号](gongzhonghao.png) | ||
|
||
![群](weixingroup.jpeg) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# encoding: utf-8 | ||
__author__ = 'liuzhijun' |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# encoding: utf-8 | ||
__author__ = 'liuzhijun' | ||
import time | ||
from zhihu import Zhihu | ||
|
||
if __name__ == '__main__': | ||
zhihu = Zhihu() | ||
profile = zhihu.user(profile_url="https://www.zhihu.com/people/xiaoxiaodouzi") | ||
time.sleep(1) | ||
profile = zhihu.user(user_slug="xiaoxiaodouzi") | ||
time.sleep(1) | ||
_id = profile.get("id") | ||
zhihu.send_message("你好,问候1", profile_url="https://www.zhihu.com/people/xiaoxiaodouzi") | ||
time.sleep(1) | ||
zhihu.send_message("你好,问候2", user_slug="xiaoxiaodouzi") | ||
time.sleep(1) | ||
zhihu.send_message("你好,问候3", user_id=_id) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
requests==2.13.0 | ||
beautifulsoup4==4.5.3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# encoding: utf-8 | ||
|
||
""" | ||
通用URL类,知乎官方URL接口地址 | ||
""" | ||
|
||
|
||
class URL(object): | ||
# 登录 | ||
@staticmethod | ||
def login(): | ||
return "https://www.zhihu.com/login/email" | ||
|
||
# 私信 | ||
@staticmethod | ||
def message(): | ||
return "https://www.zhihu.com/api/v4/messages" | ||
|
||
# 验证码 | ||
@staticmethod | ||
def captcha(timestamp): | ||
return 'https://www.zhihu.com/captcha.gif?r={timestamp}&type=login' | ||
|
||
# 首页 | ||
@staticmethod | ||
def index(): | ||
return "https://www.zhihu.com" | ||
|
||
# 用户信息 | ||
@staticmethod | ||
def profile(url_token): | ||
return "https://www.zhihu.com/api/v4/members/{url_token}".format(url_token=url_token) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
# encoding: utf-8 | ||
# !/usr/bin/env python | ||
|
||
""" | ||
知乎API | ||
""" | ||
import logging | ||
import os | ||
import re | ||
import time | ||
from http import cookiejar | ||
|
||
import requests | ||
from bs4 import BeautifulSoup | ||
from url import URL | ||
|
||
logging.basicConfig(level=logging.INFO) | ||
|
||
# cookies 文件保存在当前用户目录, | ||
# 下次程序重新启动时,直接加载cookie文件,无需再输入用户名和密码登录 | ||
cookie_filename = os.path.join(os.path.expanduser('~'), "cookies.txt") | ||
|
||
|
||
def need_login(func): | ||
""" | ||
用户认证(判断用户是否已经登录的装饰器) | ||
""" | ||
|
||
def wrapper(self, *args, **kwargs): | ||
success = True | ||
if not os.path.exists(cookie_filename): | ||
success = False | ||
while not success: | ||
email = input("请输入email:") | ||
password = input("请输入密码:") | ||
success = self.login(email, password) | ||
if success: | ||
result = func(self, *args, **kwargs) | ||
return result | ||
|
||
return wrapper | ||
|
||
|
||
class Zhihu(object): | ||
logger = logging.getLogger(__name__) | ||
|
||
def __init__(self): | ||
self._session = requests.session() | ||
self._session.verify = False | ||
self._session.headers = {"Host": "www.zhihu.com", | ||
"Referer": "https://www.zhihu.com/", | ||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36' | ||
' (KHTML, like Gecko) Chrome/56.0.2924.87', | ||
} | ||
self._session.cookies = cookiejar.LWPCookieJar(filename=cookie_filename) | ||
try: | ||
self._session.cookies.load(ignore_discard=True) | ||
except: | ||
pass | ||
|
||
def _get_captcha(self): | ||
t = str(int(time.time() * 1000)) | ||
r = self._session.get(URL.captcha(t)) | ||
with open('captcha.jpg', 'wb') as f: | ||
f.write(r.content) | ||
captcha = input("验证码:") | ||
return captcha | ||
|
||
def _get_xsrf(self): | ||
response = self._session.get(URL.index()) | ||
soup = BeautifulSoup(response.content, "html.parser") | ||
xsrf = soup.find('input', attrs={"name": "_xsrf"}).get("value") | ||
return xsrf | ||
|
||
def login(self, email, password): | ||
""" | ||
登录需要的验证码会保存在当前目录,需要用户自己识别,并输入 | ||
""" | ||
request_body = {'email': email, | ||
'password': password, | ||
'_xsrf': self._get_xsrf(), | ||
"captcha": self._get_captcha(), | ||
'remember_me': 'true'} | ||
|
||
response = self._session.post(URL.login(), data=request_body) | ||
if response.ok: | ||
data = response.json() | ||
if data.get("r") == 0: | ||
# 登录成功' | ||
self._session.cookies.save() | ||
self.logger.info("登录成功") | ||
return True | ||
else: | ||
self.logger.info("登录失败, %s" % data.get("msg")) | ||
|
||
else: | ||
self.logger.error(response.content) | ||
return False | ||
|
||
@need_login | ||
def send_message(self, content, user_id=None, profile_url=None, user_slug=None): | ||
""" | ||
给指定的用户发私信 | ||
:param content 私信内容 | ||
:param user_id 用户id | ||
:param profile_url :用户主页地址 | ||
:param user_slug : 用户的个性域名 | ||
>>> send_message(profile_url = "https://www.zhihu.com/people/xiaoxiaodouzi") | ||
>>> send_message(user_slug = "xiaoxiaodouzi") | ||
>>> send_message(user_id = "1da75b85900e00adb072e91c56fd9149") | ||
""" | ||
|
||
if not any([user_id, profile_url, user_slug]): | ||
raise Exception("至少指定一个关键字参数") | ||
|
||
if not user_id and user_slug: | ||
profile = self.user(user_slug) | ||
user_id = profile.get("id") | ||
elif not user_id and profile_url: | ||
pattern = re.compile("https?://www.zhihu.com/people/([\w-]+)") | ||
match = pattern.search(profile_url) | ||
if match: | ||
user_slug = match.group(1) | ||
profile = self.user(user_slug) | ||
user_id = profile.get("id") | ||
|
||
data = {"type": "common", "content": content, "receiver_hash": user_id} | ||
response = self._session.post(URL.message(), json=data) | ||
data = response.json() | ||
if data.get("error"): | ||
self.logger.info("私信发送失败, %s" % data.get("error").get("message")) | ||
else: | ||
self.logger.info("发送成功") | ||
return data | ||
|
||
@need_login | ||
def user(self, user_slug=None, profile_url=None): | ||
""" | ||
获取用户信息 | ||
:param user_slug : 用户的个性域名 | ||
:param profile_url: 用户主页地址 | ||
:return:dict | ||
>>> user(profile_url = "https://www.zhihu.com/people/xiaoxiaodouzi") | ||
>>> user(user_slug = "xiaoxiaodouzi") | ||
""" | ||
|
||
if not any([profile_url, user_slug]): | ||
raise Exception("至少指定一个关键字参数") | ||
|
||
if not user_slug and profile_url: | ||
|
||
pattern = re.compile("https?://www.zhihu.com/people/([\w-]+)") | ||
match = pattern.search(profile_url) | ||
if match: | ||
user_slug = match.group(1) | ||
response = self._session.get(URL.profile(user_slug)) | ||
if response.ok: | ||
return response.json() | ||
else: | ||
self.logger.error(u"获取用户信息失败, status code: %s" % response.status_code) |