登录的需求
有些数据,必须在登录之后才能查看,所以我们在爬取过程中就会产生模拟登录的需求,它有两个点:
1、未登录的情况下无法查看数据,或者直接弹出登录框提示你先登录
2、登录后登录状态的保持(通常可以理解为cookie的处理)
登录的逻辑
- 访问登录页面(部分网站会在登录页面设定token或标识来反爬虫,根据Network查看post数据来确认)
- 构造登录所需数据,并携带伪造的数据发送登录请求(如token或标识、User-Agent/HOST/Referer等数据,向登录地址POST数据。)
- 根据某个状态码或者登录后跳转url判断是否登录成功
- 登录成功后获取start_urls,并且调用parse进行数据爬取
模拟登录注意的地方
登录爬取有几个特点,比如浏览器不能换,可能UserAgent也不能换、要用到cookie、页面可能会有重定向,通常表现为登录后跳转、页面需要发送token或其他标识,所以正则是个关键。
- 最好不要用自动切换UserAgent的功能(未测试)
- 必须在配置开启cookie,COOKIES_ENABLED = True
- 关闭重定向禁止开关 #REDIRECT_ENABLED = False # 禁止重定向
- robots协议也关掉 ROBOTSTXT_OBEY = False
代码实现
这里以东盟贷为例子,
示例网站
这是最简单的一种例子(登录时没有验证码、不用携带token、不用携带其他标识),仅仅需要把用户名和密码发送过去即可。
当你需要爬取投资列表的数据时,要到【我要投资】页面去爬,你想要打开那个页面他就会判断你是否登录,如果没登录就会给你直接跳转到登录界面
被强制跳转到登录页面 审查元素,看input name那我们面对这种情况,就必须先登录后请求页面再爬取数据(我这里仅演示到登录完成),示例代码:
# -*- coding: utf-8 -*-
import scrapy
class DongmengSpider(scrapy.Spider):
name = 'dongmeng'
allowed_domains = ['www.dongmengdai.com']
start_urls = ['https://www.dongmengdai.com/view/Investment_list_che.php?page=1']
# 根据浏览器Network返回值来构造header,这是比较简单的header,复杂的还会有很多信息
header = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0",
"HOST": "www.dongmengdai.com",
"Referer": "https://www.dongmengdai.com/index.php?user&q=action/login",
}
def parse(self, response):
""" 正式进入爬取区域 """
pass
def start_requests(self):
"""
重载start_requests方法 待登录成功后,再进入parse进行数据爬取
访问登录页面 并调用do_login方法进行登录
"""
return [scrapy.Request('https://www.dongmengdai.com/index.php?user&q=action/login', headers=self.header, callback=self.do_login)]
def do_login(self, response):
"""
根据Network的信息 向登录地址发送请求
携带用户名和密码 如果需要token或者其他标识则需要用正则进行匹配,然后放到login_data中
调用is_login方法判断是否登录成功
"""
login_url = "https://www.dongmengdai.com/index.php?user&q=action/login"
login_data = {
"keywords": "18077365555",
"password": "123456789"
}
return [scrapy.FormRequest(url=login_url,formdata=login_data,headers=self.header,callback=self.is_login)]
def is_login(self, response):
"""
这个网站登陆后会自动跳转到用户中心 可根据返回的url判断是否登录成功
其他网站可以依靠状态码进行判断
如果登录成功则从 start_urls中抽取url进行爬取
这里不用设置callback回调parse 因为它默认调用parse
如果是在crawl模板的爬虫,可能需要设置callback调用
"""
if "index.php?user" in response.url:
for url in self.start_urls:
yield scrapy.Request(url, dont_filter=True, headers=self.header)
else:
print("登录失败")
具体逻辑整理
具体的逻辑我已经写在代码中了,这里再整理一下:
1、先确定起始url和设置domains
2、根据观察浏览器Network返回值来构造header(因为它也识别请求头信息)
3、重载start_requests方法并带上请求头信息来发起请求
4、do_login方法中执行具体的登录操作,(keywords和password是登录框的input name,通过右键审查元素可以看到html结构,通过input name来定位输入框)以及发起请求
5、is_login方法来判断是否登录成功,并且指定了下一步操作的方法(可以开始爬数据了)
Cookie的问题
可以看到,上面的代码里面只是发送了用户名和密码,但是常规的登录请求是需要保存和发送cookie的,我们在代码中并没有保存cookie和二次请求携带cookie的操作,那Scrapy是如何完成这个行为的呢?
在源码目录site-packages/scrapy/downloadermiddlewares/cookies.py文件中,可以看到具体的源码:
CookiesMiddleware的第前面两个个方法是重载from_crawler:
def __init__(self, debug=False):
self.jars = defaultdict(CookieJar)
self.debug = debug
@classmethod
def from_crawler(cls, crawler):
if not crawler.settings.getbool('COOKIES_ENABLED'):
raise NotConfigured
return cls(crawler.settings.getbool('COOKIES_DEBUG'))
init在加载的时候初始化CookieJar,from_crawler则是检查settings里面的cookie配置情况。
接着到process_request方法:
def process_request(self, request, spider):
if request.meta.get('dont_merge_cookies', False):
return
cookiejarkey = request.meta.get("cookiejar")
jar = self.jars[cookiejarkey]
cookies = self._get_request_cookies(jar, request)
for cookie in cookies:
jar.set_cookie_if_ok(cookie, request)
# set Cookie header
request.headers.pop('Cookie', None)
jar.add_cookie_header(request)
self._debug_cookie(request, spider)
它完成的任务大致就是设置cookie,请求的时候就带上。
而process_response方法又完成了什么任务呢:
def process_response(self, request, response, spider):
if request.meta.get('dont_merge_cookies', False):
return response
# extract cookies from Set-Cookie and drop invalid/expired cookies
cookiejarkey = request.meta.get("cookiejar")
jar = self.jars[cookiejarkey]
jar.extract_cookies(response, request)
self._debug_set_cookie(response, spider)
它是完成cookie的筛选,提取cookie和删除废弃的cookie
下面还有几个方法_debug_cookie、_debug_set_cookie、_format_cookie、_get_request_cookies他们几个完成了cookie的获取、生成和格式化等任务。
cookie小结
可以得出结论,Scrapy框架会自动帮我们处理cookie的问题,在常规的使用当中我们不需要关心它的切换和更新问题。只有在一些逻辑处理的时候,有可能涉及到登录逻辑的改动,才需要了解底层原理并对某个方法进行重载,以实现逻辑的变化。
网友评论