前言
最近学习Python模拟登陆相关的东西,副产品就是样。
效果:定时登陆DFRobot并查询是否有人回复,有则led闪烁+蜂鸣器响。
硬件:Lemaker Hikey 开发板;LED module;Buzzer module;杜邦线若干。
软件环境:windows 7 x64;Archlinux|Raspbian;Python3;phantomjs;Fiddler;Chrome
1、基础知识
之前在我的inoreader订阅里读到这么一篇文章:手机APP自动签到—python实现,既往只用过Python+RPi.GPIO库去控制树莓派的引脚,现在发现了Python的其他有趣的应用如Web、神经网络、大数据之类,求知若渴。
通过上述文章,了解到Fiddler这款软件和requests库。断断续续学了很多,知乎、微信公众号上面一大堆,一直不得入门,这次突发奇想(懒癌发作)用手头的Lemaker Hikey自动模拟登录DFRobot论坛,然后定时查看一下有木有新提醒,有的话就发微信 OR 邮件通知我。
说干就干,建议在此之前先阅读以下文章:
看不太懂没关系,大致有个概念就行。此外文中介绍的方法有些已经失效,但是看完作者的文章,再阅读作者的开源项目smart_login代码,相信会和我一样有茅塞顿开,醍醐灌顶之感。
一般来说:
- Fiddler/HTTPanalyzer/Charles用来抓包,了解正常登陆流程
- Chrome调试工具用来查看网络资源
- phantomjs等无界面浏览器用来模拟真实登陆
- selenium结合Python用来操作phantomjs浏览器(建议以后直接操作更高效)
- reuqests包用来模拟发送包
2、 分析DFRobot网站登陆
看完上述的文章,稍微上手之后,我们来看看df网站如何登陆的。使用fiddler抓包:
可以看到登陆过程中每次response都会改动cookies(安装Privacy scanner拓展可以看到),而且用chrome插件EditThisCookie
可以看到cookie内容非常丰富,会包含登陆ip、登陆地址、上次登陆时间等等内容。登陆不需要验证码。
登陆时post的数据如上,有两个值:loginhash和formhash,每次登陆都会变。hash值不可能凭空生成,有三种可能情况:1 是藏在页面中的;2 是通过服务器返回的;3 是通过运行javascript脚本生成的。我们通过chrome调试模式(F12)和搜索分析Fiddler会话之后认为该值是登陆时服务器返回的。
那么整个过程就比较清晰了:
难点:hash
值以及复杂的cookies。
解决思路:phantomjs模拟登陆+保存cookies。
以上方案都是基于dfrobot登陆需要验证码的前提下,所以最初是研究怎么获得登陆后的cookies,后来发现不需要验证码,但是沿用了这一思路。其实直接用requests库登陆理论上也是可以的(解决hash值,post的时候要对应qd)
注意:如果使用手动输入网址进行抓包,每次一定要清理缓存,不然有的请求被缓存起来了,抓包是看不到的。
3、开始动工——登陆
我这里先以Windows下进行演示。开发环境配置包括以下步骤:
- 下载安装最新版Python3
- 安装相关依赖库:
python -m pip install lxml selenium
- 下载安装phantomjs,记住安装路径如
D:\Program Files (x86)\phantomjs\bin\phantomjs.exe
- 配置编辑器,以
Visual Studio Code
为例。Ctrl+Shift+X
安装python
拓展。
核心代码如下:
# -*- coding: utf-8 -*-
__author__ = 'Code_Paintium'
# Date:2017-9-14
# Filename:DFRobot vLogin
from selenium import webdriver
import pickle, time, requests, pathlib
from lxml import etree
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class df_warmup():
# 用户名、密码登陆
def __init__(self, account_num, passwd_str):
self.account_num = account_num
self.passwd_str = passwd_str
self.url = 'https://mc.dfrobot.com.cn/member.php?mod=logging&action=login'
self.path = pathlib.Path('cookies.df')
self.proxies = {
"http": "http://127.0.0.1:8888"
}
self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'}
self.df = requests.session()
if not self.path.exists():
print('cookies not found, generating...')
self.GetCookies()
# 运行一次就会生成,防止死循环
# 调用phantomjs获取cookies:
def GetCookies(self):
self.driver = webdriver.PhantomJS(executable_path=r'D:\Program Files (x86)\phantomjs\bin\phantomjs.exe')
self.driver.get(self.url)
wait = WebDriverWait(self.driver, 10)
wait.until(EC.presence_of_element_located((By.NAME, "loginsubmit"))).click()
self.account = self.driver.find_element_by_xpath('//input[starts-with(@id, "username_")]')
self.account.clear()
self.account.send_keys(self.account_num)
self.passwd = self.driver.find_element_by_xpath('//input[starts-with(@id, "password3_")]')
self.passwd.clear()
self.passwd.send_keys(self.passwd_str)
self.remember_me = self.driver.find_element_by_name('cookietime')
self.remember_me.click()
self.click_button = self.driver.find_element_by_name('loginsubmit')
self.click_button.click()
wait = WebDriverWait(self.driver, 10)
wait.until(EC.presence_of_element_located((By.XPATH, "//*[@id='pm_ntc']")))
pickle.dump(self.driver.get_cookies(), open("cookies.df", "wb"))
self.driver.save_screenshot("df.png")
self.driver.quit()
df = df_warmup('username', 'password')
df.GetCookies()
相关知识:
基本我踩过的坑都写在上面了。至此,我们使用python+selenium以及phantomjs获取了一次完整登陆的cookies文件。参考Selenium Webdriver传递Cookies给requests及持久化 ,requests库使用cookies的方法如下:
import pickle
import requests
cookies = pickle.load(open("cookies.pkl", "rb"))
s = requests.session()
for cookie in cookies:
s.cookies.set(cookie['name'], cookie['value'])
我们可以get一下home主页,看看服务器返回内容来确定cookies是否正确。