2

scrapy模拟登陆知乎

 2 years ago
source link: https://brantou.github.io/2017/08/22/scrapy-crawl-zhihu/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

折腾了将近两天,中间数次想要放弃,还好硬着头皮搞下去了,在此分享出来,希望有同等需求的各位能少走一些弯路。 源码放在了github上, 欢迎前往查看。 若是帮你解决了问题,或者给了你启发,不要吝啬给我加一星

1 工具准备

在开始之前,请确保 scrpay 正确安装,手头有一款简洁而强大的浏览器, 若是你有使用 postman 那就更好了。

scrapy genspider zhihu

使用以上命令生成知乎爬虫,代码如下:

# -*- coding: utf-8 -*-
import scrapy


class ZhihuSpider(scrapy.Spider):
name = 'zhihu'
allowed_domains = ['www.zhihu.com']
start_urls = ['http://www.zhihu.com/']

def parse(self, response):
pass

有一点切记,不要忘了启用 Cookies, 切记切记

# Disable cookies (enabled by default)
COOKIES_ENABLED = True

2 模拟登陆

过程如下:

  • 进入登录页,获取 HeaderCookie 信息, 完善的 Header 信息能尽量伪装爬虫, 有效 Cookie 信息能迷惑知乎服务端,使其认为当前登录非首次登录,若无有效 Cookie 会遭遇验证码。 在抓取数据之前,请在浏览器中登录过知乎,这样才使得 Cookie 是有效的。

    <img src="/images/zhihu-headers-cookies.jpg” />

    HeaderCookie 整理如下:

    headers = {
    '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” />

  • 整理以上,代码如下:
    loginUrl = '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': '[email protected]',
    '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 中。

代码整理如下:

def after_login(self, response):
jdict = json.loads(response.body)
print('after_login', jdict)
if jdict['r'] == 0:
z_c0 = response.headers.getlist('Set-Cookie')[2].split(';')[0].split(
'=')[1]
self.headers['authorization'] = 'Bearer ' + z_c0
return scrapy.http.FormRequest(
url=self.feedUrl,
method='GET',
meta={'cookiejar': response.meta['cookiejar']},
headers=self.headers,
formdata={
'action_feed': 'True',
'limit': '10',
'action': 'down',
'after_id': str(self.curFeedId),
'desktop': 'true'
},
callback=self.parse)
else:
print(jdict['error'])

4 获取数据

上述步骤后,数据获取就水到渠成了,为了检测成功与否, 把返回信息写到文件中,而且只获取前五十个,代码如下:

feedUrl = 'https://www.zhihu.com/api/v3/feed/topstory'
nextFeedUrl = ''
curFeedId = 0


def parse(self, response):
with open('zhihu.json', 'a') as fd:
fd.write(response.body)
jdict = json.loads(response.body)
jdatas = jdict['data']
for entry in jdatas:
entry['pid'] = entry['id']
yield entry

jpaging = jdict['paging']
self.curFeedId += len(jdatas)
if jpaging['is_end'] == False and self.curFeedId < 50:
self.nextFeedUrl = jpaging['next']
yield self.next_request(response)


def next_request(self, response):
return scrapy.http.FormRequest(
url=self.nextFeedUrl,
method='GET',
meta={'cookiejar': response.meta['cookiejar']},
headers=self.headers,
callback=self.parse)

获取的数据,采用json格式, 如下所示:

<img src="/images/zhihu-feeds.png” />

5 写在最后

知乎的数据,只有登录完成之后,才可有效的获取,所以模拟登陆是无法忽略不管的。 所谓的模拟登陆,只是在scrapy中尽量的模拟在浏览器中的交互过程,使服务端无感抓包过程。 请求中附加有效的 CookiesHeaders 头信息,可有效的迷惑服务端, 同时在交互的过程中,获取后续请求必要信息和认证信息,使得整个流程能不断先前。

若是你遇到什么问题,尽量提出来,欢迎一起来讨论解决。 源码放在了github上, 欢迎前往查看。 若是帮你解决了问题,或者给了你启发,不要吝啬给我加一星

Last Updated 2017-08-23 Wed 15:21.
Render by hexo-renderer-org with Emacs 25.3.2 (Org mode 8.2.10)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK