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.htmlhttp://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

posted @ 2017-08-08 16:44  苏酒酒  阅读(127)  评论(0编辑  收藏  举报