Logo

郎哥编程

模拟表单登录

2020-12-21 245

有些网站需要用户登录后才能获取数据,爬虫要爬取这些受限的数据,就需要先登录网站,然后才能爬取数据。

1、 分析登录表单

使用Scrapy模拟登陆网站前,先了解一下网站登录原理,观察浏览器与网站是如何交互的。

登录表单及调试窗口

www.milihua/sample/login.aspx是一个登录页面,登录页面非常简单,要求用户输入登录账号和登录密码,因是一个参考案例,登录密码采用了明文。用户输入正确的登录账号和登录密码,网页会跳转到资源页,在资源页面包含2个图片的下载链接。

在Google Chrome浏览器打开登录页面,按下F12键,浏览器会打开网页代码调试窗口,在代码调试窗口,单击Sources选项卡,会看到网页的源代码,如下图所示:

37.png

实际的网页代码和Google Chrome浏览器显示的网页代码稍有不同,login.aspx网页代码如下:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<h3>请输入用户名和密码登录系统</h3>
<hr>
<form id="form1" method="post" action="login.aspx"  runat="server">
  <div>
    <p> 登录账号:<input type="text" id="username" value="" runat=server/></p>
    <p> 登录密码:<input type="password" id="psw" value="" runat=server/></p>
    <p><input type="submit" value="提交"/></p>
    </div>
</form>
</body>
</html>

<form>是表单标签,标签之间的内容是表单数据,表单数据会提交到由标签action属性指定的URL页面,该页面会获取从客户端提交的表单数据,在本案例中,获取id值为username和id值为psw的表单数据,然后判断username和id的值是否是正确的登录用户和登录密码,若数据正确,跳转到resource.aspx网页,该网页包含两个图片的下载链接。

查看表单提交情况。

在Google Chrome浏览器调试窗口,单击Network选项卡,在选项卡下方的内容过滤工具条中选择ALL选项。

在网页窗口输入登录用户和登录密码,登录用户为admin,登录密码为pswtest,并单击【提交】按钮,若没有输入错误,网页会跳转到resources.aspx页面,在该页面会看到两个图片的下载链接。

在Google Chrome浏览器调试窗口,单击Name子窗口下的login.aspx条目,Name子窗口的右侧子窗口会显示客户端请求和服务器响应数据。如下图所示:

38.png

Request Headers是客户端向服务端发送的请求数据,Scrapy的Request对象会创建Request Headers请求数据。

Form Date是客户端提交的表单数据,表单数据紧跟在Request Headers数据后面。表单数据由多个键值对构成,每个键值对对应一个表单元素。

39.png

案例中的表单数据有5个键值对,其中前缀“_”符号的键值对是ASPX页面自动添加的隐藏域。username键值对和psw键值对是用户输入的登录用户和登录密码。

Response Headers是服务器返回的响应数据,响应数据也是由多个键值对构成,爬虫程序可以从响应数据获取登录成功后跳转页面的URL。

40.png

爬虫程序可以从Location键值对提取登录成功后跳转页面的URL。

2、 在shell环境下模拟表单登录

FormRequest类用于发送包含表单数据的Request请求,FormRequest类继承Request类,该类额外包含一个formdata参数,接收字典形式的表单数据。

下面在scrapy shell环境下模拟表单登录。

步骤1:

在Windows命令行窗口,输入下面的scrapy shell命令,爬虫login.aspx网页:

scrapy shell http://www.milihua.com/sample/login.aspx

步骤2:

使用response的Xpath方法提取隐藏域,隐私域type属性的值为hidden。在scrapy shell环境下输入下面的命令:

hidden = response.xpath("//input[@type='hidden']")

在scrapy shell环境下输入变量名称hidden,查看hidden内容,若XPath表达式没有错误,hidden内容存储了三个隐藏域的数据,隐藏域的数据是input类型。

步骤3:

构造表单数据,将三个隐藏域的name和value添加到表单,在scrapy shell环境下输入下面的命令:

form = dict(zip(hidden.xpath('@name').extract(),hidden.xpath('@value').extract()))

在scrapy shell环境下输入变量名称form,查看form内容,若输入没有错误,生成的表单数据如下:

41.png

步骤4:

添加登录用户和登录密码输入域到表单,登录用户输入域的name属性值为username,value属性的值为admin,登录密码输入域的name属性值为psw,value属性的值为pswtest。

在scrapy shell环境下分别输入下面的命令:

form["username"] = "admin"
form["psw"] = "pswtest"

输入变量名称form,查看form内容,生成的表单数据如下:

43.png

至此,已经构建了完整的表单数据。

步骤5:

使用FormRequest类模拟表单登录,在scrapy shell环境下,分别输入下面的命令:

from scrapy.http import FormRequest
request = FormRequest("http://www.milihua.com/sample/login.aspx",formdata=form)

上面的命令创建了FormRequest实例对象request,request请求包含了URL和表单数据。下面使用shell环境下的fetch()函数来提交request,在shell环境下输入下面的命令:

fetch(request)

fetch()函数等同于scrapy fetch <url>命令,在shell环境下将fetch命令封装为fetch()函数。

执行fetch()函数后,shell环境会输出下面的内容:

2020-10-29 09:59:40 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET http://www.milihua.com/sample/resources.aspx> from <POST http://www.milihua.com/sample/login.aspx>
2020-10-29 09:59:40 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://www.milihua.com/sample/resources.aspx> (referer: None)

从输出内容可以看出,已经登录成功,服务器返回了资源页URL,后续可以继续爬取资源页来下载图片。

使用上面的表单构造方法比较麻烦,需要识别出隐藏域。Scrapy还提供了一种更简单的表单构造方法,FormRequest提供了from_response()方法,该方法从传入的response 对象中提取表单隐藏域自动添加到传入的表单中,from_response()方法声明如下:

from_response(
    response[, 
    formname=None, 
    formid=None, 
    formnumber=0, 
    formdata=None, 
    formxpath=None, 
    formcss=None, 
    clickdata=None,
     dont_click=False, 
     ...]
)

方法参数比较多,除了response参数外,其它都是可选参数。重要且常用的参数就是两个:一个是response必选参数,包含HTML表单的响应,该表单将预填充表单字段;另外一个参数是formdata,类型是一个字典对象,用于设置预填充表单中重写的字段,例如用户输入的登录用户和登录密码等表单字段。

打开Windows命令行窗口,分别执行下面的命令:

scrapy shell http://www.milihua.com/sample/login.aspx
formdata = {"username":"admin","psw":"pswtest"}
from scrapy.http import FormRequest
formdata = {"username":"admin","psw":"pswtest"}
request = FormRequest.from_response(response,formdata=formdata)
fetch(request)

命令执行完成后,从返回的结果可以看出登录成功,服务器返回了资源页URL。

3、 建立模拟登录爬虫项目

启动PyCharm建立项目,项目名称为login。在项目终端窗口使用Scrapy创建项目命令创建爬虫项目,在终端窗口输入下面的Scrapy命令并回车执行命令:

scrapy startproject simulatedlogin

在终端窗口将当前工作目录切换到simulatedlogin爬虫项目目录,输入创建爬虫命令:

scrapy genspider spider_simulatedlogin www.milihua.com

命令执行成功后,Scrapy会在项目的spiders目录创建爬虫spider_ simulatedlogin.py模块文件

(1)编辑items.py文件,定义下载图片文件必要的数据项。

import scrapy
class SimulatedloginItem(scrapy.Item):
    # 定义image_urls数据项,存储图片文件URL路径
    image_urls = scrapy.Field()
    # 定义image_paths数据项,存储图片文件下载路径
    image_paths = scrapy.Field()

(2)编辑pipelines.py文件,继承ImagesPipeline类,重写ImagesPipeline类的三个方法。

# 导入os模块
import os
# 导入scrapy
import scrapy
# 导入适配器
from itemadapter import ItemAdapter
# 导入urlparse模块
from urllib.parse import urlparse
# 导入ImagesPipeline模块
from scrapy.pipelines.images import ImagesPipeline
# 导入Scrapy异常处理模块
from scrapy.exceptions import DropItem
 
class SimulatedloginPipeline(ImagesPipeline):
    # 图片文件重命名(包含后缀名),若不重写该函数,图片名默认为图片内容的哈希值
    def file_path(self, request, response=None, info=None, *, item=None):
        # 图片文件名称取URL中的图片名称
        return os.path.basename(urlparse(request.url).path)
 
    # 设置需要下载的图片URL,并返回图片request
    def get_media_requests(self, item, info):
        # 遍历image_urls数据项,为每个图片URL生成request请求
        for image_url in item['image_urls']:
            yield scrapy.Request(image_url)
 
    # 存储图片下载路径
    def item_completed(self, results, item, info):
        # result是一个包含元组的列表
        # 元组包含两个值,第一个代表状态True/False,
        # 第二个值是一个dict对象,若元素中状态为True则取dict中的path值
        image_paths = [x['path'] for ok, x in results if ok]
        # 若image_paths为空,说明下载失败
        # 抛出DropItem异常
        if not image_paths:
            raise DropItem("图片文件下载失败")
        # 获取Item的适配器
        adapter = ItemAdapter(item)
        # 存储image_paths
        adapter['image_paths'] = image_paths
        return item

(3)编辑spider_simulatedlogin.py爬虫文件,使用FormRequest模拟表单登录。

import scrapy
# 导入Request和FormRequest
from scrapy.http import Request, FormRequest
 
from simulatedlogin.items import SimulatedloginItem
 
 
class SpiderSimulatedloginSpider(scrapy.Spider):
    name = 'spider_simulatedlogin'
    allowed_domains = ['www.milihua.com']
    start_urls = ['http://www.milihua.com']
    # 登录页面URL
    login_url = "http://www.milihua.com/sample/login.aspx"
 
    def parse(self, response):
        pass
 
    # 重写start_requests方法
    def start_requests(self):
        yield Request(self.login_url, callback=self.login)
 
    # 定义登录页面解析函数
    def login(self, response):
        # 构造FormRequest对象提交表单
        fd = {"username":"admin","psw":"pswtest"}
        # 发送FormRequest请求
        yield FormRequest.from_response(response, formdata=fd,
                                    callback=self.parse_login)
 
    # 登录成功后返回资源页
    # 解析资源页面,下载图片
    def parse_login(self, response):
        item = SimulatedloginItem()
        # 提取图片下载链接
        down_link = response.xpath("//li/a/@href").extract()
        # 相对URL转换为绝对URL
        item["image_urls"] = [response.urljoin(link) for link in down_link]
        # 使用yield语句返回item给parse的调用者
        yield item

在爬虫代码中,重写了start_requests()方法,该方法在爬取开始时调用,且仅调用一次,若要更改开始爬取的URL,可以在该方法中实现。该方法将首次爬取的URL定向为login_url,并发送Request请求,该请求绑定的处理方法为login()方法。

在login()方法内,使用response填充表单,并填入登录用户和登录密码表单字段,发送FormRequest请求,该请求绑定的处理方法为parse_login()。

在parse_login()方法内,若登录成功,response指向登录成功后服务器返回的资源页面,从资源页面中提取图片链接填充到item对象,并返回item对象。

(4)编辑settings.py配置文件,将管道类配置项的注释去掉,在配置文件尾部添加图片存储路径配置项。

去掉管道类配置项的注释

ITEM_PIPELINES = {
    'simulatedlogin.pipelines.SimulatedloginPipeline': 300,
}

添加图片存储路径配置项

# 下载图片存储路径配置项

IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)),"images")

该配置项使用了os模块,需要在配置文件头部导入os模块。

(5)运行爬虫

在PyCharm环境中,打开终端窗口,在终端窗口将当前工作目录切换到爬虫项目目录,输入Scrapy运行爬虫命令:

scrapy crawl spider_simulatedlogin -o items.json

爬虫运行完成后,会将下载的图片文件存储到爬虫项目目录下的images目录。

项目全部代码见课程资源(unit4\ login)

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论