From c86c9f627b8288a800f16d5ee37afbca7ca84174 Mon Sep 17 00:00:00 2001 From: liugenxian Date: Mon, 24 Apr 2017 22:20:28 +0800 Subject: [PATCH 01/17] 2017.04.24 --- zhihu/models/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/zhihu/models/__init__.py b/zhihu/models/__init__.py index c2f1610..1e02167 100644 --- a/zhihu/models/__init__.py +++ b/zhihu/models/__init__.py @@ -6,7 +6,11 @@ import logging import re import time -from http import cookiejar + +try: + from http import cookiejar # py3 +except: + import cookielib as cookiejar # py2 import requests import requests.packages.urllib3 as urllib3 @@ -14,8 +18,7 @@ from ..error import ZhihuError from ..url import URL -from zhihu.settings import COOKIES -from zhihu.settings import HEADERS +from zhihu import settings urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) @@ -24,8 +27,8 @@ class Model(object): def __init__(self): self._session = requests.Session() self._session.verify = False - self._session.headers = HEADERS - self._session.cookies = cookiejar.LWPCookieJar(filename=COOKIES) + self._session.headers = settings.HEADERS + self._session.cookies = cookiejar.LWPCookieJar(filename=settings.COOKIES_FILE) try: self._session.cookies.load(ignore_discard=True) except: From 42871e1688745e70d0e6d38b7e0dc0f563f872ee Mon Sep 17 00:00:00 2001 From: Pan Date: Mon, 24 Apr 2017 01:50:34 +0800 Subject: [PATCH 02/17] add unfollow function --- zhihu/models/common.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/zhihu/models/common.py b/zhihu/models/common.py index 76ed2d3..4a83af1 100644 --- a/zhihu/models/common.py +++ b/zhihu/models/common.py @@ -83,3 +83,24 @@ def follow(self, user_slug=None, profile_url=None, **kwargs): return response.json() else: self.logger.error(u"关注失败, status code: %s" % response.status_code) + + @need_login + def unfollow(self, user_slug=None, profile_url=None, **kwargs): + """ + 取消关注用户 + :param user_slug: + :param profile_url: + :return: {"follower_count": int} + + >>> unfollow(profile_url = "https://www.zhihu.com/people/xiaoxiaodouzi") + >>> unfollow(user_slug = "xiaoxiaodouzi") + """ + if not any([profile_url, user_slug]): + raise ZhihuError("至少指定一个关键字参数") + + user_slug = self._user_slug(profile_url) if user_slug is None else user_slug + response = self._session.delete(URL.follow(user_slug), **kwargs) + if response.ok: + return response.json() + else: + self.logger.error(u"取消关注失败, status code: %s" % response.status_code) From 4d4eb859e51cd8707172704798ed573d9d703606 Mon Sep 17 00:00:00 2001 From: Pan Date: Mon, 24 Apr 2017 01:54:19 +0800 Subject: [PATCH 03/17] update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d735b4c..9b817f9 100644 --- a/README.md +++ b/README.md @@ -70,13 +70,16 @@ pip install git+git://github.com/lzjun567/zhihu-api --upgrade >>> zhihu.zhihu.send_message("你好,问候1", profile_url="https://www.zhihu.com/people/xiaoxiaodouzi") ``` -### 关注用户 +### 关注/取消关注用户 ``` >>> zhihu.follow(profile_url="https://www.zhihu.com/people/gao-yu-dong-41") {"follower_count": 6} >>> zhihu.follow(user_slug="xiaoxiaodouzi") {"follower_count": 6} + +>>> zhihu.unfollow(user_slug="xiaoxiaodouzi") +{'follower_count': 5} ``` ### 点赞 From 122d425982e0df735e0cabbaddbcdf0ecf3b56da Mon Sep 17 00:00:00 2001 From: Pan Date: Tue, 25 Apr 2017 09:08:37 +0800 Subject: [PATCH 04/17] add test cases for follow and unfollow --- test/common.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/common.py b/test/common.py index ca9dbd3..3f62bb4 100644 --- a/test/common.py +++ b/test/common.py @@ -3,6 +3,7 @@ from zhihu import Zhihu from zhihu.error import ZhihuError +import time class CommonTestCase(unittest.TestCase): @@ -13,3 +14,61 @@ def test_send_message_with_no_kwargs(self): """ zhihu = Zhihu() self.assertRaises(ZhihuError, zhihu.send_message, "hello") + + def test_follow_with_no_kwargs(self): + """ + 不带关键字参数,抛出异常 + :return: + """ + time.sleep(1) + zhihu = Zhihu() + self.assertRaises(ZhihuError, zhihu.follow) + + def test_follow_with_url(self): + """ + 带url,返回follower数量 + :return: + """ + time.sleep(1) + zhihu = Zhihu() + response = zhihu.follow(profile_url="https://www.zhihu.com/people/gao-yu-dong-41") + self.assertIn(response, 'follower_count') + + def test_follow_with_user_slug(self): + """ + 带user_slug,返回follower数量 + :return: + """ + time.sleep(1) + zhihu = Zhihu() + response = zhihu.follow(user_slug="xiaoxiaodouzi") + self.assertIn(response, 'follower_count') + + def test_follow_with_no_kwargs(self): + """ + 不带关键字参数,抛出异常 + :return: + """ + time.sleep(1) + zhihu = Zhihu() + self.assertRaises(ZhihuError, zhihu.unfollow) + + def test_unfollow_with_url(self): + """ + 带url,返回follower数量 + :return: + """ + time.sleep(1) + zhihu = Zhihu() + response = zhihu.unfollow(profile_url="https://www.zhihu.com/people/gao-yu-dong-41") + self.assertIn(response, 'follower_count') + + def test_unfollow_with_user_slug(self): + """ + 带user_slug,返回follower数量 + :return: + """ + time.sleep(1) + zhihu = Zhihu() + response = zhihu.unfollow(user_slug="xiaoxiaodouzi") + self.assertIn(response, 'follower_count') From 6903db160f0f310aad1e96e384b94f728c8ae76f Mon Sep 17 00:00:00 2001 From: lzjun Date: Fri, 28 Apr 2017 13:08:01 +0800 Subject: [PATCH 05/17] add zhuanlan host --- README.md | 32 ++++++++++++++++++-------------- test/column.py | 16 +++++++--------- zhihu/main.py | 9 +++++++++ zhihu/models/__init__.py | 8 ++++++-- zhihu/models/column.py | 12 ++++++------ zhihu/settings.py | 11 ++++++++--- zhihu/url.py | 1 - 7 files changed, 54 insertions(+), 35 deletions(-) create mode 100644 zhihu/main.py diff --git a/README.md b/README.md index d735b4c..a679137 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ -## 项目简介 - ### 目标 试图构建一个更加简洁、优雅的、Pythonic 的知乎 API。 ### 使用场景 -* 如果你想自动给你喜欢的人点赞 +* 如果你想基于知乎社区做数据分析 +* 如果你想通过程序自动给回答点赞 * 如果你想批量关注用户 * 如果你想批量发送信息 * 如果你想构建一个自己的知乎客户端 @@ -22,14 +21,9 @@ ## 安装 ```python -pip install git+git://github.com/lzjun567/zhihu-api - -# 升级到最新的代码,附加参数 --upgrade -# 推荐 pip install git+git://github.com/lzjun567/zhihu-api --upgrade ``` - ## API ### 用户个人公开信息 @@ -85,8 +79,6 @@ pip install git+git://github.com/lzjun567/zhihu-api --upgrade >>> data = Answer(id=14005147).vote_up() >>> data >>> {"voting": 1, "voteup_count": 314} - ->>> data = Answer(url="https://www.zhihu.com/question/19761434/answer/14005147").vote_up() ``` ### 反对 @@ -95,12 +87,24 @@ vote_down ### 中立 vote_neutral +### 专栏关注列表 +column.followers +``` +>>> from zhihu import Column +>>> column = Column(url="https://zhuanlan.zhihu.com/pythoneer") +>>> column.followers(limit=2, offset=1) +[{u'bio': u'python', u'hash': u'463e2651f6a856d88c33bfb7fd673bf4', u'description': u'', u'isOrg': False, + u'name': u'zpf1024', u'profileUrl': u'https://www.zhihu.com/people/zpf1024', + u'avatar': {u'id': u'da8e974dc', u'template': u'https://pic1.zhimg.com/{id}_{size}.jpg'}, + u'isOrgWhiteList': False, u'slug': u'zpf1024', u'uid': 841267452498296832L}, + {u'bio': None, u'hash': u'45bbaa0aca55fec0d768ccb4845a1c53', u'description': u'', u'isOrg': False, + u'name': u'keyoka', u'profileUrl': u'https://www.zhihu.com/people/yi-hu-84', + u'avatar': {u'id': u'785bfd914', u'template': u'https://pic1.zhimg.com/{id}_{size}.jpg'}, + u'isOrgWhiteList': False, u'slug': u'yi-hu-84', u'uid': 43738302775296L}] +``` +每个接口都提供了不只一种方式调用,更多参考单元测试里面的例子 -## TODO - -* 文章点赞 -* ... ## 贡献者 欢迎 PR, 所有贡献者都将出现在这里,排名部分先后 diff --git a/test/column.py b/test/column.py index 7e2c6ed..bb42f80 100644 --- a/test/column.py +++ b/test/column.py @@ -1,19 +1,17 @@ # encoding: utf-8 -from zhihu import Column import unittest -import time + +from zhihu import Column class ColumnTestCase(unittest.TestCase): - def test_followers_with_slug(self): slug = "pythoneer" - from zhihu import Zhihu - data = Column(slug=slug).followers(limit=10, headers=Zhihu().) + data = Column(slug=slug).followers(limit=10) self.assertEqual(10, len(data)) - - -# https://zhuanlan.zhihu.com/api/columns/pythoneer/followers -# https://zhuanlan.zhihu.com/api/columns/pythoneer/followers \ No newline at end of file + def test_followers_with_url(self): + url = "https://zhuanlan.zhihu.com/pythoneer" + data = Column(url=url).followers(limit=10) + self.assertEqual(10, len(data)) diff --git a/zhihu/main.py b/zhihu/main.py new file mode 100644 index 0000000..49ad21d --- /dev/null +++ b/zhihu/main.py @@ -0,0 +1,9 @@ +from zhihu import Answer + + +def vote_up_with_id(): + data = Answer(id=14005147).vote_up() + print(data) + +if __name__ == '__main__': + vote_up_with_id() \ No newline at end of file diff --git a/zhihu/models/__init__.py b/zhihu/models/__init__.py index b76964d..22e8790 100644 --- a/zhihu/models/__init__.py +++ b/zhihu/models/__init__.py @@ -1,9 +1,11 @@ # encoding: utf-8 import logging +import os +import platform import re +import subprocess import time -import platform, os, subprocess try: from http import cookiejar # py3 @@ -22,11 +24,13 @@ class Model(object): - def __init__(self): + def __init__(self, **kwargs): self._session = requests.Session() self._session.verify = False self._session.headers = settings.HEADERS self._session.cookies = cookiejar.LWPCookieJar(filename=settings.COOKIES_FILE) + for k, v in kwargs.items(): + setattr(self._session, k, v) try: self._session.cookies.load(ignore_discard=True) except: diff --git a/zhihu/models/column.py b/zhihu/models/column.py index f4ddd63..c10be8a 100644 --- a/zhihu/models/column.py +++ b/zhihu/models/column.py @@ -1,8 +1,10 @@ # encoding: utf-8 -from zhihu.models import Model -from zhihu.error import ZhihuError import re + +from zhihu.error import ZhihuError +from zhihu.models import Model +from zhihu.settings import ZHUANLAN_HEADERS from zhihu.url import URL @@ -16,7 +18,7 @@ def __init__(self, slug=None, url=None): if not slug: raise ZhihuError("没有指定专栏的的slug或者url") self.slug = slug - super(Column, self).__init__() + super(Column, self).__init__(headers=ZHUANLAN_HEADERS) @staticmethod def _extract_slug(url): @@ -32,9 +34,7 @@ def followers(self, limit=500, offset=0, **kwargs): 用户关注列表 :param limit: 最大获取条数,不能超过500条 :param offset: 偏移量 - :return: + :return: 返回用户列表 """ r = self._session.get(URL.column_followers(self.slug), params={"limit": limit, "offset": offset}, **kwargs) - print(r.url) - print(r.text) return r.json() diff --git a/zhihu/settings.py b/zhihu/settings.py index 6daddaa..59ea0b8 100644 --- a/zhihu/settings.py +++ b/zhihu/settings.py @@ -1,11 +1,16 @@ # encoding: utf-8 import os.path +USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87' + 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', + 'User-Agent': USER_AGENT } +ZHUANLAN_HEADERS = {"Host": "zhuanlan.zhihu.com", + "Referer": "https://zhuanlan.zhihu.com/", + 'User-Agent': USER_AGENT + } + COOKIES_FILE = os.path.join(os.path.expanduser('~'), "cookies.txt") diff --git a/zhihu/url.py b/zhihu/url.py index cd50ec8..5d0c03b 100644 --- a/zhihu/url.py +++ b/zhihu/url.py @@ -69,5 +69,4 @@ def column(slug): @staticmethod def column_followers(slug): a = URL.zhuanlan_host + "/api/columns/{slug}/followers".format(slug=slug) - print(a) return a From c55725472bfc8ee72a7b321a670a17ec965eed75 Mon Sep 17 00:00:00 2001 From: Oopswc <706543191@qq.com> Date: Fri, 28 Apr 2017 21:25:57 +0800 Subject: [PATCH 06/17] add UserAgent list for random selection --- zhihu/settings.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/zhihu/settings.py b/zhihu/settings.py index 59ea0b8..23bc277 100644 --- a/zhihu/settings.py +++ b/zhihu/settings.py @@ -1,7 +1,52 @@ # encoding: utf-8 import os.path +import random -USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87' +#UserAgent list +user_agent_list = [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87" + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 " + "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", + "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 " + "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 " + "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", + "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 " + "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", + "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 " + "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 " + "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", + "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 " + "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", + "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 " + "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", + "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 " + "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24" + ] + +#Select UA by random +USER_AGENT = random.choice(user_agent_list) + +print(USER_AGENT) HEADERS = {"Host": "www.zhihu.com", "Referer": "https://www.zhihu.com/", From 91225b4a67b8edf28b5c6e5f44833041f12f563d Mon Sep 17 00:00:00 2001 From: Oopswc <706543191@qq.com> Date: Fri, 28 Apr 2017 21:35:13 +0800 Subject: [PATCH 07/17] Update settings.py --- zhihu/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zhihu/settings.py b/zhihu/settings.py index 23bc277..7bd2e49 100644 --- a/zhihu/settings.py +++ b/zhihu/settings.py @@ -4,7 +4,8 @@ #UserAgent list user_agent_list = [ - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87" + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/56.0.2924.87", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 " From f90e37bcbd6379b15fc9498719ad6a133ee16ac5 Mon Sep 17 00:00:00 2001 From: liuzhijun Date: Sat, 29 Apr 2017 00:13:38 +0800 Subject: [PATCH 08/17] update readme --- README.md | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index a679137..777d496 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ pip install git+git://github.com/lzjun567/zhihu-api --upgrade ``` >>> from zhihu.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': [], @@ -45,34 +45,22 @@ pip install git+git://github.com/lzjun567/zhihu-api --upgrade 'id': '1da75b85900e00adb072e91c56fd9149', 'is_org': False} -# 还可以用 ->>> zhihu.user(user_slug="xiaoxiaodouzi") - ``` ### 私信发送 - - ```python ->>> zhihu.send_message("你好,问候3", user_id="1da75b85900e00adb072e91c56fd9149") - -# 还支持 user_slug >>> zhihu.send_message("你好,问候2", user_slug="xiaoxiaodouzi") - -# 还支持 profile_url ->>> zhihu.zhihu.send_message("你好,问候1", profile_url="https://www.zhihu.com/people/xiaoxiaodouzi") ``` ### 关注用户 ``` ->>> zhihu.follow(profile_url="https://www.zhihu.com/people/gao-yu-dong-41") -{"follower_count": 6} - >>> zhihu.follow(user_slug="xiaoxiaodouzi") {"follower_count": 6} ``` +## 回答 + ### 点赞 ``` >>> from zhihu import Answer @@ -88,7 +76,6 @@ vote_down vote_neutral ### 专栏关注列表 -column.followers ``` >>> from zhihu import Column >>> column = Column(url="https://zhuanlan.zhihu.com/pythoneer") @@ -111,8 +98,9 @@ column.followers * [@BigBorg](https://github.com/BigBorg) * [@xiaowenlong100](https://github.com/xiaowenlong100) -* [chenghengchao](https://github.com/chenghengchao) -* [MaxPoon](https://github.com/MaxPoon) +* [@chenghengchao](https://github.com/chenghengchao) +* [@MaxPoon](https://github.com/MaxPoon) +* [@Oopswc](https://github.com/Oopswc) ## 交流 群已经加不进,可以先加微信:lzjun567 拉你入群 From b28a7df7ed06676e9e2779c944b476eb7969c7df Mon Sep 17 00:00:00 2001 From: liuzhijun Date: Sat, 29 Apr 2017 00:21:15 +0800 Subject: [PATCH 09/17] fix test --- test/common.py | 16 ++++---- zhihu/models/common.py | 4 +- zhihu/settings.py | 83 ++++++++++++++++++++---------------------- 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/test/common.py b/test/common.py index 3f62bb4..20cf2e7 100644 --- a/test/common.py +++ b/test/common.py @@ -31,8 +31,8 @@ def test_follow_with_url(self): """ time.sleep(1) zhihu = Zhihu() - response = zhihu.follow(profile_url="https://www.zhihu.com/people/gao-yu-dong-41") - self.assertIn(response, 'follower_count') + data = zhihu.follow(profile_url="https://www.zhihu.com/people/gao-yu-dong-41") + self.assertIn('follower_count', data) def test_follow_with_user_slug(self): """ @@ -41,8 +41,8 @@ def test_follow_with_user_slug(self): """ time.sleep(1) zhihu = Zhihu() - response = zhihu.follow(user_slug="xiaoxiaodouzi") - self.assertIn(response, 'follower_count') + data = zhihu.follow(user_slug="xiaoxiaodouzi") + self.assertIn('follower_count', data) def test_follow_with_no_kwargs(self): """ @@ -60,8 +60,8 @@ def test_unfollow_with_url(self): """ time.sleep(1) zhihu = Zhihu() - response = zhihu.unfollow(profile_url="https://www.zhihu.com/people/gao-yu-dong-41") - self.assertIn(response, 'follower_count') + data = zhihu.unfollow(profile_url="https://www.zhihu.com/people/gao-yu-dong-41") + self.assertIn('follower_count', data) def test_unfollow_with_user_slug(self): """ @@ -70,5 +70,5 @@ def test_unfollow_with_user_slug(self): """ time.sleep(1) zhihu = Zhihu() - response = zhihu.unfollow(user_slug="xiaoxiaodouzi") - self.assertIn(response, 'follower_count') + data = zhihu.unfollow(user_slug="xiaoxiaodouzi") + self.assertIn('follower_count', data) diff --git a/zhihu/models/common.py b/zhihu/models/common.py index 4a83af1..4c19420 100644 --- a/zhihu/models/common.py +++ b/zhihu/models/common.py @@ -34,10 +34,10 @@ def send_message(self, content, user_id=None, profile_url=None, user_slug=None, data = {"type": "common", "content": content, "receiver_hash": user_id} response = self._session.post(URL.message(), json=data, **kwargs) if response.ok: + response.json() self.log("发送成功") else: self.log("发送失败") - return response.text @need_login def user(self, user_slug=None, profile_url=None, **kwargs): @@ -99,7 +99,7 @@ def unfollow(self, user_slug=None, profile_url=None, **kwargs): raise ZhihuError("至少指定一个关键字参数") user_slug = self._user_slug(profile_url) if user_slug is None else user_slug - response = self._session.delete(URL.follow(user_slug), **kwargs) + response = self._session.delete(URL.follow_people(user_slug), **kwargs) if response.ok: return response.json() else: diff --git a/zhihu/settings.py b/zhihu/settings.py index 7bd2e49..f449a37 100644 --- a/zhihu/settings.py +++ b/zhihu/settings.py @@ -2,53 +2,50 @@ import os.path import random -#UserAgent list +# UserAgent list user_agent_list = [ - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 " - "(KHTML, like Gecko) Chrome/56.0.2924.87", - "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 " - "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", - "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 " - "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", - "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 " - "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", - "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 " - "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", - "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 " - "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 " - "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", - "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 " - "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", - "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " - "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", - "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 " - "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 " - "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", - "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " - "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", - "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " - "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", - "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " - "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", - "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " - "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", - "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 " - "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", - "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " - "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 " - "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", - "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 " - "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24" - ] + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/56.0.2924.87", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 " + "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", + "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 " + "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 " + "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", + "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 " + "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", + "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 " + "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 " + "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", + "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 " + "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", + "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", + "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " + "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 " + "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", + "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 " + "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24" +] -#Select UA by random USER_AGENT = random.choice(user_agent_list) -print(USER_AGENT) - HEADERS = {"Host": "www.zhihu.com", "Referer": "https://www.zhihu.com/", 'User-Agent': USER_AGENT From ecf1df466fc00a98ce48ac0094b6926bb44746ee Mon Sep 17 00:00:00 2001 From: liuzhijun Date: Sat, 29 Apr 2017 02:39:47 +0800 Subject: [PATCH 10/17] update readme --- README.md | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index ce2630a..7cacaee 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ pip install git+git://github.com/lzjun567/zhihu-api --upgrade ``` -## API +### API -### 用户个人公开信息 +**个人信息** ``` >>> from zhihu.zhihu import Zhihu >>> zhihu = Zhihu() @@ -47,24 +47,23 @@ pip install git+git://github.com/lzjun567/zhihu-api --upgrade ``` -### 私信发送 +**私信发送** ```python >>> zhihu.send_message("你好,问候2", user_slug="xiaoxiaodouzi") ``` -### 关注/取消关注用户 +**关注用户** ``` >>> zhihu.follow(user_slug="xiaoxiaodouzi") {"follower_count": 6} - +``` +**取消关注** >>> zhihu.unfollow(user_slug="xiaoxiaodouzi") {'follower_count': 5} ``` -## 回答 - -### 点赞 +**点赞回答** ``` >>> from zhihu import Answer >>> data = Answer(id=14005147).vote_up() @@ -72,13 +71,25 @@ pip install git+git://github.com/lzjun567/zhihu-api --upgrade >>> {"voting": 1, "voteup_count": 314} ``` -### 反对 -vote_down +**反对** +``` +>>> from zhihu import Answer +>>> data = Answer(id=14005147).vote_down() +>>> data +>>> {"voting": 1, "voteup_count": 314} +``` + + +**中立** +``` +>>> from zhihu import Answer +>>> data = Answer(id=14005147).vote_neutral() +>>> data +>>> {"voting": 1, "voteup_count": 314} +``` -### 中立 -vote_neutral -### 专栏关注列表 +**专栏的关注列表** ``` >>> from zhihu import Column >>> column = Column(url="https://zhuanlan.zhihu.com/pythoneer") From 368664eca9d0df58e66f71a474a23bc1d5de0a79 Mon Sep 17 00:00:00 2001 From: liuzhijun Date: Sat, 29 Apr 2017 02:40:57 +0800 Subject: [PATCH 11/17] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7cacaee..39dc9bf 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ pip install git+git://github.com/lzjun567/zhihu-api --upgrade {"follower_count": 6} ``` **取消关注** +``` >>> zhihu.unfollow(user_slug="xiaoxiaodouzi") {'follower_count': 5} ``` From bd7dbbb104de236c0040f05a3311c4a3a0c8ddf8 Mon Sep 17 00:00:00 2001 From: liuzhijun Date: Mon, 1 May 2017 11:41:31 +0800 Subject: [PATCH 12/17] refactor login --- doc.md | 14 +++++++++ test/answer.py | 5 ++-- test/common.py | 1 + test/question.py | 14 ++++----- zhihu/__init__.py | 2 ++ zhihu/auth.py | 12 +++++--- zhihu/main.py | 14 ++++++++- zhihu/models/__init__.py | 62 ++++++++++++++++------------------------ zhihu/models/account.py | 62 ++++++++++++++++++++++++++++++++++++++++ zhihu/models/answer.py | 20 +++++++++---- zhihu/models/question.py | 10 +++++-- zhihu/url.py | 9 ++++-- 12 files changed, 162 insertions(+), 63 deletions(-) create mode 100644 doc.md create mode 100644 zhihu/models/account.py diff --git a/doc.md b/doc.md new file mode 100644 index 0000000..4a4f592 --- /dev/null +++ b/doc.md @@ -0,0 +1,14 @@ + +### Account +账户类 + +**登录** + +``` + +>>> from zhihu import Account +>>> account = Account() +>>> account.login("12334@qq.com", "xxxxxx") +>>> + +``` \ No newline at end of file diff --git a/test/answer.py b/test/answer.py index b7b0a06..d331c42 100644 --- a/test/answer.py +++ b/test/answer.py @@ -34,11 +34,10 @@ def test_vote_neutral_with_url(self): def test_thank_with_url(self): time.sleep(1) data = Answer(url="https://www.zhihu.com/question/19761434/answer/14005147").thank() - self.assertIn("is_thanked", data) - self.assertIn("true", data) + self.assertEqual({"is_thanked": True}, data) def test_thank_cancel_with_url(self): time.sleep(1) data = Answer(url="https://www.zhihu.com/question/19761434/answer/14005147").thank_cancel() self.assertIn("is_thanked", data) - self.assertIn("false", data) + self.assertEqual({"is_thanked": False}, data) diff --git a/test/common.py b/test/common.py index 20cf2e7..58d45f2 100644 --- a/test/common.py +++ b/test/common.py @@ -4,6 +4,7 @@ from zhihu import Zhihu from zhihu.error import ZhihuError import time +from zhihu.models import Model class CommonTestCase(unittest.TestCase): diff --git a/test/question.py b/test/question.py index b1259b3..0d8edf7 100644 --- a/test/question.py +++ b/test/question.py @@ -7,17 +7,15 @@ class QuestionTestCase(unittest.TestCase): def test_follow_question_with_id(self): data = Question(id=32096743).follow_question() - self.assertIn("is_following", data) - self.assertIn("true", data) + self.assertEqual({"is_following": True}, data) def test_unfollow_question_with_id(self): data = Question(id=32096743).unfollow_question() - self.assertEqual('', data) + self.assertEqual({"is_following": False}, data) def test_follow_question_with_url(self): data = Question(url='https://www.zhihu.com/question/58684385').follow_question() - self.assertIn("is_following", data) - self.assertIn("true", data) + self.assertEqual({"is_following": True}, data) def test_follow_question_with_answer_url(self): """ @@ -25,9 +23,9 @@ def test_follow_question_with_answer_url(self): :return: """ data = Question(url='https://www.zhihu.com/question/59001738/answer/160832685').follow_question() - self.assertIn("is_following", data) - self.assertIn("true", data) + self.assertEqual({"is_following": True}, data) def test_unfollow_question_with_url(self): data = Question(url='https://www.zhihu.com/question/58684385').unfollow_question() - self.assertEqual('', data) + self.assertEqual({"is_following": False}, data) + diff --git a/zhihu/__init__.py b/zhihu/__init__.py index b20df2a..a9a8bf6 100644 --- a/zhihu/__init__.py +++ b/zhihu/__init__.py @@ -4,8 +4,10 @@ from zhihu.models import common from zhihu.models import question from zhihu.models import column +from zhihu.models import account Answer = answer.Answer Zhihu = common.Common Question = question.Question Column = column.Column +Account = account.Account diff --git a/zhihu/auth.py b/zhihu/auth.py index 4e0db5c..6114ac3 100644 --- a/zhihu/auth.py +++ b/zhihu/auth.py @@ -2,9 +2,14 @@ import os -from zhihu.models import Model +from zhihu.models import account from zhihu.settings import COOKIES_FILE +try: + input = raw_input # py2 +except: + pass + def need_login(func): """ @@ -12,15 +17,14 @@ def need_login(func): """ def wrapper(self, *args, **kwargs): - assert isinstance(self, Model) success = True # TODO 1. 不能这样简单粗暴判断cookie文件存不存在,因为有可能文件里面的cookie信息已经过期,也有可能只是一个空文件 if not os.path.exists(COOKIES_FILE): success = False while not success: - email = input("请输入email:") + email = input("请输入email或者手机号码:") password = input("请输入密码:") - success = self.login(email, password) + success = account.Account().login(email, password) if success: result = func(self, *args, **kwargs) return result diff --git a/zhihu/main.py b/zhihu/main.py index 49ad21d..384853a 100644 --- a/zhihu/main.py +++ b/zhihu/main.py @@ -5,5 +5,17 @@ def vote_up_with_id(): data = Answer(id=14005147).vote_up() print(data) + +from zhihu.models import Model +from zhihu.models.account import Account + +import logging + +logging.basicConfig(level=logging.INFO) + if __name__ == '__main__': - vote_up_with_id() \ No newline at end of file + # vote_up_with_id() + + model = Account() + model.login("ssss@qq.com", "ssss") + model.login("158", "xxxx") diff --git a/zhihu/models/__init__.py b/zhihu/models/__init__.py index 22e8790..d004b34 100644 --- a/zhihu/models/__init__.py +++ b/zhihu/models/__init__.py @@ -3,26 +3,39 @@ import logging import os import platform -import re import subprocess import time +import re try: from http import cookiejar # py3 except: import cookielib as cookiejar # py2 +try: + input = raw_input # py2 +except: + pass + import requests import requests.packages.urllib3 as urllib3 from bs4 import BeautifulSoup -from ..error import ZhihuError -from ..url import URL +from zhihu.error import ZhihuError +from zhihu.url import URL from zhihu import settings urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) +class RequestDataType(object): + """ + 发送请求时,传给知乎服务器的数据类型 + 有两种请求,1. 表单类型,2 json格式的字符串 + """ + FORM_DATA, JSON_DATA = range(2) + + class Model(object): def __init__(self, **kwargs): self._session = requests.Session() @@ -56,7 +69,6 @@ def _get_captcha(self, **kwargs): subprocess.call(['xdg-open', 'captcha.jpg']) else: os.startfile('captcha.jpg') - captcha = input("验证码:") return captcha @@ -90,44 +102,18 @@ def _user_slug(self, profile_url): else: raise ZhihuError("invalid profile url") - def login(self, email, password, **kwargs): - """ - 登录需要的验证码会保存在当前目录,需要用户自己识别,并输入 - """ - # TODO 还有其他登录方式 - request_body = {'email': email, - 'password': password, - '_xsrf': self._get_xsrf(**kwargs), - "captcha": self._get_captcha(**kwargs), - 'remember_me': 'true'} - - response = self._session.post(URL.login(), data=request_body, **kwargs) - 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 - - def _execute(self, method="post", url=None, data=None, **kwargs): + def _execute(self, method="post", url=None, data=None, data_type=RequestDataType.JSON_DATA, **kwargs): """ - 用户执行某些交互操作(点赞、关注等)的请求方法 + 通用请求方法 :param method: 请求方法 :param url: 请求URL :param data: 请求数据 + :param data_type: 提交的数据格式(可能是表单类型,也可能是json格式的字符串) :param kwargs: requests支持的参数,比如可以设置代理参数 - :return: text + :return: response """ - r = getattr(self._session, method)(url, json=data, **kwargs) - if r.ok: - self.log("操作成功") + if data_type == RequestDataType.JSON_DATA: + r = getattr(self._session, method)(url, json=data, **kwargs) else: - self.log("操作失败") - return r.text + r = getattr(self._session, method)(url, data=data, **kwargs) + return r diff --git a/zhihu/models/account.py b/zhihu/models/account.py new file mode 100644 index 0000000..128f5ff --- /dev/null +++ b/zhihu/models/account.py @@ -0,0 +1,62 @@ +# encoding: utf-8 + +from zhihu.models import Model +from zhihu.url import URL +from zhihu.models import RequestDataType +import re +import logging + + +class Account(Model): + def login(self, account, password, **kwargs): + """ + 账户登录 + :param account: email或者手机号码 + :param password: + :param kwargs: + :return: + """ + email_regex = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)" + pattern = re.compile(email_regex) + if pattern.match(account): + return self._login_with_email(account, password, **kwargs) + else: + return self._login_with_phone(account, password, **kwargs) + + def _login_with_phone(self, phone, password, **kwargs): + data = { + '_xsrf': self._get_xsrf(), + 'password': password, + 'phone_num': phone, + "captcha": self._get_captcha(), + "remeber_me": "true", + } + return self._execute(url=URL.phone_login(), data=data, **kwargs) + + def _login_with_email(self, email, password, **kwargs): + data = {'email': email, + 'password': password, + '_xsrf': self._get_xsrf(**kwargs), + "captcha": self._get_captcha(**kwargs), + 'remember_me': 'true'} + return self._execute(url=URL.email_login(), data=data, **kwargs) + + def _execute(self, method="post", url=None, data=None, data_type=RequestDataType.JSON_DATA, **kwargs): + + r = super(Account, self)._execute(method="post", url=url, data=data, data_type=RequestDataType.FORM_DATA, + **kwargs) + + if r.ok: + result = r.json() + if result.get("r") == 0: + self.log(result.get("msg")) + return True + else: + + self.log(result.get("msg"), level=logging.ERROR) + return False + + else: + self.log("登录失败", level=logging.ERROR) + self.log(r.text) + return False diff --git a/zhihu/models/answer.py b/zhihu/models/answer.py index ed0bba6..2cb89f4 100644 --- a/zhihu/models/answer.py +++ b/zhihu/models/answer.py @@ -30,32 +30,42 @@ def vote_up(self, **kwargs): """ 赞同 """ - return self._execute(url=URL.vote_up(self.id), data={"type": "up"}, **kwargs) + r = self._execute(url=URL.vote_up(self.id), data={"type": "up"}, **kwargs) + if r.ok: + return r.json() @need_login def vote_down(self, **kwargs): """ 反对 """ - return self._execute(url=URL.vote_down(self.id), data={"type": "down"}, **kwargs) + r = self._execute(url=URL.vote_down(self.id), data={"type": "down"}, **kwargs) + if r.ok: + return r.json() @need_login def vote_neutral(self, **kwargs): """ 中立 """ - return self._execute(url=URL.vote_neutral(self.id), data={"type": "neutral"}, **kwargs) + r = self._execute(url=URL.vote_neutral(self.id), data={"type": "neutral"}, **kwargs) + if r.ok: + return r.json() @need_login def thank(self, **kwargs): """ 感谢 """ - return self._execute(url=URL.thank(self.id), **kwargs) + r = self._execute(url=URL.thank(self.id), **kwargs) + if r.ok: + return r.json() @need_login def thank_cancel(self, **kwargs): """ 感谢取消 """ - return self._execute(method="delete", url=URL.thank_cancel(self.id), **kwargs) + r = self._execute(method="delete", url=URL.thank_cancel(self.id), **kwargs) + if r.ok: + return r.json() diff --git a/zhihu/models/question.py b/zhihu/models/question.py index 396bad5..c726bf2 100644 --- a/zhihu/models/question.py +++ b/zhihu/models/question.py @@ -25,12 +25,18 @@ def _extract_id(url): match = pattern.search(url) return match.group(1) if match else None + # def _execute(self, method="post", url=None, data=None, **kwargs): + # super(Question, self)._execute(method=method, url=url, data=data, **kwargs) + @need_login def follow_question(self, **kwargs): """关注某问题""" - return self._execute(url=URL.follow_question(self.id), **kwargs) + r = self._execute(url=URL.follow_question(self.id), **kwargs) + return r.json() @need_login def unfollow_question(self, **kwargs): """取消关注某问题""" - return self._execute(method="delete", url=URL.unfollow_question(self.id), **kwargs) + r = self._execute(method="delete", url=URL.unfollow_question(self.id), **kwargs) + if r.ok: + return {"is_following": False} diff --git a/zhihu/url.py b/zhihu/url.py index 5d0c03b..ab8a992 100644 --- a/zhihu/url.py +++ b/zhihu/url.py @@ -9,11 +9,16 @@ class URL(object): host = "https://www.zhihu.com" zhuanlan_host = "https://zhuanlan.zhihu.com" - # 登录 + # 邮箱登录 @staticmethod - def login(): + def email_login(): return URL.host + "/login/email" + # 手机登录 + @staticmethod + def phone_login(): + return URL.host + "/login/phone_num" + # 私信 @staticmethod def message(): From 28eca6636335bcf8211e6e6c4b37542a71b5c850 Mon Sep 17 00:00:00 2001 From: Oopswc <706543191@qq.com> Date: Tue, 2 May 2017 18:51:24 +0800 Subject: [PATCH 13/17] fix phonenum match which can check correctness of input account. --- zhihu/models/account.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/zhihu/models/account.py b/zhihu/models/account.py index 128f5ff..0d34dc6 100644 --- a/zhihu/models/account.py +++ b/zhihu/models/account.py @@ -17,11 +17,20 @@ def login(self, account, password, **kwargs): :return: """ email_regex = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)" + #check phone num with loose limit. + #allows: phonenum startwith 1 and length = 11 + phone_regex = r"(^1\d{10}$)" pattern = re.compile(email_regex) + phone = re.compile(phone_regex) + if pattern.match(account): return self._login_with_email(account, password, **kwargs) - else: + elif phone.match(account): return self._login_with_phone(account, password, **kwargs) + else: + self.log("登录名错误,需要重新输入.", level=logging.ERROR) + #if False, need reinput in auth.py + return False def _login_with_phone(self, phone, password, **kwargs): data = { From b2e0368606eba32abebd06cef0f297477c27fb84 Mon Sep 17 00:00:00 2001 From: liuzhijun Date: Wed, 3 May 2017 00:24:44 +0800 Subject: [PATCH 14/17] fix document error charactor --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 39dc9bf..ce9a4b4 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ pip install git+git://github.com/lzjun567/zhihu-api --upgrade ## 贡献者 -欢迎 PR, 所有贡献者都将出现在这里,排名部分先后 +欢迎 PR, 所有贡献者都将出现在这里,排名不分先后 * [@BigBorg](https://github.com/BigBorg) * [@xiaowenlong100](https://github.com/xiaowenlong100) From 7a79d2ab76ffd8ba1ea0daada8acb9403996f3c6 Mon Sep 17 00:00:00 2001 From: liuzhijun Date: Wed, 3 May 2017 00:27:25 +0800 Subject: [PATCH 15/17] remove unneccsary comment --- zhihu/models/account.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/zhihu/models/account.py b/zhihu/models/account.py index 0d34dc6..6daaca3 100644 --- a/zhihu/models/account.py +++ b/zhihu/models/account.py @@ -17,19 +17,16 @@ def login(self, account, password, **kwargs): :return: """ email_regex = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)" - #check phone num with loose limit. - #allows: phonenum startwith 1 and length = 11 phone_regex = r"(^1\d{10}$)" - pattern = re.compile(email_regex) - phone = re.compile(phone_regex) + email_pattern = re.compile(email_regex) + phone_pattern = re.compile(phone_regex) - if pattern.match(account): + if email_pattern.match(account): return self._login_with_email(account, password, **kwargs) - elif phone.match(account): + elif phone_pattern.match(account): return self._login_with_phone(account, password, **kwargs) else: self.log("登录名错误,需要重新输入.", level=logging.ERROR) - #if False, need reinput in auth.py return False def _login_with_phone(self, phone, password, **kwargs): From 98ee3df75e9b18c96820922e2efb1c248dc6f6e5 Mon Sep 17 00:00:00 2001 From: liugenxian Date: Sat, 6 May 2017 09:43:49 +0800 Subject: [PATCH 16/17] 2017.05.06 add userasks function --- zhihu/__init__.py | 2 ++ zhihu/main.py | 19 +++++++++++-- zhihu/models/userasks.py | 58 ++++++++++++++++++++++++++++++++++++++++ zhihu/settings.py | 3 ++- zhihu/url.py | 6 +++++ 5 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 zhihu/models/userasks.py diff --git a/zhihu/__init__.py b/zhihu/__init__.py index b20df2a..6b5d84f 100644 --- a/zhihu/__init__.py +++ b/zhihu/__init__.py @@ -4,8 +4,10 @@ from zhihu.models import common from zhihu.models import question from zhihu.models import column +from zhihu.models import userasks Answer = answer.Answer Zhihu = common.Common Question = question.Question Column = column.Column +Userasks = userasks.Userasks \ No newline at end of file diff --git a/zhihu/main.py b/zhihu/main.py index 49ad21d..e68b3bd 100644 --- a/zhihu/main.py +++ b/zhihu/main.py @@ -1,9 +1,24 @@ from zhihu import Answer - +from zhihu import Zhihu +from zhihu import Userasks +from zhihu import Column def vote_up_with_id(): data = Answer(id=14005147).vote_up() print(data) +def zhihu_user(): + data = Zhihu().user(user_slug="6xian") + print(data) + +def user_asks(): + data = Userasks(slug='heikehuawuya').asks_list() + print(data) + +def column(): + data = Column("pythoneer").followers(5) + print(data) + if __name__ == '__main__': - vote_up_with_id() \ No newline at end of file + # vote_up_with_id() + user_asks() diff --git a/zhihu/models/userasks.py b/zhihu/models/userasks.py new file mode 100644 index 0000000..66d0b97 --- /dev/null +++ b/zhihu/models/userasks.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +__mtime__ = 2017/5/1 0001 +""" + +import re + +from zhihu.auth import need_login +from zhihu.error import ZhihuError +from zhihu.models import Model +from zhihu.url import URL +from bs4 import BeautifulSoup + + +class Userasks(Model): + """ + 获取指定用户的“提问”列表 + """ + def __init__(self, slug=None, url=None): + slug = slug if slug is not None else self._extract_slug(url) + if not slug: + raise ZhihuError("没有指定用户的的slug或者url") + self.slug = slug + super(Userasks, self).__init__() + + @staticmethod + def _extract_id(url): + """ + 从url中提取目标id + :param url: + :return: + """ + pattern = re.compile("https://www.zhihu.com/people/(\w+).*?/") + match = pattern.search(url) + return match.group(1) if match else None + + def asks_list(self, **kwargs): + question_list = [] + url = [] + url.append(URL.user_asks(self.slug)) + response = self._session.get(URL.user_asks(self.slug),**kwargs) + soup = BeautifulSoup(response.content, "html.parser") + page_tag = soup.find(name="div", class_="Pagination") + if page_tag: + url = [] + for button in page_tag.find_all("button"): + if re.match(r'^\d$', button.get_text()): + url.append(URL.user_asks(self.slug) + '?page=' + button.get_text()) + + for page in url: + response = self._session.get(page, **kwargs) + soup = BeautifulSoup(response.content, "html.parser") + tag_list = soup.find_all(name="div", class_="List-item") + for name in tag_list: + question_list.append(name.find("a").get_text()) + + return question_list diff --git a/zhihu/settings.py b/zhihu/settings.py index f449a37..45c3c5f 100644 --- a/zhihu/settings.py +++ b/zhihu/settings.py @@ -44,7 +44,8 @@ "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24" ] -USER_AGENT = random.choice(user_agent_list) +# USER_AGENT = random.choice(user_agent_list) +USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" HEADERS = {"Host": "www.zhihu.com", "Referer": "https://www.zhihu.com/", diff --git a/zhihu/url.py b/zhihu/url.py index 5d0c03b..03e4794 100644 --- a/zhihu/url.py +++ b/zhihu/url.py @@ -70,3 +70,9 @@ def column(slug): def column_followers(slug): a = URL.zhuanlan_host + "/api/columns/{slug}/followers".format(slug=slug) return a + + # 用户的“提问”清单列表 + @staticmethod + def user_asks(slug): + return "https://www.zhihu.com/people/{slug}/asks".format(slug=slug) + From ed001725fd406dc0c08085378f5aaafb1a8db645 Mon Sep 17 00:00:00 2001 From: liugenxian Date: Sat, 6 May 2017 10:14:26 +0800 Subject: [PATCH 17/17] add test/userasks.py --- test/userasks.py | 17 +++++++++++++++++ zhihu/__init__.py | 11 ++--------- zhihu/main.py | 2 +- zhihu/models/userasks.py | 4 ++-- 4 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 test/userasks.py diff --git a/test/userasks.py b/test/userasks.py new file mode 100644 index 0000000..7c9c895 --- /dev/null +++ b/test/userasks.py @@ -0,0 +1,17 @@ +# encoding: utf-8 + +import unittest + +from zhihu import Userasks + + +class UserasksTestCase(unittest.TestCase): + def test_asks_list_with_slug(self): + slug = "heikehuawuya" + data = Userasks(slug=slug).asks_list() + self.assertIn('黑客电影和现实区别?', data) + + def test_asks_list_with_url(self): + url = "https://www.zhihu.com/people/heikehuawuya" + data = Userasks(url=url).asks_list() + self.assertIn('黑客电影和现实区别?', data) diff --git a/zhihu/__init__.py b/zhihu/__init__.py index 9ddd1ff..48f4d10 100644 --- a/zhihu/__init__.py +++ b/zhihu/__init__.py @@ -4,18 +4,11 @@ from zhihu.models import common from zhihu.models import question from zhihu.models import column -<<<<<<< HEAD -from zhihu.models import userasks -======= from zhihu.models import account ->>>>>>> upstream/master +from zhihu.models import userasks Answer = answer.Answer Zhihu = common.Common Question = question.Question -Column = column.Column -<<<<<<< HEAD -Userasks = userasks.Userasks -======= Account = account.Account ->>>>>>> upstream/master +Userasks = userasks.Userasks diff --git a/zhihu/main.py b/zhihu/main.py index c280fe7..fe58196 100644 --- a/zhihu/main.py +++ b/zhihu/main.py @@ -7,7 +7,7 @@ def vote_up_with_id(): data = Answer(id=14005147).vote_up() print(data) -<<<<<<< HEAD + def zhihu_user(): data = Zhihu().user(user_slug="6xian") print(data) diff --git a/zhihu/models/userasks.py b/zhihu/models/userasks.py index 66d0b97..99099e3 100644 --- a/zhihu/models/userasks.py +++ b/zhihu/models/userasks.py @@ -25,13 +25,13 @@ def __init__(self, slug=None, url=None): super(Userasks, self).__init__() @staticmethod - def _extract_id(url): + def _extract_slug(url): """ 从url中提取目标id :param url: :return: """ - pattern = re.compile("https://www.zhihu.com/people/(\w+).*?/") + pattern = re.compile("https://www.zhihu.com/people/(\w+).*?") match = pattern.search(url) return match.group(1) if match else None