Selenium和PhantomJS配合,可以模拟浏览器获取包括JavaScript的数据。不单要获取网站数据,还需要过滤出“有效数据”。Selenium本身带有一套自己的定位过滤函数。它可以很方便地从网站返回的数据中过滤出所需的“有效数据”。
Python资源共享群:626017123
1、获取百度搜索结果
想知道Python模块最详细的用法,直接用help函数就可以了。鉴于Selenium.Webdriver的help文件太大,分屏显示又不那么方便,干脆将帮助文件保存到文件中慢慢查看。执行命令:
python
from selenium import webdriver
import sys
browser=webdriver.PhantomJS()
out=sys.stdout
sys.stdout=open(browserHelp.txt,w)
help(browser)
sys.stdout.close()
sys.stdout=out
browser.quit()
exit()
一定要加上browser.quit(),否则cmd.exe在执行exit时会无法退出。
执行结果如图1所示。
图1 获取help文件
想获取“有效信息”,第一步当然是网站获取返回数据,第二步就是定位“有效数据”的位置,第三步就是从定位中获取“有效数据”。
以百度搜索为例,使用百度搜索“Python Selenium”,并保存第一页搜索结果的标题和链接。从服务器返回数据,由PhantomJS负责,获取返回的数据用Selenium.Webdriver自带的方法page_source,例如:
from selenium import webdriver
browser=webdriver.PhantomJS()
browser.get(URL)
html=browser.page_source
有两种方法可以得到搜索结果页面。第一种,百度主页还是使用get方式上传request。这里可以先找一个浏览器,打开百度后搜索关键词。再把返回来的搜索结果的URL保存下来用Selenium&PhantomJS打开,再获取返回的数据。第二种,直接用Selenium&PhantomJS打开百度的主页,然后模拟搜索关键词。直接从Selenium&PhantomJS中返回数据。这里使用第二种方法,可以很清楚地看到Selenium&PhantomJS获取数据的过程。
第一步获取搜索结果。打开cmd.exe,准备好环境。执行命令:
python
from selenium import webdriver
browser=webdriver.PhantomJS()
browser.get(https://www.baidu.com)
browser.implicitly_wait(10)
执行结果如图2所示。
图2 模拟百度搜索
这里要关注一个函数implicitly_wait()。使用Selenium&PhantomJS最大的优势是支持JavaScript,而PhantomJS浏览器解释JavaScript是需要时间的。这个时间是多少并不好确定,当然可以用time.sleep()强行休眠等待一个固定时间。可这个固定的时间定长了,浪费时间;定短了,又没能完整地解释JavaScript。Implicitly_wait函数则完美地解决了这个问题,给implicitly_wait一个时间参数。Implicitly_wait会智能等待,只要解释完成了就进行下一步,完全没有浪费时间。下面从网页的框架中选取表单框,并输入搜索的关键词,完成搜索的过程。
2、获取搜索结果
第二步定位表单框架或“有效数据”位置,可以用import导入bs4来完成,也可以用Selenium本身自带的函数来完成。Selenium本身给出了18个函数,总共8种方法从返回数据中定位“有效数据”位置。这些函数分别是:
find_element(self,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=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。命名是9个函数,为什么只有8种方法呢?上面函数中,不带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,真的是超级方便。
先定位文本框,输入搜索关键词并向服务器发送数据。在Chrome中打开百度主页,查看源代码页面(如果想全程无GUI,也可以直接在Selenium中用page_source获取页面代码,保存后再慢慢搜索,不过这样就比较麻烦了)。在源代码页面搜索type=text,也就是查找页面使用的文本框,搜索结果如图3所示。
图3 搜索文本框位置
从图3可以看出文本框里有class、name、id属性,可以使用find_element_by_class_name、find_element_by_id、find_element_by_name来定位。执行命令:
textElement=browser.find_element_by_class_name(s_ipt)
textElement=browser.find_element_by_id(kw)
textElement=browser.find_element_by_name(wd) #这三个任选其一都可以
textElement.clear()
textElement.send_keys(Python selenium)
回到Chorme中百度源代码页面,搜索type=submit,定位submit按键位置,如图4所示。
图4 搜索submit按键
从图4可看出,submit按键有id、class属性,可以用find_element_by_class_name和find_element_by_id定位。执行命令:
submitElement=browser.find_element_by_class_name(btn self-btn bgs_btn)
submitElement=browser.find_element_by_id(su) #这两个任选一个
submitElement.click()
print browser.title
执行结果如图5所示。
图5 获取搜索结果
此时browser已经获取搜索的结果。
3、获取有效数据位置
第三步获取“有效数据”位置或者说是element。先定位搜索结果的标题和链接,再回到Chrome浏览器,在百度中搜索python selenium,在搜索结果页面中查看源代码。因为Chrome浏览器和PhantomJS浏览器返回的结果可能有所不同,这里只需要知道返回结果的大致结构,不需要完全一致。Chrome浏览器返回结果如图6所示。
图6 Chrome浏览器搜索结果
打开源代码页面搜索第一个结果的标题Selenium with Python,如图7所示。
图7 搜索结果定位
在这里发现了一个比较特别的属性class="c-tools",在代码中查找这个属性,如图8所示。
图8 搜索class属性
发现共有10个结果,并且第二个搜索结果的标题和搜索页面中第二个搜索结果相同,再数一数百度搜索结果页面中总共10个结果。可以确定所有的搜索结果中都包含有class="ctools"标签,可以使用find_elements_by_class_name定位所有的搜索结果了。执行命令:
resultElements=browser.find_elements_by_class_name(c-tools)
len(resultElements)
执行结果如图9所示。
图9 定位搜索结果
这里使用的是find_elements,不是find_element。定位多个结果时用elements。
一般来说定位结果用find_element_by_xpath或find_element_by_css比较方便,如果结果中有特殊的属性,用find_element_by_class_name也挺好,哪个方便就用哪一个。
4、从位置中获取有效数据
有效数据的位置确定后,如何从位置中过滤出有效的数据呢?一般就是获取element的文字或者获取Element中某个属性值。Selenium有自己独特的方法,分别是:
element.text
element.get_attribute(name)
回到Chrome浏览器搜索结果的源代码页面,如图10所示。
图10 有效数据
所需的有效数据就是data-tools属性的值。执行命令:
value=resultElements[0].get_attribute(data-tools)
valueDic=eval(value)
print valueDic.get(title).decode(utf8)
print valueDic.get(url)
执行结果如图11所示。
图11 获取有效数据
遍历resultElements列表,可以获取所有搜索结果的title和url。至此,已将Selenium&PhantomJS爬虫运行了一遍。根据这个过程可以编写一个完整的爬虫。