注意:最新版本的selenium停止对PhantomJS的支持(可以使用谷歌&火狐的无头浏览器),如果还想用PhantomJS,需要对selenium降级
卸载最新版本:pip3 uninstall selenium
安装老版本:pip3 install selenium==3.8.0
Python网络爬虫中最麻烦的不是那些需要登录才能获取数据的网站,而是那些通过JavaScript获取数据的网站。python对javascript的支持不太好,所以就需要模拟浏览器。这个模拟浏览器跟Mechanize模块稍有不同,Mechanize模块并不支持javascript,所以这里需要一款可以模拟真实浏览器的模块---Selenium模块
浏览器选择
在编写Python网络爬虫时,主要用到Selenium的Webdriver, Selenium的Webdriver不可能支持所有的浏览器,也没有必要支持所有浏览器
webdriver支持列表
查看模块的功能,最简单也是最方便的方法就是直接使用help命令
代码语言:javascript复制from selenium import webdriver
help(webdriver)
运行结果:
PACKAGE CONTENTS
android (package)
blackberry (package)
chrome (package)
common (package)
edge (package)
firefox (package)
ie (package)
opera (package)
phantomjs (package)
remote (package)
safari (package)
support (package)
webkitgtk (package)
在上面列表中, android和blackberry是移动端的浏览器,移动端和PC端两码事,可以先忽略,剩下的有谷歌,ie,火狐,opera等等,而PhantomJS可能很多人没有听说过
PhantomJS是一个基于webkit的服务端JavaScriptAPI,它全面支持web而不需浏览器支持,其快速,原生支持各种web标准:DOM处理,CSS选择器,JSON,Canvas和SVG。PhantomJS可以用于页面自动化,网络监测,网页截屏,以及无界面测试等。事实上,在爬JavaScript才能返回数据的网站时,没有比Selenium和PhantomJS更适合的组合了
windows下安装PhantomJS
下载地址:http://phantomjs.org/download.html
进入下载页面后,选择windows版本的Phantomjs下载,如果是其它系统,对应下载版本安装就好
下载完成后,解压压缩包,直接将解压后的Phantomjs.exe复制到python的目录中就可以了,如下图
在python环境中测试一下,如下
代码语言:javascript复制#!/usr/bin/env python
# coding: utf-8
from selenium import webdriver
driver = webdriver.PhantomJS()
Selenium & PhantomJS抓取数据
Selenium和PhantomJS配合,可以模拟浏览器获取包括JavaScript的数据,现在不单要获取网站数据,还需要过滤出"有效数据"才行,Selenium本身就带有一套自己的定位过滤函数,它可以很方便地从网站返回的数据中过滤出所需的“有效数据”.
获取百度搜索结果
鉴于Selenium.Webdriver的help文件太大,分屏显示又不太方便,干脆将帮助文件保存到文件中慢慢查看,执行命令
代码语言:javascript复制#!/usr/bin/env python
# coding: utf-8
import sys
from selenium import webdriver
browser = webdriver.PhantomJS()
out = sys.stdout
sys.stdout = open('browserHelp.txt', 'w')
help(browser)
sys.stdout.close()
sys.stdout = out
browser.quit()
运行截图
想获取"有效信息",第一步当然是网站获取返回数据,第二步就是定位"有效数据"的位置,第三步就是从定位中获取“有效数据”.
以百度搜索为例,使用百度搜索"Python Selenium",并保存第一页搜索结果的标题和链接。从服务器返回数据,由PhantomJS负责,获取返回的数据用Selenium.Webdriver自带的方法page_source,例如:
代码语言:javascript复制from selenium import webdriver
URL = 'https://www.baidu.com'
browser = webdriver.PhantomJS()
browser.get(URL)
html = browser.page_source
print(html)
运行结果:
第二种方法:直接用selenium&PhantomJS打开百度的主页,然后模拟搜索关键字。直接从Selenium&PhantomJS中返回数据,使用第二种方法,可以很清楚地看到Selenium&PhantomJS获取数据的过程
执行代码:
代码语言:javascript复制from selenium import webdriver
browser = webdriver.PhantomJS()
browser.get('https://www.baidu.com')
browser.implicitly_wait(10)
print(browser.page_source)
执行结果
PS:关注一个函数implicitly_wait()。使用Selenium&PhantomJS最大的优势是支持JavaScript,而PhantomJS浏览器解释JavaScript是需要时间的,这个时间是多少并不好确定,当然可以用time.sleep()强行休眠等待一个固定时间.但是这个时间定长了,浪费时间;定短了,又没能完整解释JavaScript,而implicitly_wait函数则完美地解决了这个问题,给implicitly_wait一个时间参数,implicitly_wait会智能等待,只要解释完成了就进行下一步,完全没有浪费时间
下面从网页的框架中选取表单框,并输入搜索的关键词,完成搜索的过程
获取搜索结果
Selenium本身给出了18个函数,总共有8种方法从返回数据中定位“有效数据”位置,这些函数分别是:
代码语言:javascript复制find_element(self, by=By.ID, value=None)
find_element_by_class_name(self, name)
find_element_by_css_selector(self, css_selector)
find_element_by_id(self, id_)
find_element_by_link_text(self, link_text)
find_element_by_name(self, name)
find_element_by_partial_link_text(self, link_text)
find_element_by_tag_name(self, name)
find_element_by_xpath(self, xpath)
find_elements(self, by=By.ID, value=None)
find_elements_by_class_name(self, name)
find_elements_by_css_selector(self, css_selector)
find_elements_by_id(self, id_)
find_elements_by_link_text(self, text)
find_elements_by_name(self, name)
find_elements_by_partial_link_text(self, link_text)
find_elements_by_tag_name(self, name)
find_elements_by_xpath(self, xpath)
这18个函数前面的9个带element的函数将返回第一个符合参数要求的element,后面9个带elements的函数将返回一个列表,列表中包含所有符合参数要求的element.
上面函数中,不带by的函数,配合参数可以替代其他的函数.例如:find_element(by='id',value='abc')就可以替代find_element_by_id('abc')。同理find_elements(by='id',value='abc')也可以代替find_elements_by_id('abc').
这8种定位方法组合应用,灵活配合,可以获取定位数据中的任意位置。
在使用浏览器请求数据时,用find_element_by_name、find_element_by_class_name、find_element_by_id、find_element_by_tag_name会比较方便。一般的表单,元素都会有name,class,id,这样定位会比较方便。如果仅仅是为了获取“有效数据”的位置,那还是find_element_by_xpath和find_element_by_css比较方便,强烈推荐find_element_by_xpath,真的是超级方便
先定位文本框,输入搜索关键词并向服务器发送数据,在浏览器中打开百度,定位输入框查看代码,如下图:
从上图中可以看出文本框里有class,name,id属性,可以使用find_element_by_class_name、find_element_by_id、find_element_by_name来定位。执行命令:
代码语言:javascript复制testElement = browser.find_element_by_class_name('s_ipt')
testElement = browser.find_element_by_name('wd')
testElement = browser.find_element_by_id('kw') # 这三个任选其一都可以定位到
testElement.clear()
testElement.send_keys('Python selenium') # 在输入框中输入关键字
回到浏览器中,定位submit按钮,如下
从上图中看出,submit按键有id,class属性,可以用find_element_by_class_name和find_element_by_id来定位,执行命令:
代码语言:javascript复制submitElement = browser.find_element_by_class_name('bg s_btn btnhover')
submitElement = browser.find_element_by_id('su') # 这两个任选一个
submitElement.click()
print(browser.title)
代码:
代码语言:javascript复制from selenium import webdriver
browser = webdriver.PhantomJS()
browser.get('https://www.baidu.com')
browser.implicitly_wait(10)
testElement = browser.find_element_by_class_name('s_ipt')
testElement.send_keys('Python selenium')
submitElement = browser.find_element_by_id('su')
submitElement.click()
print(browser.title)
运行结果:
Python selenium_百度搜索
此时browser已经获取到了搜索的结果了
获取有效数据位置
获取“有效数据”位置或者说是element,先定位搜索结果的标题和链接。在浏览器打开百度搜索Python seleninum,在搜索结果页面中查看源代码
在这里发现了比较特别的属性class="c-tools"
发现共有10个结果,可以用find_elements_by_class_name定位所有的搜索结果了,执行命令
代码语言:javascript复制resultElements = browser.find_elements_by_class_name("result c-container ")
print(len(resultElements))
从位置中获取有效数据
有效数据的位置确定后,如何从位置中过滤出有效的数据呢?一般就是获取element的文字或者获取Element中某个属性值。
Selenium有自己独特的方法:
代码语言:javascript复制import json
resultElements = browser.find_elements_by_class_name("c-tools")
print(len(resultElements))
value = resultElements[0].get_attribute('data-tools')
valueDic = json.loads(value)
print(valueDic.get('title'))
print(valueDic.get('url'))
执行结果
10
Selenium with Python — Selenium Python Bindings 2 ...
http://www.baidu.com/link?url=CSU8JkNWTcCvLT0miYp8_frqdg7UTLGNjYJyv5cbc71oTDC_ZZNxUIbfu5bZa9Xu
完整代码如下:
遍历resultElements列表,可以获取所有的搜索结果的title和url,已将Selenium&PhantomJS爬虫运行了一遍
代码语言:javascript复制from selenium import webdriver
browser = webdriver.PhantomJS()
browser.get('https://www.baidu.com')
browser.implicitly_wait(10)
testElement = browser.find_element_by_id('kw')
testElement.send_keys('Python selenium')
submitElement = browser.find_element_by_id('su')
submitElement.click()
import json
resultElements = browser.find_elements_by_class_name("c-tools")
for i in resultElements:
value = i.get_attribute('data-tools')
valueDic = json.loads(value)
print(valueDic.get('title'))
print(valueDic.get('url'))
运行结果: