知识点:request 带密码登录、selenium+headless Chrome、pandas
要求:公司需要每个月一次的数据统计,需要从内网上下载excel文档,然后再筛选统计,最后汇出统计结果。每次都去下载,公司服务器还又有点 (特别)慢,可不可以把这个过程用 python 来实现呢?
思路:request 爬虫模拟登录,下载文档,再用 pandas 进行数据统计。
先小再大 一步一步 做大做强 再创辉煌!
先从模拟登录做起~
作战准备(分析)
目标网址(logined_url):http://dtsap.wpgholdings.com/WPGDTS/main/login.action ,平平无奇。
点击后若未登录(你肯定没有登录)会自动跳转到登录网址(login_url):http://login.wpgholdings.com/mysso/signon.php
简单,那我们就从这个网页来开刀!我的刀呢???按 F12 召唤大刀。刀长这样
不好意思拿错刀了,刀长这样。打码保命,防止被老板 gank。
在该网址下开始分析,选择 Network -->> 勾选 Preserve log(连续记录) -->> 按下左侧 Sign In 登录 -->> 右侧进行抓包,结果如下,在 Name 依次寻找,直到找到 Headers 下的 General 选项里的 Request Method 为 Post 的,如下。这个 Request URL 就是我们即将向他 post 表单数据的那个网址(post_url = ‘http://login.wpgholdings.com/mysso/authCheck.php’ )
拉到下面看 post 的表单信息(Form Data):
可以看到具体的 Form Data,待会儿就可以把这几个数据 post 给服务器就可以了。
那么问题来了,request_id 是什么鬼???不知道这个参数是干嘛的呀!看起来像是随机数?多试几次看看
那我先来多登录几次,多抓包几次,发现 request_id 是一个变化的内容,其他的几项都没变!
接下来我去原网页中去找看看有没有该项。打开登录网址(http://login.wpgholdings.com/mysso/signon.php )到登录界面,右键选择查看源代码,然后到源代码中搜索 request_id,发现还真有这个选项。BlingBlingBling~
那就一切好办了。分析完毕,开始撸代码!
强攻(撸代码)
废话少说,先来撸一段代码,看下结果。
# requests login wpi
import requests
from lxml import etree
headers = {
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Referer': 'http://login.wpgholdings.com/mysso/authCheck.php',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
}
login_url = 'http://login.wpgholdings.com/mysso/signon.php'
post_url = 'http://login.wpgholdings.com/mysso/authCheck.php'
r_login = requests.get(login_url, headers=headers) #到登录网址找 request_id 值
selector = etree.HTML(r_login.text) #创建一个select 选择器
token = selector.xpath('/html/body/form/input[8]//@value')[0] # 选择 request_id 值
post_data = {
'password':'******',# 密码,已打码
'request_id':token,
'username':'******',#用户名,已打码
'passwd_tmp':'',
'btnlogin':'Sign In',
}
r_login.encoding = 'utf-8'
# print(r_login.text)
s = requests.session() #保持登录状态,创建 session
r_post = s.post(post_url, data=post_data, headers=headers) #post 数据
print(r_post.url) #打印登录后的页面 url
结果是这样的,好像有点不太对。。
滑铁卢(反思)
没有成功,反思原因:
无从下手,不知道原因,这就尴尬了
模拟登录失败,听说有可能是 cookies 错了,我再去分析一下
下面那个红框是登录后的网址,其上面还有几项信息,我们逐一查看,发现第二个也是 post 方法,于是乎用这个网址试试,看一下他的 Form Data:
多了一个 OAM_REQ,是不是和 request_id 类似呢?果真,那我们再构建一个 post_url 试试。
再战(Debug)
将之前代码修改成第二版如下:
import requests
from lxml import etree
headers = {
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Referer': 'http://login.wpgholdings.com/mysso/authCheck.php',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
}
login_url = 'http://login.wpgholdings.com/mysso/signon.php'
post_url = 'http://login.wpgholdings.com/oam/server/auth_cred_submit'
# post_url = 'http://login.wpgholdings.com/mysso/authCheck.php'
r_login = requests.get(login_url, headers=headers)
selector = etree.HTML(r_login.text)
token = selector.xpath('/html/body/form/input[8]//@value')[0]
oam = selector.xpath('/html/body/form/input[9]//@value')[0]
post_data = {
'password':'******',
'request_id':token,
'username':'******',
'OAM_REQ':oam,
}
r_login.encoding = 'utf-8'
# print(r_login.text)
s = requests.session()
r_post = s.post(post_url, data=post_data, headers=headers)
print(r_post.url)
结果如下:
作了 2 处修改:post_url 和 post_data,其他基本不变,果然,成功。
暂时不知道为什么选第二个作为 post_url,望各位大佬告知。
结束了?还没!
感觉上面一套走下来,还是太累人了,作为自动化达人(懒人),有没有一套终极方法一键模拟登录呢,
还!真!有!
下面这个方法巨方便:
打开网址,f12 抓包,登录。找到网址是你登录后的网址(http://portal.wpgholdings.com/ )的那个选项,右键复制 bash 信息。
然后到 https://curl.trillworks.com/ 下,粘贴,右侧就会生成一段 Python 代码,复制下来就能运行,很是方便。这种方法还能防止你自己配置时 headers 出错(他帮你配好了),美中不足就是若有加密参数就很难受。
舒服!复制下来的代码如下(cookies 被我手动改了):
import requests
cookies = {
'COOKIE_SUPPORT': 'true',
'_ga': 'GA1.2.1059431120.1509670707',
'__utma': '32845816.1059431120.1509670707.1550979009.1551230824.172',
'__utmz': '32845816.1532486277.91.11.utmcsr=baidu|utmccn=(organic)|utmcmd=organic',
'Hm_lvt_798c5a1e4501fb3279c9a65c775a2613': '1550579337,1550802729,1550979011,1551230825',
'JSESSIONID': '***',#已打码
'GUEST_LANGUAGE_ID': 'zh_CN',
'LFR_SESSION_STATE_2824029': '1551340375509',
'checkbrowser': '0',
'NSC_FJQ': 'ffffffff090d1d5345525d5f4f58455e445a4a4229a0',
'OAMAuthnCookie_portal.wpgholdings.com:80': '***',#已打码
'NSC_ENA-SSO': '******',# 已打码
}
headers = {
'Accept-Encoding': 'gzip, deflate, sdch',
'Accept-Language': 'zh-CN,zh;q=0.8',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Referer': 'http://login.wpgholdings.com/mysso/authCheck.php',
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
}
response = requests.get('http://portal.wpgholdings.com/', headers=headers, cookies=cookies)
总结:
1、分清正确的 login_url 和 post_url,两者正常不一样,后者一般需要自己在开发者工具中分析出来
2、Form Data 中有不明参数需要自己去分析源码,找出参数的出处来代替
3、爬虫偷懒可以用 https://curl.trillworks.com/ ,挺方便的
4、登录失败,寻找原因,多从 post 数据,post 网址找原因
未完待续。。。
下期是用 Selenium+Headless Chrome 实战(https://blog.csdn.net/luckycdy/article/details/88036288 ),爬取同样同样内容。