折腾了将近两天,中间数次想要放弃,还好硬着头皮搞下去了,在此分享出来,希望有同等需求的各位能少走一些弯路。 源码放在了github上, 欢迎前往查看。 若是帮你解决了问题,或者给了你启发,不要吝啬给我加一星。
1 工具准备
在开始之前,请确保 scrpay 正确安装,手头有一款简洁而强大的浏览器, 若是你有使用 postman 那就更好了。
scrapy genspider zhihu |
使用以上命令生成知乎爬虫,代码如下:
1 | # -*- coding: utf-8 -*- |
有一点切记,不要忘了启用 Cookies, 切记切记 :
1 | # Disable cookies (enabled by default) |
2 模拟登陆
过程如下:
- 进入登录页,获取 Header 和 Cookie 信息, 完善的 Header 信息能尽量伪装爬虫, 有效 Cookie 信息能迷惑知乎服务端,使其认为当前登录非首次登录,若无有效 Cookie 会遭遇验证码。 在抓取数据之前,请在浏览器中登录过知乎,这样才使得 Cookie 是有效的。
<img src="/images/zhihu-headers-cookies.jpg” />
Header 和 Cookie 整理如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47headers = {
'Host':
'www.zhihu.com',
'Connection':
'keep-alive',
'Origin':
'https://www.zhihu.com',
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
'Content-Type':
'application/x-www-form-urlencoded; charset=UTF-8',
'Accept':
'*/*',
'X-Requested-With':
'XMLHttpRequest',
'DNT':
1,
'Referer':
'https://www.zhihu.com/',
'Accept-Encoding':
'gzip, deflate, br',
'Accept-Language':
'zh-CN,zh;q=0.8,en;q=0.6',
}
cookies = {
'd_c0':
'"AHCAtu1iqAmPTped76X1ZdN0X_qAwhjdLUU=|1458699045"',
'__utma':
'51854390.1407411155.1458699046.1458699046.1458699046.1',
'__utmv':
'51854390.000--|3=entry_date=20160322=1',
'_zap':
'850897bb-cba4-4d0b-8653-fd65e7578ac2',
'q_c1':
'b7918ff9a5514d2981c30050c8c732e1|1502937247000|1491446589000',
'aliyungf_tc':
'AQAAAHVgviiNyQsAOhSntJ5J/coWYtad',
'_xsrf':
'b12fdca8-cb35-407a-bc4c-6b05feff37cb',
'l_cap_id':
'"MDk0MzRjYjM4NjAwNDU0MzhlYWNlODQ3MGQzZWM0YWU=|1503382513|9af99534aa22d5db92c7f58b45f3f3c772675fed"',
'r_cap_id':
'"M2RlNDZjN2RkNTBmNGFmNDk2ZjY4NjIzY2FmNTE4NDg=|1503382513|13370a99ee367273b71d877de17f05b2986ce0ef"',
'cap_id':
'"NmZjODUxZjQ0NzgxNGEzNmJiOTJhOTlkMTVjNWIxMDQ=|1503382513|dba2e9c6af7f950547474f827ef440d7a2950163"',
} - 在浏览器中,模拟登陆,抓取登陆请求信息。
<img src="/images/zhihu-login-args.jpg” />
从图中可以看到 _xsrf 参数, 这个参数与登陆验证信息无关,但很明显是由登陆页面携带的信息。 Google 了下 xsrf 的含义, 是用于防范 跨站请求伪造 。
<img src="/images/zhihu-xsrf.jpg” />
- 整理以上,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35loginUrl = 'https://www.zhihu.com/#signin'
siginUrl = 'https://www.zhihu.com/login/email'
def start_requests(self):
return [
scrapy.http.FormRequest(
self.loginUrl,
headers=self.headers,
cookies=self.cookies,
meta={'cookiejar': 1},
callback=self.post_login)
]
def post_login(self, response):
xsrf = response.css(
'div.view-signin > form > input[name=_xsrf]::attr(value)'
).extract_first()
self.headers['X-Xsrftoken'] = xsrf
return [
scrapy.http.FormRequest(
self.siginUrl,
method='POST',
headers=self.headers,
meta={'cookiejar': response.meta['cookiejar']},
formdata={
'_xsrf': xsrf,
'captcha_type': 'cn',
'email': 'xxxxxx@163.com',
'password': 'xxxxxx',
},
callback=self.after_login)
]
3 设置Bearer Token
经过上述步骤登陆成功了,有点小激动,有没有! 但苦难到此还远没有结束,这个时候尝试抓取最近热门话题,直接返回 code:401 ,未授权的访问。 授权信息未设置,导致了此类错误,莫非遗漏了什么,看来只能在浏览器中追踪请求参数来侦测问题。 在浏览器的请求中,包含了Bearer Token, 而我在scrapy中模拟的请求中未包含此信息,所以我被服务器认定为未授权的。 通过观察发现 Bearer Token 的关键部分,就是 Cookies 中的 z_c0 对应的信息。
<img src="/images/zhihu-bearer-token.png” />
z_c0 包含的信息,是在登陆完成时种下的,所以从登陆完成返回的登陆信息里,获取要设置的 Cookies 信息, 然后拼接出 Bearer Token,最后设置到 Header 中。
代码整理如下:
1 | def after_login(self, response): |
4 获取数据
上述步骤后,数据获取就水到渠成了,为了检测成功与否, 把返回信息写到文件中,而且只获取前五十个,代码如下:
1 | feedUrl = 'https://www.zhihu.com/api/v3/feed/topstory' |
获取的数据,采用json格式, 如下所示:
<img src="/images/zhihu-feeds.png” />
Render by hexo-renderer-org with Emacs 25.3.2 (Org mode 8.2.10)