我们在Python做网络爬虫的时候,对于一些页面信息的爬取,很多信息网页会提示说需要登陆才能查看更多信息,这个时候我们就逃不开需要模拟登陆了,这里我们就可以利用上自动化测试神奇Selenium来模拟我们的鼠标、键盘进行登陆。这里我用我最近刚刚完成的一个网页来做讲解,也算是一种记录了。
本篇分两部分讲,第一部分介绍cookie值,第二部分介绍Selenium的相关方法以及操作
##第一部分:Cookie值
####1.什么是Cookie值:
当我们对网页发送请求时,对于那些需要浏览器需要登陆才能返回的信息,服务器如何去区分浏览器是否包登陆信息,然后分别返回对应的内容呢?这里就是我们要介绍到的Cookie值用处了。
cookie是由服务器发送到浏览器的变量。cookie 通常是服务器嵌入到用户计算机中的小文本文件。每当计算机通过浏览器请求一个页面,就会发送这个cookie。
####2.Cookie值有什么用
Cookie值被设计用来保存一些站点的用户数据,这样能够让服务器为这样的用户定制内容,后者页面代码能够获取到Cookie值然后发送给服务器。
下面以一个例子来说明Cookie值的用处
当我们希望对需要登陆才能访问的信息发送请求时,服务器如何知道浏览器发送的请求是否包含登陆信息,这里就需要利用到Cookie值,服务器通过检索浏览器发送来的Cookie值来判断,然后返回不同的内容。如果用浏览器的概念比较不好理解,那么接下来就用一个简单的代码来发送请求,并获取Cookie值,然后利用两种Cookie值(登陆后和没有包含登陆信息的Cookie值),来说明Cookie的用处
*我们知道想要获取浏览器的Cookie值,Python有一个包叫做http.cookiejar包,这个包可以用来获取浏览器的Cookie值,下面我直接对一个网页发送请求,然后获取一个网页的Cookie值,结果如下:
name: PHPSESSID -value: kn1n2iq9usj77davkmec4vi7a4
name: acw_tc -value: AQAAAKEGMFaK7wMA/X/oKsHdye4JAEWw
经过测试,利用这些Cookie值并不能返回给我登录后才能返回的信息。而且,在网页上利用浏览器的开发者选项(F12)查看Cookie值会发现,其他键值对都没有返回。
接下来是我利用Selenium模拟登录网页后获取的Cookie值:
['acw_tc=AQAAAF+W1SvfaAIA/X/oKtCgzhD+AeBV',
'_umdata=ED82BDCEC1AA6EB98EEDF380D0DDDD1DB0686BDCF0848311659A6108D5A46E3BE2754ADA65ACE593CD43AD3E795C914CBC0E61E5F537EFB039856DF64AF08E44',
'PHPSESSID=d1srrbvlt75qq5p46jeh8g7m23', 'hasShow=1',
'CNZZDATA1254842228=260499800-1518055240-%7C1518055240',
'_uab_collina=151805999511010799873236',
'zg_did=%7B%22did%22%3A%20%22161736d30a4cb-0c0aa0eefb862a-3b60490d-144000-161736d30a552e%22%7D',
'zg_de1d1a35bfa24ce29bbf2c7eb17e6c4f=%7B%22sid%22%3A%201518059991209%2C%22updated%22%3A%201518060013600%2C%22info%22%3A%201518059991270%2C%22superProperty%22%3A%20%22%7B%7D%22%2C%22platform%22%3A%20%22%7B%7D%22%2C%22utm%22%3A%20%22%7B%7D%22%2C%22referrerDomain%22%3A%20%22%22%2C%22cuid%22%3A%20%228731ad9f297a7dbbcff0814343e06ea3%22%7D']
这里可以看到登录后的Cookie值返回了所有键值对,经过测试,通过发送这里的Cookie值请求网页,可以实现模拟登录的效果。
*第二种我们可以从模拟浏览器的方法的入手,理解Cookie值的用处。我们知道用Python的Requests模块对网页发送请求可以用requests.get(url, params, headers, proxies, timeout .....)
当我们需要模拟浏览器登录的时候可以将header属性赋使用的浏览器User-Agent,例如:
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) \
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
或者更复杂一点的,把Request Headers下的所有属性都加上,例如:
request_headers = {
'Accept':'text/html, */*; q=0.01',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Cookie':'acw_tc=AQAAAAFSbEV6igQAU3joKmb1pHb7Amkd;_umdata=2BA477700510A7DF4059C61C78E8395939FAD577E94352328D751D6CD84A71DC483C7165B9EADDACCD43AD3E795C914C9155FE5460049F4934F91027D34B6E6F;PHPSESSID=lrcjhpkrjtbffirbtch8hrh9g2;hasShow=1;UM_distinctid=1616afff050228-00a2c5f077a432-3b60490d-144000-1616afff052387;CNZZDATA1254842228=1796631317-1517916172-%7C1517916172;_uab_collina=151791861517128939720221;zg_did=%7B%22did%22%3A%20%221616afff15d69f-06e4c98572085c-3b60490d-144000-1616afff15e48b%22%7D;zg_de1d1a35bfa24ce29bbf2c7eb17e6c4f=%7B%22sid%22%3A%201517918613857%2C%22updated%22%3A%201517918631284%2C%22info%22%3A%201517918613860%2C%22superProperty%22%3A%20%22%7B%7D%22%2C%22platform%22%3A%20%22%7B%7D%22%2C%22utm%22%3A%20%22%7B%7D%22%2C%22referrerDomain%22%3A%20%22%22%2C%22cuid%22%3A%20%228731ad9f297a7dbbcff0814343e06ea3%22%7D;',
'Host':'www.qichacha.com',
'Referer':'http://www.qichacha.com/firm_06396efe66551d4ac07ee8cb41b0e325.html',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36',
'X-Requested-With':'XMLHttpRequest'
}
把所有键值对用字典的形式保存下来,然后赋给headers属性。
那么这两种方法有什么不同用处和区别呢?当时我们在做淘宝评论爬虫的时候也遇到过需要登陆的问题,当时发现只用User-Agent没有用,仍然需要登录,于是我就在PC浏览器端手动登录,然后想着希望程序能模拟浏览器,就把Request Headers下的属性值像上面那样构造成字典,赋给header属性,结果真的可以了。当时我还以为程序真的能检测到我在PC端浏览器的操作这么神奇。。。当时没有管那么多,就一直这样做了。
但是这次在做这次的爬虫时,我才体会到其实程序的确’检测’了我在PC端浏览器的操作,只是这种方式是利用Cookie值。当时我直接复制了浏览器下的Request Headers下的内容,那么这里的Cookie值就对应了我PC端浏览器访问网页后,保存在本地的小文件里的一些用户信息(对应前面说的什么是Cookie值)。
当我通过浏览器访问站点时,浏览器会在个人电脑上行查找和该站点对应的Cookie文件,如果发现了就会把里面的键值对内容全部发送给Web服务器,如果没找到则不发送。所以也就是说当我在程序中利用PC浏览器端保存的Cookie值进行访问时,这个Cookie值其实记录和保存了的是我在浏览器端登录后保存在本地文件的Cookie值,每次我在PC端浏览器登录后再请求站点时,浏览器就会在我的电脑上找到该站点登录后对应的Cookie值。所以如果我想在程序中利用该Cookie值模拟登录的效果,那么就必须在浏览器上登录后,对应站点的Web服务器才能获取到该Cookie值,并告知该Cookie包含登录信息,有效。否则,程序只单独利用该Cookie值,不能取得登录效果。所以这也就解释了,为什么当时我会以为程序’神奇‘的真的模拟了我的PC端浏览器。
######而且通过实验也可以发现,对于大部分的网页,Request Headers下的属性,只有Cookie属性和User-Agent是常常利用比较多的。所以说了那么多,其实简而言之,就是希望大家能理解,对于那些站点需要登录才能获取的信息,我们需要构建Cookie值,并携带登录后的Cookie值发送请求,这样才能获得对应信息。
那么如何才能获得登录后的Cookie值呢?而且要知道,对于不同的电脑和不同的浏览器,Cookie值都是不相同的,如果你想你的携带了Cookie值的爬虫程序可以在不同的电脑上都能运行,那么就不能像上面那样,只是把你自己浏览器下的Cookie值复制过来,然后携带发送请求,因为这样你的程序换一台电脑之后,就无法实现模拟登录的效果了。所以,如果希望我们的程序繁华能力更强,那么我们就一定要让我们的程序能自己输入账号密码进行登录,然后自动获取Cookie值,并让程序携带Cookie值发送请求。
####利用Selenium模拟登录
如何你百度如何模拟浏览器登录,那么你肯定能看到这个模块。Selenium是一个网页自动化测试的神器,它可以通过简单的函数,就能模拟你的鼠标、键盘等的操作,例如输入账号密码,点击、拖拽等等功能,可谓是简单高效。
具体相关的API我就不过多介绍了,这里有篇博客介绍的还是比较详细的,以及Selenium的官方中文翻译文档
python中seleniumAPI的用法
Selenium with Python中文翻译文档
这里我就主要介绍如何来模拟登录,如何处理登录时需要拖动滑块进行验证的情况。
输入账号密码都比较简单了,拖动滑块的话,就需要用到move_by_offset(xoffset, yoffset)这个函数了,参数故名思意就是横向和纵向所要移动的距离。那拖动滑块自然就只需要设定x的值。
这个值要去哪儿获取呢?这时我们可以打开浏览器的开发者选项(F12),然后找到滑块对应的大小
当然,有点数学知识的都可能感觉到不对呀,那滑块也是有大小的呀,真正的拖动距离应该是348-滑块的长才对。所以我们可以用同样的方法对应到滑块的大小,然后相减。(当然其实我们拖多了也是可以的,例如直接设置成400)。
就这样就OK了,就是这么简单神奇,当然网页也并不会让你这么简单就破解。我觉得Selenium比较不好的就是,这个函数不能设定拖动速度,每次拖动完可能是因为拖动速度太快,总是会需要再输入验证码(当然可能某些网站不需要,那就最好了)。那就再需要找到对应的验证码的png图片然后识别,分割之类的了,这里就没有办法再讨论了,(我也不会。。)
所以,以后当我们遇到需要登录的情况时,就可以利用Selenium来实现了~Selenium支持多款浏览器,这里我用的是Selenium+Chrome,所以就需要下载一个chromedriver.exe的驱动程序。
这里贴出chromedriver下载的官方网址,可自行下载
chromedriver
代码如下:
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
import re
acw_tc_re = re.compile('acw_tc')
phpsessid_re = re.compile('PHPSESSID')
acw_php_re = re.compile('acw_tc|PHPSESSID')
path = 'E:\Anaconda3_4.4.0_Windows_x86_64\chromedriver_win32\chromedriver.exe'
#url = 'http://www.qichacha.com/firm_06396efe66551d4ac07ee8cb41b0e325.html'
login_url = 'http://www.qichacha.com/user_login'
url2 = 'http://www.qichacha.com/'
class Get_Cookies(object):
def __init__(self, path, url):
self.path = path #chromedriver驱动程序的绝对路径地址
self.url = url #访问站点
def extract_cookie(self):
cookies = ''
driver = webdriver.Chrome(self.path) #定义对象
driver.get(self.url) #请求网页
denglu = driver.find_element_by_link_text('登录')
action = ActionChains(driver) #定义需要鼠标键盘类动作
denglu.click() #模拟鼠标点击右上角的登录
curpage_url = driver.current_url #返回当前界面的url
print(curpage_url)
while(curpage_url == login_url): #进行循环判断,直到当前界面发生切换,才说明登录成功
username = '****' #账号
password = '*******' #密码
nameNormal =driver.find_element_by_id('nameNormal') #找到输入账号的文本框
pwdNormal = driver.find_element_by_id('pwdNormal') #找到输入密码的文本框
nameNormal.clear()
nameNormal.send_keys(username) #写入账号
time.sleep(3)
pwdNormal.clear()
pwdNormal.send_keys(password) #写入密码
while True: #防止因为网络不稳定,导致滑块没有加载出来,就会报错。(根据自己的实际情况)
try:
huakuai = driver.find_element_by_id('nc_1_n1z') #根据id值找到滑块的位置
action.click_and_hold(huakuai) #按住滑块
action.move_by_offset(100,0).perform() #先拖动滑块100
time.sleep(1)
action.move_by_offset(100,0).perform()
time.sleep(1)
action.move_by_offset(108,0).perform()
break
except:
pass
tongguo_xpath2 = '//*[@id="nc_1__scale_text"]/span[@data-nc-lang="_yesTEXT"]'
login_in_xpath = '//*[@id="user_login_normal"]/button'
while True:
try:
text = driver.find_element_by_xpath(tongguo_xpath2) #直到拖动滑块验证完成后,条形框上显示‘验证通过’
break
except:
pass
login_in = driver.find_element_by_xpath(login_in_xpath)
login_in.click()
time.sleep(3)
curpage_url = driver.current_url
#get the session cookie
cookie = [item['name'] + '=' + item['value'] for item in driver.get_cookies()]
print(cookie)
for value in cookie:
#m = acw_php_re.search(value)
#if m:
#cookies += value + ';'
cookies += value + ';'
print(cookies)
return cookies
#cookie = Get_Cookies(path, url2)
#cookie.extract_cookie()
最后提供几个类似的用Selenium攻破验证码的博客
python2.7+selenium2实现淘宝滑块自动认证
滑块验证码(滑动验证码)相比图形验证码,破解难度如何?
Python爬虫常用之登录(二) 浏览器模拟登录
使用Selenium模拟浏览器,实现自动爬取数据