最近在工作中频繁遇到需要登录后才能抓取的数据,但也不是没有解决办法,以往解决办法如下:
A. 现在浏览器里登录,然后把登录后的cookie粘贴到代码中去请求,这样就相当与利用cookie"伪造"了一个分身,从而跳过登录验证。
这样往往可以解决大部分需求,但是遇到需要crontab定时爬取就显得鸡肋。cookies是有生存时间的,一旦cookies死掉那么服务器上正在跑的代码也会崩溃掉,如果还是用方法A的话,就需要手动的再去粘贴一个新的cookies。但是这对于一个程序员来说简直是不能忍,一个不能自动化运行的代码不是一个成熟的代码(对代码说的话:孩子,你已经长大了,是时候学会自己粘cookie了~.~)
基于以上问题,结合自己所学 得出plan B:
先用requests库去请求登录地址,得到response对象,然后调用response对象的 .cookies()方法 得到cookie,然后把这个cookie带入A,然后问题解决。
# -*- coding: utf-8 -*-
import requests
userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
header = {
'Referer': 'https://account.wxb.com/from=https%3A%2F%2Fdata.wxb.com%2FrankArticle%3Fcate%3D3%26page%3D1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
def get_cookie():
#登录时所请求的url(如果找不到此接口,先在浏览器中打开登录界面,输入一个错的账户名密码,点击登录,然后再开发者选项中就可以找到。
#为什么要输入错的呢?因为如果正确的账户名密码点击登录会立刻进行重定向,就找不到这个接口了)
postUrl = "https://account.wxb.com/login?from=https%3A%2F%2Fdata.wxb.com%2FrankArticle%3Fcate%3D3%26page%3D1"
#模拟登录时带的帐户名,密码
postData = {
"email": "177*****043",
"password": "weixiaobao123",
}
#发送请求,得到响应体
response=requests.post(postUrl, data=postData, headers=header,verify=False)
#返回cookie
return response.cookies()
调用此方法便可返回登录后的cookie 注意:这个cookie是个RequestCookieJar的实例,而不是我们通常见到的字典类型。这也是这个办法用起来麻烦的地方
重点来了,plan C:
B中使用了response对象来返回cookie,然后每次用时需要再次设置cookies,但这样做起来显得很繁琐,下面介绍一个更简单的解决办法。
其实解决这个问题的主要方法就是维持同一个会话,也就是相当于打开一个新的浏览器选项卡而不是新开一个浏览器。但是我又不想每次设置cookies, 那该怎么办呢?这时候就有了新的利器 Session对象。
利用它,我们可以方便地维护一一个会话,而且不用担心cookies的问题,它会帮我们自动处理好。如下:
# -*- coding: utf-8 -*-
import requests, json
#注意:此get_heager方法是自己写的一个转换headers的方法。从网页上复制下来的headers是字符串型,而发送请求的headers是字典类型,为避
#免每次手动改,就索性写了个方法自动转换,返回字典型(懒果然是驱动进步的源泉啊 ~ ^.^)最后会送上。
from header import get_header
userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
header = {
'Referer': 'https://account.wxb.com/?from=https%3A%2F%2Fdata.wxb.com%2FrankArticle%3Fcate%3D3%26page%3D1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
def get_session():
postUrl = "https://account.wxb.com/login?from=https%3A%2F%2Fdata.wxb.com%2FrankArticle%3Fcate%3D3%26page%3D1"
postData = {
"email": "177*****043",
"password": "weixiaobao123",
}
#创建session对象
session = requests.session()
#使用Session对象发送登录请求,之后cookies就已经保存在Session对象中了
session.post(postUrl, data=postData, headers=header,verify=False)
header_str = '''Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Host: data.wxb.com
Referer: https://data.wxb.com/rankArticle?cate=-1&page=6
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
X-Requested-With: XMLHttpRequest
'''
#使用get_header()方法将字符串型转换为可以使用的字典型
header1 = get_header(header_str)
#返回携带cookies的Session对象和请求头
return session, header1
返回Session对象以后,就可以使用该对象访问想要访问的资源了。这是框架里的一部分调用了此方法:
def parse(self, response):
# 得到模拟登陆后的session对象和请求头
session, header = get_session()
for i in range(10):
url = 'https://data.wxb.com/rank/article?category=-1&page=' + str(i + 1) + '&pageSize=20&type=2&order='
res = session.get(url, headers=header, verify=False)
#将得到的数据序列化
lis = json.loads(res.text)
print(res.text)
for j in lis['data']:
item = WeixiaobaoItem()
read_num = j['read_num']
# print(read_num)
if read_num == u'10万+':
title = j['title']
account = j['account']
index_scores = j['index_scores']
url = j['url']
like_num = j['like_num']
item['title'] = title
item['account'] = account
item['index_scores'] = index_scores
item['read_num'] = read_num
item['like_num'] = like_num
item['url'] = url
yield item
最后,把headres转换方法以及cookies转换方法放在这里:
# -*- coding: utf-8 -*-
def get_header(str):
result = {} # 初始化返回结果
str_new = str.replace(': ', ':').replace(' ', '') # 第一步,将里面的冒号空格转换为冒号,然后消掉tab
str_list = str_new.split('\n') # 第二部,将字符串按行分割,可能会出现列表的第一个元素和最后一个元素为空字符串的情况
for i in str_list:
if i: # 做个筛选,过滤掉空字符串
# 此时的i是字符串,现在要将它转为键值对
temp = i.split(':')
key = temp[0]
value = ':'.join(temp[1:])
result[key] = value # 将键值对赋值给字典
return result
def get_cookie(str):
result = {}
str_new = str.replace(' ', '')
str_list = str_new.split(';')
for i in str_list:
if i:
temp = i.split('=')
key = temp[0]
value = ':'.join(temp[1:])
result[key] = value # 将键值对赋值给字典
return result
使用方法:
header_str='''Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: max-age=0
Connection: keep-alive
Host: www.baidu.com
Referer: https://www.baidu.com/s?ie=UTF-8&wd=linux%20crontab
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'''
# 返回字典
header = get_header(header_str)
cookie_str='''BIDUPSID=64C64E96A29EFCD95DBC15E7B3476840; PSTM=1527935009; BAIDUID=C5854F8A373874C0FDC858FB42BB5A3E:FG=1; __cfduid=db02ba44dd85fc27f9e77e1206c5aa4251539954648; BDUSS=hYc3hpUG1MOS16RS1TNlBTSDdSS2RwWUN1dzd5bWo2NWFrRHFOaGFvY1ZsfnRiQVFBQUFBJCQAAAAAAAAAAAEAAAC6YFstwqbKwMP0NTIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUK1FsVCtRbNU; BD_UPN=12314753; MCITY=-131%3A; BAIDUCUID=gavI8gaB28YEPvaAj8SA8juf2i0_i289_uvStguq2ii9avt_gavct_uH-t_OP2tDA; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_PSSID=26524_1454_25810_21101_28329_28414; BD_HOME=1; delPer=0; BD_CK_SAM=1; PSINO=2; sugstore=1'''
# 返回字典
cookie= get_cookie(cookie_str)