diff --git a/README.md b/README.md index d735b4c..ce9a4b4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ -## 项目简介 - ### 目标 试图构建一个更加简洁、优雅的、Pythonic 的知乎 API。 ### 使用场景 -* 如果你想自动给你喜欢的人点赞 +* 如果你想基于知乎社区做数据分析 +* 如果你想通过程序自动给回答点赞 * 如果你想批量关注用户 * 如果你想批量发送信息 * 如果你想构建一个自己的知乎客户端 @@ -22,21 +21,16 @@ ## 安装 ```python -pip install git+git://github.com/lzjun567/zhihu-api - -# 升级到最新的代码,附加参数 --upgrade -# 推荐 pip install git+git://github.com/lzjun567/zhihu-api --upgrade ``` +### API -## API - -### 用户个人公开信息 +**个人信息** ``` >>> 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': [], @@ -51,64 +45,77 @@ 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} ``` +**取消关注** +``` +>>> zhihu.unfollow(user_slug="xiaoxiaodouzi") +{'follower_count': 5} +``` -### 点赞 +**点赞回答** ``` >>> from zhihu import Answer >>> 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() +**反对** +``` +>>> from zhihu import Answer +>>> data = Answer(id=14005147).vote_down() +>>> data +>>> {"voting": 1, "voteup_count": 314} ``` -### 反对 -vote_down -### 中立 -vote_neutral +**中立** +``` +>>> from zhihu import Answer +>>> data = Answer(id=14005147).vote_neutral() +>>> data +>>> {"voting": 1, "voteup_count": 314} +``` +**专栏的关注列表** +``` +>>> 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, 所有贡献者都将出现在这里,排名部分先后 +欢迎 PR, 所有贡献者都将出现在这里,排名不分先后 * [@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 拉你入群 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/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/test/common.py b/test/common.py index ca9dbd3..58d45f2 100644 --- a/test/common.py +++ b/test/common.py @@ -3,6 +3,8 @@ from zhihu import Zhihu from zhihu.error import ZhihuError +import time +from zhihu.models import Model class CommonTestCase(unittest.TestCase): @@ -13,3 +15,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() + 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): + """ + 带user_slug,返回follower数量 + :return: + """ + time.sleep(1) + zhihu = Zhihu() + data = zhihu.follow(user_slug="xiaoxiaodouzi") + self.assertIn('follower_count', data) + + 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() + 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): + """ + 带user_slug,返回follower数量 + :return: + """ + time.sleep(1) + zhihu = Zhihu() + data = zhihu.unfollow(user_slug="xiaoxiaodouzi") + self.assertIn('follower_count', data) 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/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 b20df2a..48f4d10 100644 --- a/zhihu/__init__.py +++ b/zhihu/__init__.py @@ -4,8 +4,11 @@ from zhihu.models import common from zhihu.models import question from zhihu.models import column +from zhihu.models import account +from zhihu.models import userasks Answer = answer.Answer Zhihu = common.Common Question = question.Question -Column = column.Column +Account = account.Account +Userasks = userasks.Userasks 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 new file mode 100644 index 0000000..fe58196 --- /dev/null +++ b/zhihu/main.py @@ -0,0 +1,41 @@ +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() + user_asks() + + +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() + + 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 b76964d..d004b34 100644 --- a/zhihu/models/__init__.py +++ b/zhihu/models/__init__.py @@ -1,32 +1,49 @@ # encoding: utf-8 import logging -import re +import os +import platform +import subprocess import time -import platform, os, subprocess +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): + 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: @@ -52,7 +69,6 @@ def _get_captcha(self, **kwargs): subprocess.call(['xdg-open', 'captcha.jpg']) else: os.startfile('captcha.jpg') - captcha = input("验证码:") return captcha @@ -86,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..6daaca3 --- /dev/null +++ b/zhihu/models/account.py @@ -0,0 +1,68 @@ +# 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-.]+$)" + phone_regex = r"(^1\d{10}$)" + email_pattern = re.compile(email_regex) + phone_pattern = re.compile(phone_regex) + + if email_pattern.match(account): + return self._login_with_email(account, password, **kwargs) + elif phone_pattern.match(account): + return self._login_with_phone(account, password, **kwargs) + else: + self.log("登录名错误,需要重新输入.", level=logging.ERROR) + return False + + 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/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/models/common.py b/zhihu/models/common.py index 76ed2d3..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): @@ -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_people(user_slug), **kwargs) + if response.ok: + return response.json() + else: + self.logger.error(u"取消关注失败, status code: %s" % response.status_code) 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/models/userasks.py b/zhihu/models/userasks.py new file mode 100644 index 0000000..99099e3 --- /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_slug(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 6daddaa..45c3c5f 100644 --- a/zhihu/settings.py +++ b/zhihu/settings.py @@ -1,11 +1,60 @@ # encoding: utf-8 import os.path +import random + +# 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" +] + +# 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/", - '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..64c0cdb 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(): @@ -69,5 +74,10 @@ def column(slug): @staticmethod def column_followers(slug): a = URL.zhuanlan_host + "/api/columns/{slug}/followers".format(slug=slug) - print(a) return a + + # 用户的“提问”清单列表 + @staticmethod + def user_asks(slug): + return "https://www.zhihu.com/people/{slug}/asks".format(slug=slug) +