Scrapy模拟登录豆瓣网初级篇



在进行模拟登录之前,应该先对网站登录的原理有所了解,首先在 Chrome 浏览器中进行一次实际的登录操作,再来观察浏览器和网站服务器是如何交互的。

在这里我使用 豆瓣网 作为此次模拟登录的示例。

首先打开 F12 开发者模式,在登录表单中输入用户名和密码(这里我的验证码是因为我尝试次数过多出现),点击登录按钮,观察控制台中 Network 下第一条请求,其为一条 post 请求,且参数在图中有所展示,那么如果需要模拟登录,就需要对这些参数进行构造。

登录的核心其实就是向服务器发送含有登录表单数据的 HTTP 请求(通常是 POST),在 Scrapy 中提供了一个 FormRequest 类(Request的子类),专门用于构造含有登录表单的请求,FormRequest 的构造器方法有一个 formdata 参数,接收字典形式的表单数据。

在本篇文章中,我先模拟登录到网站后,跳转至个人中心,然后修改我的个人签名。

模拟登录

要构造 post 请求的参数,来看上图参数中 source, redir 和 login 都是固定值,form_email, form_password 分别为用户名和密码,captcha-solution 是图片验证码的字符,captcha-id 就先去网页源代码中寻找,如下图。

在这里我将 start_urls 设置为我的个人详情页,模拟登录这里需要重写 start_requests 方法,因为如果不去重写这个方法,那么 scrapy 就会对我的个人详情页直接进行请求。

FormRequest 的 from_response 方法需传入一个 Response 对象作为第一个参数,该方法会解析 Response 对象所包含的 form 元素,帮助用户创建 FormRequest 对象,并将隐藏 input 中的信息自动填入表单数据。使用这种方法,只需通过 formdata 参数填写账号和密码即可。这里使用 PIL 的 Image 方法将图片展示出来,人工识别并输入到程序中,程序继续进行登录。

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
# 模拟登录
login_url = 'https://accounts.douban.com/login'
def start_requests(self):
yield Request(self.login_url, callback=self.login)


def login(self, response):
print('-----登录程序-----')
captcha_id = response.xpath(".//input[@name='captcha-id']/@value").get()
captcha_url = response.xpath("//*[@id='captcha_image']/@src").get()
if captcha_url is None:
print('-----登录时无验证码-----')
data = {
'form_email': 'xxxxxx@qq.com',
'form_password': 'xxxxxx'
}
else:
print('-----登录时有验证码-----')
print('-----即将下载验证码-----')
# 使用urllib 的 urlretrieve 直接下载验证码图片到本地
request.urlretrieve(captcha_url, 'captcha.png',)
try:
image = Image('captcha.png')
image.show()
except Exception as e:
pass
captcha_solution = input("请输入图片中的验证码")
data = {
'form_email': 'xxxxxx@qq.com',
'form_password': 'xxxxxx',
'captcha-solution': captcha_solution,
'captcha-id': captcha_id,
'login': '登录'
}
print('-----登录中-----')
yield FormRequest.from_response(response, formdata=data, callback=self.parse_after_login)

在 login 函数中,最后的 FormRequest 的回调函数是 parse_after_login 函数,代码如下。

1
2
3
def parse_after_login(self, response):
if "xxxxxxx的帐号" in response.text:
print("-----登录成功-----")

修改签名

在登录成功之后,需要先跳转到我的个人详情页面,再进行修改签名操作。

先手动修改签名一次,观察浏览器的请求过程,如图所示。点击修改后,浏览器中这条 POST 请求的 formdata 只有两个参数,一个 signature 就是我们正在修改的签名。

另一个是 ck 参数,ck 参数在网页源代码中同样可以找到,如下图。

有了修改签名的两个参数,我们就可以构造修改签名的 FormRequest 了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def parse_after_login(self, response):
if "xxxxxxx的帐号" in response.text:
yield from super().start_requests()

def parse(self, response):
if response.url == 'https://www.douban.com/people/xxxxxxx/':
print('-----已经进入个人详情页-----')
print('-----正在修改个人签名-----')
ck = response.xpath("//*[@id='edit_signature']/form/div/input/@value").get()
data = {
'ck': ck,
'signature': '我是 scrapy 修改的~~'
}
yield FormRequest(self.edit_signature_url, formdata=data)

完整代码

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# -*- coding: utf-8 -*-
import scrapy
from scrapy.http import Request, FormRequest
import requests
from urllib import request
from PIL import Image


class LoginSpider(scrapy.Spider):
name = 'login'
allowed_domains = ['douban.com']
start_urls = ['https://www.douban.com/people/xxxxxx/']
edit_signature_url = 'https://www.douban.com/j/people/xxxxxx/edit_signature'
headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}

def parse(self ,response):
if response.url == 'https://www.douban.com/people/xxxxxx/':
print('-----已经进入个人详情页-----')
print('-----正在修改个人签名-----')
ck = response.xpath("//*[@id='edit_signature']/form/div/input/@value").get()
data = {
'ck': ck,
'signature': '我是 scrapy 修改的~~'
}
yield FormRequest(self.edit_signature_url, formdata=data, callback=self.success)

def success(self, response):
print('-----个人签名修改成功-----')


# 模拟登录
login_url = 'https://accounts.douban.com/login'
def start_requests(self):
yield Request(self.login_url, callback=self.login)


def login(self, response):
print('-----登录程序-----')
captcha_id = response.xpath(".//input[@name='captcha-id']/@value").get()
captcha_url = response.xpath("//*[@id='captcha_image']/@src").get()
if captcha_url is None:
print('-----登录时无验证码-----')
data = {
'form_email': 'xxxxxx@qq.com',
'form_password': 'xxxxxx'
}
else:
print('-----登录时有验证码-----')
print('-----即将下载验证码-----')
request.urlretrieve(captcha_url, 'captcha.png',)
image = Image.open('captcha.png')
image.show()
captcha_solution = input("请输入验证码:")
# captcha_solution = self.recognize_captcha('captcha.png')
data = {
'form_email': 'xxxxxx@qq.com',
'form_password': 'xxxxxx',
'captcha-solution': captcha_solution,
'captcha-id': captcha_id,
'login': '登录'
}
print('-----登录中-----')
yield FormRequest.from_response(response, formdata=data, callback=self.parse_after_login)

def parse_after_login(self, response):
if "xxxxxx的帐号" in response.text:
print('-----登录成功-----')
yield from super().start_requests()

-------------本文结束感谢您的阅读-------------
0%