Selenium是一个自动化测试工具,可以驱动浏览器器执行特定的动作,如点击,下拉等。同时还可以获取浏览器当前呈现页面的源代码,可见即可爬。
浏览器驱动
根据自己的浏览器进行选择:
Chrome: https://sites.google.com/a/chromium.org/chromedriver/downloads
Firefox: https://github.com/mozilla/geckodriver/releases
使用selenium写爬虫
优势
* 不需要做复杂的抓包、构造请求、解析数据等,开发难度相对要低一些。
* 其访问参数跟使用浏览器的正常用户一模一样,访问行为也相对更像正常用户,不容易被反爬虫策略命中。
* 生成的浏览器环境可以自动运行 JS 文件,所以不用担心如何逆向混淆过的JS文件生成用作人机校验的参数,如马蜂窝酒店评论的人机校验参数_sn,网易云音乐评论的人机校验参数params、encSecKey。可以自行抓包查看。
* 如果需要抓取同一个前端页面上面来自不同后端接口的信息,如OTA酒店详情页的酒店基础信息、价格、评论等,使用Selenium可以在一次请求中同时完成对三个接口的调用,相对方便
劣势
相比于抓包→构造请求→解析返回值的爬虫,由于Selenium需要生成一个浏览器环境,所有操作(与元素交互、获取元素内容等)均需要等待页面加载完毕后才可以继续进行,所以速度相比构造请求的慢很多。
对于为了反爬做了特殊处理的展示内容,如字体加密(参考猫眼)、图片替换数字(参考自如)等,可能取不到想要的数据。
准备
以Firefox为例来研究selenium的用法, 在开始之前,请确保已经正确安装好Firefox浏览器并配置好了GeckoDriver;另外,还需要正确安装Python的Selenium库
初步体验
from selenium import webdriver
browser = webdriver.Firefox()
browser.get('http://www.baidu.com/')
运行这段代码,会自动打开浏览器,然后访问百度。
如果程序执行错误,浏览器没有打开,那么应该是没有装 Firefox浏览器或者 Firefox驱动没有配置在环境变量里。下载驱动,然后将驱动文件路径配置在环境变量即可
模拟提交
首先等页面加载完成,然后输入到搜索框文本,点击提交。
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
driver = webdriver.Firefox()
driver.get("http://www.python.org")
assert "Python" in driver.title
elem = driver.find_element_by_name("q")
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)
print(driver.page_source)
页面操作
页面交互
要做到页面交互,比如点击,输入等, 前提就是要找到页面中的元素。WebDriver提供了各种方法来寻找元素。
Eg:
<input type="text" name="passwd" id="passwd-id" />
element = driver.find_element_by_id("passwd-id")
element = driver.find_element_by_name("passwd")
element = driver.find_elements_by_tag_name("input")
element = driver.find_element_by_xpath("//input[@id='passwd-id']")
在用 xpath 的时候还需要注意的是,如果有多个元素匹配了 xpath,它只会返回第一个匹配的元素。如果没有找到,那么会抛出 NoSuchElementException 的异常。
获取了元素之后,下一步当然就是向文本输入内容了,可以利用下面的方法
element.send_keys("some text")
同样你还可以利用 Keys 这个类来模拟点击某个按键。
element.send_keys("and some", Keys.ARROW_DOWN)
你可以对任何获取到到元素使用 send_keys 方法,就像你在 GMail 里面点击发送键一样。不过这样会导致的结果就是输入的文本不会自动清除。所以输入的文本都会在原来的基础上继续输入。你可以用下面的方法来清除输入文本的内容
element.clear()
这样输入的文本会被清除
填充表单
下拉选项卡处理方式
element = driver.find_element_by_xpath("//select[@name='name']")
all_options = element.find_elements_by_tag_name("option")
for option in all_options:
print("Value is: %s" % option.get_attribute("value"))
option.click()
首先获取了第一个 select 元素,也就是下拉选项卡。然后轮流设置了 select 选项卡中的每一个 option 选项。你可以看到,这并不是一个非常有效的方法。
其实 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 = Select(driver.find_element_by_id('id'))
select.deselect_all()
获取所有的已选选项
select = Select(driver.find_element_by_xpath("xpath"))
all_selected_options = select.all_selected_options
获取所有可选选项
options = select.options
提交表单
driver.find_element_by_id("submit").click()
相当于模拟点击了 submit 按钮,做到表单提交
页面切换
一个浏览器肯定会有很多窗口,所以我们肯定要有方法来实现窗口的切换
driver.switch_to_window("windowName")
另外还可以使用 window_handles 方法来获取每个窗口的操作对象
for handle in driver.window_handles:
driver.switch_to_window(handle)
操作页面的前进和后退功能
driver.forward()
driver.back()
元素选取
关于元素的选取,有如下api
* 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
* find_elements_by_name
* find_elements_by_xpath
* find_elements_by_link_text
* find_elements_by_partial_link_text
* find_elements_by_tag_name
* find_elements_by_class_name
* find_elements_by_css_selector
另外还可以利用 By 类来确定哪种选择方式
from selenium.webdriver.common.by import By
driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_elements(By.XPATH, '//button')
By 类的一些属性如下
ID = "id"
XPATH = "xpath"
LINK_TEXT = "link text"
PARTIAL_LINK_TEXT = "partial link text"
NAME = "name"
TAG_NAME = "tag name"
CLASS_NAME = "class name"
CSS_SELECTOR = "css selector"
页面等待
Selenium 提供了两种等待方式,一种是隐式等待,一种是显式等待。 隐式等待是等待特定的时间,显式等待是指定某一条件直到这个条件成立时继续执行。
显示等待
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.ID,'someid')))
其他的等待条件
* 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 – it is Displayed and Enabled.
* 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
隐式等待
隐式等待比较简单,就是简单地设置一个等待时间,单位为秒。
driver.implicitly_wait(10)
简单的爬虫示例
爬取Amazon某一类商品图片URL及产品的基本讯息
from selenium import webdriver
option = webdriver.FirefoxOptions()
# option.add_argument('--headless') #无头模式
option.set_preference('permissions.default.image', 2) #不加载图片
# option.set_preference('permissions.default.stylesheet', 2)
driver = webdriver.Firefox(options=option)
driver.get('https://www.amazon.com/s?bbn=13861679011&rh=n%3A3760911%2Cn%3A%2111055981%2Cn%3A17242866011%2Cn%3A11063461%2Cn%3A13861679011%2Cp_36%3A10000-200000&qid=1581758015&rnid=386662011&ref=lp_13861679011_nr_p_36_4')
def get_product_conf(max_num):
global id,CSV_headers,random_str_list
for i in range(max_num):
id = id + 1
# print(i.get_attribute('href'))
product_img = driver.find_element_by_xpath('//span/div[@class="s-result-list s-search-results sg-row"]/div[%d]//img[@class="s-image"]' %(i+1))
print(product_img.get_attribute('src'))
try:
driver.find_element_by_xpath('//span/div[@class="s-result-list s-search-results sg-row"]/div[%d]' %(i+1)).click()
product_title = driver.find_element_by_id('productTitle').text
except:
driver.back()
continue
# 商品描述
product_description = driver.find_element_by_id('productDescription').text.replace('\n','<br/>')
# 商品介绍
product_feature = driver.find_element_by_id('feature-bullets').text
# 所属栏位
product_categories = driver.find_element_by_id('wayfinding-breadcrumbs_feature_div').text.replace('\n','').replace('›','>')
print('product_title:',product_title)
print('product_description:',product_description)
print('product_feature:',product_feature)
print('product_categories:',product_categories)
driver.back()
if __name__ == '__main__':
id =1
while True:
print('start')
max_num = len(
driver.find_elements_by_xpath('//span/div[@class="s-result-list s-search-results sg-row"]/div')) - 1
print(max_num)
get_product_conf(max_num)
# data = get_product_conf(max_num)
# write_csv_rows('demo_test.csv', CSV_headers, data)
# print('1111111111111111111111')
if driver.find_elements_by_xpath('//li[@class="a-last"]/a[@href]'):
print(driver.find_elements_by_xpath('//li[@class="a-last"]/a[@href]'))
driver.find_element_by_class_name('a-last').click()
# print('222222222222222222222')
else:
break
driver.close()
其他常用函数
关闭浏览器:quit()
最大化窗口: maximize_window()
设置窗口参数:set_window_size(600,800)
刷新页面: refresh()
鼠标事件:
双击:double_click()
右击:context_click()
拖放:drag_and_drop()
悬停:move_to_element()
按下:click_and_hold()
键盘事件:
send_keys(Keys.BACK_SPACE) = BackSpace
send_keys(Keys.SPACE) = Space
send_keys(Keys.TAB) = Tab
send_keys(Keys.ESCAPE) = Esc
send_keys(Keys.ENTER) = Enter
send_keys(Keys.CONTROL,‘a’) = Ctrl+A
send_keys(Keys.F1) = 键盘F1
多表单切换:switch_to.frame()
多窗口切换:switch_to.window()
当前句柄:current_window_handle
所有句柄:window_handles
警告框处理:switch_to_alert()
text:返回所有alert/confirm/prompt中的文字信息
accept():接受现有警告框
dismiss():解散现有警告框
send_keys(keysToSend):发送文本至警告框
cookie处理:
get_cookies():获得所有cookie信息
get_cookie(name):返回字典的key为“name”的cookie信息
add_cookie(cookie_dict):添加cookie。“cookie_dict”指字典对象,必须有name和value值
delete_cookie(name,optionsString):删除cookie信息。“name”是要删除的cookie的名称,“optionsString”是该cookie的选项,目前支持的选项包括“路径”,“域”
delete_all_cookies():删除所有cookie信息
窗口截图:get_screenshot_as_file()
生成随机数:radint()
滚动条设置
# 使用scrollTop滑动到底部
js = "var action=document.documentElement.scrollTop=10000"
driver.execute_script(js)
# 使用scrollTo设置位置
driver.set_window_size(600, 600)
js = "window.scrollTo(100,450);"
driver.execute_script(js)