Web scraping with Selenium

参考:Selenium tutorial, Selenium basic



selenium本来是用来做网站测试的,但逐渐成为一个强大的爬虫工具,主要用于抓取使用了Ajax和动态HTML技术的网页内容。

安装

$ pip install selenium

针对不同的浏览器,还需要下载对应的插件,如Chrome v59,需要下载chromedriver v2.32/usr/bin目录中。

也可以使用PhantomJS(不显示界面的浏览器):

$ sudo apt-get install phantomjs

安装完成后在/usr/bin/phantomjs中添加以下代码:

export QT_QPA_PLATFORM=offscreen
export QT_QPA_FONTDIR=/usr/share/fonts

特别提示:如果在调试中发现如下的异常信息,就表明你下载的PhantomJS很可能是不完整的(源里的phantomjs文件有问题)。

selenium.common.exceptions.WebDriverException: Message: Error - Unable to load Atom 'execute_script' from file ':/ghostdriver/./third_party/webdriver-atoms/execute_script.js'

这种情况下,建议去官网下载编译好的二进制文件,复制到/usr/bin中,替换同名文件即可。

[TOP]


获取网页源代码

from selenium import webdriver
browser = webdriver.PhantomJS() # 使用PhantomJS,不弹出浏览器界面
browser.get('http://www.taobao.com/')
page_html = browser.page_source # 获取网页源代码
print(page_html)
browser.close()

如在淘宝上搜索“全球通史”:

from selenium import webdriver
browser = webdriver.Chrome()
browser.get('http://www.taobao.com/')
element=browser.find_element_by_id("q")
element.send_keys(ur"全球通史")
element.submit()
browser.back() # 相当于浏览器的"后退"按钮,"前进"为browser.forward()
element=browser.find_element_by_id("q")
element.send_keys(ur"全球通史 斯塔夫里阿诺斯")
element.submit()
browser.close()

[TOP]


元素选择器

上面的例子用到了find_element_by_id,selenium还提供了很多类似的选择器:

find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector

element换成elements就可以一次提取具有同样特征的多个元素,返回的是一个python列表。

[TOP]


元素拖拽

指定被拖动的元素和目标元素后,可以使用ActionChains类来实现拖动,如

element = driver.find_element_by_name("source")
target = driver.find_element_by_name("target")
 
from selenium.webdriver import ActionChains
action_chains = ActionChains(driver)
action_chains.drag_and_drop(element, target).perform()

[TOP]


表单

上面的例子已经用到了表单的填充

element=browser.find_element_by_id("q")
element.send_keys(ur"全球通史")

清除表单中的文本

element.clear()

实际上send_keys还可以模拟按键,如

element.send_keys("text", Keys.ARROW_DOWN)

对于下拉选项,可以使用WebDriver中的Select方法

from selenium.webdriver.support.ui import Select
select = Select(driver.find_element_by_name('name'))
select.select_by_index(index) # 根据索引选择选项
select.select_by_visible_text("text") # 根据文本选择选项
select.select_by_value(value) # 根据值选择选项
select.deselect_all() # 取消所有选项
all_selected_options = select.all_selected_options # 获取已选的选项
select.options # 获取所有可选选项
browser.find_element_by_id("submit").click() # 点击submit按钮

[TOP]


弹窗

alert = browser.switch_to_alert()

获取弹窗对象。

[TOP]


Frame切换

browser.switch_to_frame("frameName.0.child")

将焦点切换到namechild的frame上。

[TOP]


隐式等待

很多网页用Ajax异步加载数据,比如当页面被打开几秒钟以后,会被替换为Ajax生成的内容,采用传统的爬虫只能获取Ajax加载之前的页面。

如果知道异步加载的延时,可以采用显示等待的方式抓取Ajax生成的内容,如:

from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get('http://www.toutiao.com/')
for i in range(10):
    browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    webdriver.ActionChains(browser).key_down(Keys.DOWN).perform() # 将页面拖到最底端
    time.sleep(5) # 显式等待5秒
print browser.find_elements_by_class_name("link")[-1].text
browser.close()

很多时候异步加载的时间是不确定的,因此这种方法有时候会出错。更加有效的方法是通过检查页面中的某个元素是否存在,以此判断是否可以开始采集数据,这种方法就是selenium的隐式等待(implicit wait)。

触发隐式等待条件(expected_conditions)有很多,如弹出一个提示框,一个元素被选中,页面标题改变,某些文字显示在页面上,一个元素在DOM中变为可见或不可见等等。

下面的代码用idloadedButton的按钮检查页面是否加载完成:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
 
browser = webdriver.PhantomJS()
browser.get("***some_url***")
try:
    element = WebDriverWait(browser, 10).until(
        EC.presence_of_element_located((By.ID, "loadedButton"))
    )
finally:
    print browser.find_element_by_id("content").text
    driver.close()

By用以指定expected_conditions所在的元素,称为定位器,定位器与选择器是不一样的,定位器可以用于创建选择器,如

browser.find_element(By.ID, "q")

By对象策略有ID, CLASS_NAME, CSS_SELECTOR, LINK_TEXT, PARTIAL_LINK_TEXT, NAME, TAG_NAME, XPATH

selenium内置的隐式等待的期望条件有

title_is
title_contains
presence_of_element_located
visibility_of_element_located
visibility_of
presence_of_all_elements_located
text_to_be_present_in_element
text_to_be_present_in_element_value
frame_to_be_available_and_switch_to_it
invisibility_of_element_located
element_to_be_clickable
staleness_of
element_to_be_selected
element_located_to_be_selected
element_selection_state_to_be
element_located_selection_state_to_be
alert_is_present

[TOP]


获取或发送Cookies

# cookie在某个域名下才能生效,因此需要首先建立连接
browser.get('http://www.taobao.com/')
# 获取该URL的所有cookies
coo = browser.get_cookies()
print(coo)

请求网页时发送cookie信息,可用于身份验证,实现免登录访问:

# cookie在某个域名下才能生效,因此需要首先建立连接
browser.get('http://www.taobao.com/')
# 删除第一次建立连接时的cookies
browser.delete_all_cookies()
# 读取手动登录时预存到本地的cookies
with open('cookies.json', 'r', encoding='utf-8') as f:
    listCookies = json.loads(f.read())
for cookie in listCookies:
    browser.add_cookie({
        'domain': '.taobao.com', # 注意xxx.com前面的.
        'name': cookie['name'],
        'value': cookie['value'],
        'path': '/',
        'expires': None
    })
# 再次访问页面,便可实现免登录
browser.get('http://www.taobao.com/')

[TOP]