python selenium的使用、文件下载、登录跳转问题和简单封装
这几天折腾selenium,折腾的够呛,我们拿穷游网来举例吧,起因是因为要下载穷游网所有的旅游锦囊,它的文件是PDF格式的,点击这个按钮即可下载,但有个前提,它需要登录,于是就拿手机注册了一个
起初为了方便,我是打算用phantomjs+selenium这个组合的,但是折腾了半天,忘了在哪看到的,我发现原来phantomjs是不支持非html文件下载的,需要可以看到的浏览器,如果你想要phantomjs,可能还需要用到capsterjs,但是我不打算用,主要是测试了一下也没有成功。另外说一句,新版的selenium已经不支持phantomjs了,所以大家不要白费力去折腾这个。
于是还是去下了chromedriver,我用的是chrome浏览器,如果你用的是Firefox,那么需要下载对应的驱动,目前大多数人就这俩,也够用了。
附上下载地址:https://chromedriver.storage.googleapis.com/index.html?path=2.31/,我是Windows,于是就下了那个Windows32,起初我也有些犹豫,因为我是64位,但是并没有什么关系。
环境及工具:Python2.7+Windows +Selenium+Chrome+ChromeDriver
首先,我们进穷游网的首页,看到这里
这里有所有锦囊,起初我以为是通过js的方式显示的,但一看源代码,它是通过ul/li/dt/dd/a组合的,他们的url就在a标签的href里,这里只要稍微用requests+beautifulsoup4就可以弄下来。
为什么我们要通过这种方式呢?我们随便点进一个锦囊,它的url是这样的,http://guide.qyer.com/taste-of-los-angeles/,首页的url+锦囊的名字组成的,那这样我们就不能遍历了,那就只能从首页下手了。
并且用这种方式还不需要考虑下一页的问题,它几乎都显示在了源代码里。
import requests
from bs4 import BeautifulSoup
headers = {
'Host':'guide.qyer.com',
'Accept':
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.8',
'Connection':'keep-alive',
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
}
def get_url():
url = 'http://guide.qyer.com'
req = requests.get(url,headers=headers)
soup = BeautifulSoup(req.content,'lxml')
gui_nav = soup.find('ul',{'class':'gui_nav'}).find_all('a')
urls = [nav['href'] for nav in gui_nav]
return urls
我把selenium要用到的地方写成了一个类,至于获取url这个函数,爱放到里面也可以,无所谓,我放在了外面。之所以用requests+beautifulsoup4这个组合,而不是一起用selenium,是因为前者更简单,我写的也顺手,只要几行就可以,如果不是按钮和下载问题,我其实是不愿意用selenium的,但是用着用着也就习惯了
然后我们要来分析一下chromedriver的下载方式,这里我参考的是http://www.jianshu.com/p/b03ef6ffc4a5这篇文章,写的很好,里面也有Firefox的方法
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
# 这个其实是selenium启动chrome的一些配置文件需要的
options = webdriver.ChromeOptions()
# 这个是添加user-agent
options.add_argument("user-agent='Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36'")
# 这个就是设置下载文件的一些参数,第一个参数是是否弹出窗口,设置为 0 禁止弹出窗口,第二个参数是设置下载路径
prefs = {'profile.default_content_settings.popups': 0, 'download.default_directory': 'C:\\Users\\Administrator\\Desktop\\jijian-intern\\guide\\pdf'}
options.add_experimental_option('prefs', prefs)
# 这是你下载chromedriver要放的路径,用过chrome的童鞋都知道,chrome默认安装的位置在C盘,所以我们要把驱动放在这个路径下面,如果是win7,那么你的chrome所在位置应该与我相同,我看到很多方法,但测试只有这种将驱动路径写在代码里的有用
chromedriver = "C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe"
# 这里就已经可以让浏览器自动操作了,第一个参数是驱动的路径,第二个是前面文件下载位置的一些配置
driver = webdriver.Chrome(executable_path=chromedriver, chrome_options=options)
# 最大化窗口,这一步其实很有必要,可以省去很多不必要的麻烦,因为窗口打开的宽度可能把你要的地方盖住了
driver.maximize_window()
然后就是点击下载了:
通过审查我们可以发现那个免费下载的span标签有个样式名是down jsdownaction,它中间是有空格的时候,但是我测试的时候出错,网上查了一下,是不允许有空格的,那么有两种方法,一种是用空格,一种是随便取一个,查看它是否是唯一的,我这里测试,down是唯一的,点击就可以下载了,前面已经设置了不会弹出下载框和下载文件的位置:
driver.get(url)
driver.find_element_by_class_name("down").click()
还有一个问题就是登录了,一般有两种情况,一种是点击的时候弹出一个窗口要你登录,还有一种就是会跳转到另一个页面,这个时候建议第二种,一般的网站都应该会有一个登录页面的吧,这里也是,两种登录窗口都有,我的就是第二种了。
我之前也是很懵的,查了半天有各种各样的回答,也没见有个说清的,
这里测试了一下,使用driver.current_window_handle
就可以获取或者说定位到跳转之后那个页面。
同样,还有一个关于标签页的问题,就是下载完一个页面之后,我要关闭它,然后打开一个新标签页,要用到ActionChains,不建议使用driver.close(),它会关掉整个窗口,并且,关浏览器是要等到所有都完成才用的,我们操作标签页就好了,它的用法大家可以去查一下,很有用,是关于鼠标键盘操作的
from selenium.webdriver.common.action_chains import ActionChains
# 通过Ctrl+T打开一个新标签页
ActionChains(self.driver).key_down(Keys.CONTROL).send_keys("t").key_up(Keys.CONTROL).perform()
# 通过Ctrl+W关闭一个标签页
ActionChains(self.driver).key_down(Keys.CONTROL).send_keys("w").key_up(Keys.CONTROL).perform()
完整代码:
# -*- coding:utf-8 -*-
import sys
import time
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
# 我使用的是python2.7,所以可能会有编码问题,python3可省略
reload(sys)
sys.setdefaultencoding('utf8')
headers = {
'Host':'guide.qyer.com',
'Accept':
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.8',
'Connection':'keep-alive',
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
}
def get_url():
url = 'http://guide.qyer.com'
req = requests.get(url,headers=headers)
soup = BeautifulSoup(req.content,'lxml')
gui_nav = soup.find('ul',{'class':'gui_nav'}).find_all('a')
urls = [nav['href'] for nav in gui_nav]
return urls
class Login(object):
def __init__(self):
# 这里唯一一个需要被使用到的就是driver,所以初始化
options = webdriver.ChromeOptions()
options.add_argument("user-agent='Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36'")
prefs = {'profile.default_content_settings.popups': 0, 'download.default_directory': 'D:\\guide\\pdf'}
options.add_experimental_option('prefs', prefs)
chromedriver = "C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe"
self.driver = webdriver.Chrome(executable_path=chromedriver, chrome_options=options)
self.driver.maximize_window()
def login(self,username,pwd):
# 穷游网的登录页
url = 'https://passport.qyer.com/login?refer=http://guide.qyer.com%2Fjeju%2F'
# 智能等待,比time.sleep的方法好一些
self.driver.implicitly_wait(5)
# 这里我试了几种方法,前两种都没有成功,我不知道为什么,但是事实证明选择比较独特的一些类似id,tag这种会比较好,class_name和css_selector这些都比较广泛,xpath我也试过,都没成功
# inputs = self.driver.find_elements_by_class_name("input")
# inputs = self.driver.find_elements_by_css_selector("input")
# input框总共有七个,find_elements是查找所有,第一个input输入框是输入账号的,第4个是输入密码的,然后通过send_keys的方式将参数传入
inputs = self.driver.find_elements_by_tag_name("input")
# 将用户名输入第一个输入框
inputs[0].send_keys(username)
# 因为可能打开网页的速度可能不好,这里非常建议执行不同步骤直接设置智能等待,这是隐式等待,还有一种是显示
self.driver.implicitly_wait(5)
# 将密码输入第4个输入框
inputs[3].send_keys(pwd)
self.driver.implicitly_wait(5)
# 点击按钮,可以通过click,也可以通过send_keys的方式,
# self.driver.find_element_by_class_name("btn").send_keys(Keys.ENTER)
self.driver.find_element_by_class_name("btn").click()
# driver.current_window_handle是切换到当前位置,因为登录完会跳转到首页,我们需要定位,就会定位到首页
# 这里返回它主要是为了后面切换窗口,请看new_tab函数
old_tab = self.driver.current_window_handle
return old_tab
# 这里是下载的函数,这里的url就是外面刚刚获取的每个锦囊的url
def get_download(self,url):
self.driver.get(url)
self.driver.find_element_by_class_name("down").click()
self.driver.implicitly_wait(10)
# 这里我们又需要定位,因为下载完之后又会弹出一个框,我们必须选中它,然后点击关闭按钮,才能继续下一个
self.driver.current_window_handle
# 关闭弹出框按钮
self.driver.find_element_by_class_name("ui_pupBox_close").click()
# 然后我们打开一个新标签页,我们知道chrome浏览器,关掉最后一个标签页就会关闭浏览器,这里就是以防下载完关闭标签页后会退出浏览器
self.driver.find_element_by_tag_name("body").send_keys(Keys.CONTROL + "t")
def new_tab(self):
# 我们定义一个打开新标签页的函数,这里就是使用Ctrl+T的方法打开新标签页
ActionChains(self.driver).key_down(Keys.CONTROL).send_keys("t").key_up(Keys.CONTROL).perform()
# 这里我们获取上一个标签页
old_tab = self.login()
# 使用switch_to_window切换标签页,切换到刚刚那个标签页,这里就是防止浏览器被关闭
self.driver.switch_to_window(old_tab)
# 这一步很重要,刷新
self.driver.refresh()
def close_tab(self):
# 关闭标签页,和上面差不多,Ctrl+T是打开新标签页,Ctrl+W是关闭
ActionChains(self.driver).key_down(Keys.CONTROL).send_keys("w").key_up(Keys.CONTROL).perform()
self.driver.refresh()
def tearDown(self):
# 等待一会再关闭浏览器
self.driver.implicitly_wait(5)
self.driver.quit()
if __name__ == '__main__':
username = 'XX'
pwd = 'xxx'
# 取名叫Login不太好,本来只是为了封装登录的类,但是后来写一起了,就没改
login_test = Login()
login_test.login(username,pwd)
urls = get_url()
for url in urls:
login_test.get_download(url)
# 下载完当页就关闭该页,然后又会打开新的
login_test.close_tab()
# 下载完所有的再关闭浏览器
login_test.tearDown()
我写的很详细了,一眼看过去注释都比代码多
以下是我正在爬取的浏览器窗口和爬下来的截图:
这个是selenium自动操作浏览器,可以看到下面自动在爬取
这个是我下载到本地PDF的截图:
另外,附上我查看参考的一些博客或者文档:
0、本文重点,文件下载,讲得非常清楚,有chrome和Firefox的,还有文件上传:http://www.jianshu.com/p/b03ef6ffc4a5
1、这篇就是窗口切换的,使用ActionChains进行键盘鼠标操作就是在这里看到的,http://blog.csdn.net/xm_csdn/article/details/53395900
2、这两篇主要是一些元素选取基本的使用,后面那篇比较详细,讲到了键盘鼠标操作,http://blog.csdn.net/c406495762/article/details/72331737#3135-添加user-agent,http://www.cnblogs.com/eastmount/p/4810690.html
3、这篇主要是Firefox还有一些鼠标键盘操作的一些使用,http://blog.csdn.net/u011159607/article/details/56288878
4、这篇是将cookie的,它其实还是需要模拟登录,然后获得cookie,我尝试在浏览器上找cookie信息,并没有发现name和value这两个值,测试的时候发现它需要selenium操作才会有,不管是否登录它都有,不过这里我没有用到
http://www.cnblogs.com/fnng/p/3269450.html
5、这篇讲到selenium一些设置的方法,如user-agent,cookie,主要是phantomjs和Chrome的,像add_argument这个方法,只有浏览器才有,在phantomjs是没有的,所以浏览器设置user-agent会比较方便,https://www.zhihu.com/question/35547395
6、登录封装有参考这两篇,不过它们都用到了测试单元,我们可以不需要,自己写个类,照样可以调用,我也没有去做是否成功登录的相关验证,因为暂时只需要自己用于一个完整的爬取项目,只要能成功,就足够了,http://www.cnblogs.com/cnkemi/p/6228137.html,http://www.cnblogs.com/yoyoketang/p/6576668.html,这两篇大体上是相同的,不同的是SetUp那里,因为我没继承测试单元这个类,所以这个地方我改成了init,然后把基本不会变的都放在了那里,包括文件配置
7、这篇也是讲文件下载的,两个浏览器的都有,虽然不及第一篇那么详细,但是简洁明了,不过它这个例子是下载的那个地方直接写了会弹出什么,而我们碰到的可能是<a href="javascript:void(0)">
这样的,也没有关系,我们只要找到那个元素,然后给它一个点击事件就可以了,https://huilansame.github.io/huilansame.github.io/archivers/how-to-download-files-with-selenium
8、在解决窗口这个问题的时候,我看到很多提到window_handles这个的,但是这个多个窗口也许和我碰到的不是同一个,我也没能成功的切换,有兴趣的朋友可以试一试,这篇它有说明句柄对应的顺序,似乎和我们想象的不一样,并不是按顺序http://www.cnblogs.com/paisen/archive/2013/08/29/3289708.html
9、如果有多前面提到的CasperJS有兴趣可以看看这篇,和phantomjs一起使用,http://blog.csdn.net/eastmount/article/details/47023199
10、这篇讲的主要是Selenium + PhantomJS + python ,不过也挺有用的,之前一直是通过截图的方式查看操作:http://www.cnblogs.com/luxiaojun/p/6144748.html
11、最后一个就是官网了,老实说没怎么用到,因为搜索问题的时候没有查到都去看博客去了,http://selenium-python-zh.readthedocs.io/en/latest/index.html
本文来自博客园,作者:苏酒酒,转载请注明原文链接:https://www.cnblogs.com/sujiujiu/p/15370027.html