美文网首页
【复盘】使用Scrapy模拟登录知乎

【复盘】使用Scrapy模拟登录知乎

作者: 糊君 | 来源:发表于2017-09-08 15:34 被阅读1480次

    前一阵子完成了第一阶段Python基础知识的学习,从最近开始学习使用Scrapy框架抓数据。不得不说学习进度十分缓慢啊啊啊,王者农药真害(shang)人(yin)。顺便吐槽下,买了一堆书没有看,真是买书如山倒,看书如抽丝,不过最近确实花在研究Scrapy的时间很多。废话不多说,下面切回正题。

    学习过程中有一些参考文章,先贴出来以示尊重:
    【CSDN】用scrapy 模拟知乎的登录过程
    【GitHub】fuck-login/zhihu

    创建项目

    在命令行中切换到要创建scrapy项目的目录,并使用以下命令:

    scrapy startproject zhihu_login
    

    之后进入项目的根目录(scrapy.cfg文件所在的目录),使用以下命令创建spider:

    scrapy genspider zhihu zhihu.com
    

    其中,zhihu为spider的名字,zhihu.com为spider的allow_domain和start_urls。
    项目目录类似如下所示结构:

    tutorial/
        scrapy.cfg            # deploy configuration file
    
        tutorial/             # project's Python module, you'll import your code from here
            __init__.py
    
            items.py          # project items definition file
    
            pipelines.py      # project pipelines file
    
            settings.py       # project settings file
    
            spiders/          # a directory where you'll later put your spiders
                __init__.py
    

    至此,项目创建完成,接下来就是实现代码细节了。

    spider大致工作流程

    • 由start_requests发起第一个request,如果不重写start_requests的话,请求的是start_urls中的URL,回调函数是parse,如果重写start_requests,那么请求的URL和回调函数都可以自定义;
    • 回调函数里,可以继续发起请求,也可以对爬取页面内容,比如可以爬取下一个URL;
    • 最后就是把爬到的内容持久化(存储)就万事大吉了,winner winner。
      详细可以参考【英文】Scrapy文档,中文文档可自行百度。

    此时,尝试以下命令:

    scrapy crawl zhihu
    

    得到如下结果:

    2017-09-08 14:44:06 [scrapy.core.engine] DEBUG: Crawled (500) <GET http://zhihu.com/> (referer: None)
    2017-09-08 14:44:06 [scrapy.spidermiddlewares.httperror] INFO: Ignoring response <500 http://zhihu.com/>: HTTP status code is not handled or not allowed
    

    参考文章中说这是因为未设置UA,在settings.py文件下,取消DEFAULT_REQUEST_HEADERS的注释并添加UA属性即可。

    DEFAULT_REQUEST_HEADERS = {
       'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
       'Accept-Language': 'en',
       'User-Agent' : 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Mobile Safari/537.36'
    }
    

    执行crawl命令把页面下载下来了,需要分析知乎的登录接口以及提交的参数。
    回忆下登录知乎的过程,在页面输入帐号、密码(有时候还有验证码),点击登录按钮,发生错误会有提示,正确则跳转到首页。先随便输入看看ajax请求:

    登录分析
    我在这里用的邮箱,登录接口为:https://www.zhihu.com/login/email,同时表单提交了四项数据,email和password很好理解,captcha为验证码,_xsrf在页面中有: _xsrf

    再观察下验证码的请求接口:

    验证码

    参数r是当前时间的时间戳。
    齐活儿,由此分析可知:

    1. 请求登录页面,从页面提取_xsrf;
    2. 请求验证码并下载,人工查看并输入;
    3. 构造登录请求

    完整spider代码如下:

    # -*- coding: utf-8 -*-
    import scrapy, json, time, os, requests
    from scrapy import FormRequest, Request
    try:
        from PIL import Image
    except:
        pass
    
    class ZhihuSpider(scrapy.Spider):
        name = 'zhihu'
        allowed_domains = ['zhihu.com']
        start_urls = ['https://www.zhihu.com/signin?next=/']
        headers = {
            'Host':'www.zhihu.com',
            'Referer':'https://www.zhihu.com/',
            'User-Agent':'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Mobile Safari/537.36',
        }
        #使用验证码登录
        def captcha_login(self, response):
            with open('captcha.jpg', 'wb') as f:
                f.write(response.body)
    
            try:
                im = Image.open('captcha.jpg')
                im.show()
                im.close()
            except:
                print(u'请到 %s 目录找到captcha.jpg 手动输入' % os.path.abspath('captcha.jpg'))
    
            captcha = input('请输入验证码\n:')
            formdata = response.meta['formdata']
            formdata['captcha'] = captcha
            yield FormRequest(url='https://www.zhihu.com/login/email', formdata=formdata, headers=self.headers, meta={'formdata':formdata}, callback=self.check_login)
        #构造参数,不使用验证码直接登录
        def parse(self, response):
            #xsrf = response.css('[name=_xsrf]::attr(value)').extract_first()
            xsrf = response.xpath("//input[@name='_xsrf']/@value").extract_first()
            self.headers['X-Xsrftoken'] = xsrf
            self.headers['X-Requested-With'] = 'XMLHttpRequest'
            formdata = {
                '_xsrf':xsrf,
                'email':'jasonharnic@gmail.com',
                'password':'hujunjay'
            }
            yield FormRequest(url='https://www.zhihu.com/login/email', formdata=formdata, headers=self.headers, meta={'formdata':formdata}, callback=self.check_login)
        #检查直接登录是否成功,若失败则尝试使用验证码登录
        def check_login(self, response):
            formdata = response.meta['formdata']
            msg = json.loads(response.text)
            print(msg)
            #scrapy.shell.inspect_response(response,self)
            if msg['r']==1:
                t = str(int(time.time() * 1000))
                captcha_url = 'https://www.zhihu.com/captcha.gif?r=' + t + "&type=login&lang=en"
                yield Request(url=captcha_url, headers=self.headers, meta={'formdata':formdata}, callback=self.captcha_login)
            elif msg['r']==0:
                yield Request(url='https://www.zhihu.com', headers=self.headers, callback=self.check_login)
    
    

    中间遇到的最大的坑在于,开始的时候获取验证码并没有使用scrapy,而是使用的Request,导致一直是验证码错误,因为scrapy会处理cookoies,所以中间突然用其他方式获取,导致我获取的验证码不是我当前登录所用的。

    相关文章

      网友评论

          本文标题:【复盘】使用Scrapy模拟登录知乎

          本文链接:https://www.haomeiwen.com/subject/cliwjxtx.html