用scrapy 模拟知乎的登录过程

本文介绍了一种使用Scrapy框架实现的知乎登录爬虫方法,包括解析登录接口、获取验证码及登录过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本人刚开始学习,可能有些不对的地方,希望大家指正~~

首先,我们分析下知乎登陆的接口,打开浏览器,到知乎的登陆界面,随便输入一个账号密码,查看点击登陆它干了些什么(别输入正确的,不然他就跳到首页去啦~)
这里写图片描述
可以看到它调用了一个phone_num的接口(邮箱登陆同理就不演示了),再看下这个接口的参数
这里写图片描述
出了那个_xsrf外,其他参数根据名字大概都可以猜到了
我们先来看看这个_xsrf从哪里来的,打开网页源码,查找_xsrf,发现有一个叫_xsrf的隐藏input标签,值和我们传入的这个参数是一样的
这里写图片描述
这样,我们就可以直接去取了
除了这个_xsrf外,我们还看到有一个叫captcha的参数,我们猜测它应该就是验证码,我们为了看看这个验证码是如何获得的,刷新一下看看
这里写图片描述
发现它调了这样一个接口,我们把这个url复制下来,然后直接打开看看~发现正是我们的验证码,我们看这个验证码,他是让我们点击倒立的字,再看我们上传的参数,除了那张图片的大小外,还有一个input_points的参数,应该就是我们点击的点的坐标,服务器通过这个判断我们是否点击了倒立的字,如果我们这样传参数的话,验证码图片的大小我们看上去是固定的,但是这个坐标就比较麻烦了,我们再来分析一下这个请求验证码的url的参数,有3个参数,r,type,lang,最后一个参数好像是表示语言是中文,我们把它变为en验证一下,
这里写图片描述
发现请求变成了这样一张图片,这不就是直接输入验证码的图片了么~如果可以用这个输入验证码的话我们就可以解决验证码这个参数了

好了,请求的分析差不多就这样了~下面我们可以开始写代码了

先一下我使用的python版本为3.6
新建一个scrapy的项目,新建一个爬虫(以scrapy的base模板新建~另外,这篇文章就做登录的过程,item,pipelines,middleware,settings这些文件就用默认的就行了~)

我们想一下,这个登录的过程中,我们有两个接口需要调用,1是获取验证码,2就是登录,毫无疑问,获取验证码应该优先调用,我们把我们的初始url定为获取验证码,再看一下我们上面验证码接口的参数,有个r的参数,是一串数字,我们优先应该想到是一个和时间有关的参数,我们先用当前时间来试试,我们的入口函数start_requests就变成下面这样

    def start_requests(self):
        t = str(int(time.time() * 1000))
        captcha_url = 'https://www.zhihu.com/captcha.gif?r=' + t + '&type=login&lang=en'
        return [scrapy.Request(url=captcha_url, headers={},
        callback=self.parser_captcha)]

debug 看下我们的回调函数的response
发现服务器返回了500的错误,并未进入我们的回调函数,再去看下我们的网络请求的header,我们知道scrapy对于cookie等一系列值都已经封装好了,但是下面有一个Use-Agent的参数,是需要我们传的,它表示了我们的系统信息以及我们的浏览器信息等一系列信息,我们直接在我们的浏览器复制这个,在我们的header里传进去,再次debug,进入到回调函数里了
这里写图片描述
看出body是一个二进制文件,我们把文件写入本地看看是不是我们要的验证码图片(注意:这里要用pillow的Image来把图片展示出来),发现我们的猜测都是正确的,打开就是我们的验证码图片了,然后就是读取验证码里的信息问题了,这里就用简单的打开图片我们手动输入的方式,当然,还可以用云打码这种三方平台来读取验证码信息,就可以让爬虫全程不需要人来操作了
接下来,我们就需要去调用登录的接口了,记得前面我们说的参数有个_xsrf的,我们先去取这个参数

_xsrf = response.xpath("//input[@name='_xsrf']/@value").extract_first()

参数都齐了,我们去调接口去~

    def login(self, response):
        xsrf = response.xpath("//input[@name='_xsrf']/@value").extract_first()
        if xsrf is None:
            return ''
        post_url = 'https://www.zhihu.com/login/phone_num'
        post_data = {
            "_xsrf": xsrf,
            "phone_num": 'your phone number',
            "password": 'your password',
            "captcha": response.meta['captcha']
        }
        return [scrapy.FormRequest(url=post_url, formdata=post_data, headers=self.header, callback=self.check_login)]

后面还写了个检查登录是否成功的回调

    def check_login(self, response):
        js = json.loads(response.text)
        if 'msg' in js and js['msg'] == '登录成功':
            for url in self.start_urls:
                yield scrapy.Request(url=url, headers=self.header, dont_filter=True)

运行一下我们的爬虫
这里写图片描述
嗯 ,成功了,接下来就可以去主页上爬取自己感兴趣的内容了
第一次写博客,有很多东西可能描述的也不是很清楚,最后把这个爬虫的完整代码放上来吧

# -*- coding: utf-8 -*-
import json
import os

import scrapy
import time

from PIL import Image


class ZhihuloginSpider(scrapy.Spider):
    name = 'zhihulogin'
    allowed_domains = ['www.zhihu.com']
    start_urls = ['https://www.zhihu.com/']
    Agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'
    header = {
        'User-Agent': Agent,
    }

    def parse(self, response):
        #主页爬取的具体内容
        pass

    def start_requests(self):
        t = str(int(time.time() * 1000))
        captcha_url = 'https://www.zhihu.com/captcha.gif?r=' + t + '&type=login&lang=en'
        return [scrapy.Request(url=captcha_url, headers=self.header, callback=self.parser_captcha)]

    def parser_captcha(self, response):
        with open('captcha.jpg', 'wb') as f:
            f.write(response.body)
            f.close()
        try:
            im = Image.open('captcha.jpg')
            im.show()
            im.close()
        except:
            print(u'请到 %s 目录找到captcha.jpg 手动输入' % os.path.abspath('captcha.jpg'))
        captcha = input("please input the captcha\n>")
        return scrapy.FormRequest(url='https://www.zhihu.com/#signin', headers=self.header, callback=self.login, meta={
            'captcha': captcha
        })

    def login(self, response):
        xsrf = response.xpath("//input[@name='_xsrf']/@value").extract_first()
        if xsrf is None:
            return ''
        post_url = 'https://www.zhihu.com/login/phone_num'
        post_data = {
            "_xsrf": xsrf,
            "phone_num": 'your phone number',
            "password": 'your password',
            "captcha": response.meta['captcha']
        }
        return [scrapy.FormRequest(url=post_url, formdata=post_data, headers=self.header, callback=self.check_login)]

    # 验证返回是否成功
    def check_login(self, response):
        js = json.loads(response.text)
        if 'msg' in js and js['msg'] == '登录成功':
            for url in self.start_urls:
                yield scrapy.Request(url=url, headers=self.header, dont_filter=True)
### 使用 Scrapy 实现乎网页抓取 #### 自定义 Downloader Middleware 处理反爬虫机制 为了成功爬取乎网站的数据,可以利用 Scrapy 的 `Downloader Middleware` 来模拟浏览器行为并设置必要的请求头。通过这种方式能够有效规避乎的反爬虫策略。 ```python from scrapy import signals class CustomMiddleware(object): def process_request(self, request, spider): request.headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' request.headers['authorization'] = 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20' ``` 上述代码展示了如何创建一个自定义中间件类,在其中设置了 User-Agent 和 authorization 字段[^3]。 #### 定义 Item 存储结构 在项目目录下的 items.py 文件中需定义存储数据所需的字段。以下是针对乎问答的具体实现: ```python import scrapy class ZhihuAnswerItem(scrapy.Item): zhihu_id = scrapy.Field() url = scrapy.Field() question = scrapy.Field() author_id = scrapy.Field() content = scrapy.Field() praise_num = scrapy.Field() comments_num = scrapy.Field() create_time = scrapy.Field() update_time = scrapy.Field() crawl_time = scrapy.Field() ``` 这段代码定义了一个用于存储乎回答详情的对象模型[^2]。 #### JSON 数据解析与翻页逻辑 乎 API 返回的结果通常是以 JSON 格式的字符串形式存在,因此需要编写相应的解析函数来获取所需的信息,并判断是否有下一页链接继续抓取。 ```python def parse(self, response): json_data = response.json() for data in json_data.get('data'): answer_item = ZhihuAnswerItem( zhihu_id=data.get('id'), url=data.get('url'), question=data.get('question').get('title') if isinstance(data.get('question'), dict) else None, author_id=data.get('author').get('name') if isinstance(data.get('author'), dict) else None, content=data.get('content'), praise_num=data.get('voteup_count'), comments_num=data.get('comment_count') ) yield answer_item next_page_url = json_data.get('paging', {}).get('next') is_end = json_data.get('paging', {}).get('is_end') if not is_end and next_page_url: yield scrapy.Request(url=next_page_url, callback=self.parse) ``` 这里实现了对单条记录的提取以及分页加载的功能[^4]。 #### 数据持久化至 MySQL 最后一步就是把采集下来的内容存入关系型数据库MySQL当中。这可以通过 pipelines 功能模块完成配置工作。 ```python import pymysql.cursors class MysqlPipeline(): def __init__(self): self.connect = pymysql.connect(host='localhost', port=3306, user='root', password='', db='zhihudata', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) def close_spider(self, spider): self.connect.close() def process_item(self, item, spider): try: with self.connect.cursor() as cursor: sql = """INSERT INTO answers(zhihu_id,url,question,author_id,content,praise_num,comments_num,crawl_time) VALUES (%s,%s,%s,%s,%s,%s,%s,NOW())""" cursor.execute(sql,(item["zhihu_id"],item["url"],item["question"],item["author_id"],item["content"],item["praise_num"],item["comments_num"])) self.connect.commit() except Exception as e: print(e) return item ``` 以上脚本完成了从连接建立到最终关闭整个过程中的每一条记录入库操作。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值