一.标签内部存在iframe时, 普通的方式无法直接定位到iframe内部的标签元素,这时候就需要进行切换
举个栗子...
比如登录QQ邮箱的输入框就是iframe,以此为例
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Firefox()
driver.get('https://www.baidu.com/')
"""请求登录邮箱的网址"""
driver.get('https://mail.qq.com/')
# frame()查找和定位iframe框架的方法
driver.switch_to.frame(driver.find_element_by_id('login_frame'))
driver.find_element_by_id('switcher_plogin').click()
username = WebDriverWait(driver, 10).until(lambda driver: driver.find_element_by_css_selector('#u'))
pwd = WebDriverWait(driver, 10).until(lambda driver: driver.find_element_by_css_selector('#p'))
username.send_keys('123')
pwd.send_keys('123')
二.一个浏览器对象, 存在多个标签选项卡, 这时候也需要进行切换
第一种:
需要多个标签选项卡来回切换提取信息, 这时候需要不停的来回切换标签选项卡
举个栗子...
比如下载天堂图片的图片(需要在列表页和详情页不断的切换)
import time,requests, os, shutil
from selenium import webdriver
from urllib.request import urlretrieve
"""进行多个窗口的爬虫任务时要记得窗口之间的切换"""
options = webdriver.FirefoxOptions()
options.headless = True
# driver = webdriver.Firefox()
driver = webdriver.Firefox(firefox_options=options)
driver.get('http://www.ivsky.com/tupian/ziranfengguang/')
"""获取当前窗口(列表页)并打印当前窗口id和title"""
current_window = driver.current_window_handle
print(current_window, driver.title)
list_pics = driver.find_elements_by_css_selector('li > p > a')
print(list_pics)
for pics in list_pics:
title = pics.text
print(title)
if os.path.exists(title):
shutil.rmtree(title, ignore_errors=True) # 移除目录树
os.mkdir(title)
os.chdir(title)
"""跳转到一个新的界面(详情页), 但此时的窗口id还是原来界面(列表页)的窗口id"""
pics.click()
time.sleep(3)
"""获取当前打开的所有窗口(列表页和详情页)"""
windows = driver.window_handles
print(windows)
for win in windows:
"""判断当前(第一次打开的窗口id是否等于win, 若不等于...)"""
if current_window != win:
"""切换窗口id(窗口id是详情页的id)"""
driver.switch_to.window(win)
list_imgs = driver.find_elements_by_css_selector(' li > div > a > img')
for img in list_imgs:
src = img.get_attribute('src')
print(src)
img_title = src.split('/')[-1]
urlretrieve(src, '%s.jpg' % img_title)
"""下载图片完成,出来文件夹"""
os.chdir(os.path.pardir)
time.sleep(2)
"""关闭当前页面"""
driver.close() # 关闭当前页
time.sleep(2)
"""将窗口id切换到列表页"""
driver.switch_to.window(current_window)
第二种:
算是第一种的另外一种办法吧(迷茫脸)...
第一种是通过来回切换选项卡来提取数据的; 第二种是避免了第一类来回切换选项卡的操作, 采用另外一种方法来提取多个窗口的数据...大概描述一下:打开一个窗口可以获取到列表页的信息, 由于打开详情页是又新打开一个窗口呈现的, 在这里我们通过解析列表页拿到详情页的url, 然后单独的去driver.get(详情页url), 然后提取数据, 这样就避免了来回切换窗口提取数据的操作...
个人觉得第二种还有待深入的去学习
上一个栗子...淘宝网, 并且有使用多进程
# selenium本身自带这种幽灵浏览器/无头浏览器
import time
from multiprocessing import Pool
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from lxml.html import etree
# apply_async() / map() 方法添加函数任务的时候,需要注意如果是在类中,保证类中的方法不能是实例方法。
class TaoBao(object):
options = webdriver.FirefoxOptions()
options.headless = True
driver = webdriver.Firefox()
# driver = webdriver.Firefox(firefox_options=options)
def __init__(self, pool):
self.pool = pool
self.start_url = 'https://www.taobao.com/'
def start(self):
self.driver.get(self.start_url)
search_input = WebDriverWait(self.driver, 15).until(
lambda driver: driver.find_element_by_id('q'))
search_input.send_keys('笔记本电脑')
"""点击之后不会跳转到新的窗口(是在当前窗口打开的)"""
self.driver.find_element_by_class_name('btn-search').click()
windows = self.driver.window_handles
print(windows)
# 点击进入到列表页之后,需要使用selenium操作滚动条,让页面滚动到底部。
for x in range(1, 11, 2):
height = float(x) / 10
# 根据x的值,计算整个页面高度需要循环的次数
# document.documentElement.scrollTop:当前页面相对于窗口顶部的偏移量
# document.documentElement.scrollHeight: 整个页面的高度,包含可滚动的部分
js = "document.documentElement.scrollTop = document.documentElement.scrollHeight * %f" % height
self.driver.execute_script(js)
time.sleep(0.2)
html = self.driver.page_source
self.parse_list_page(html)
# @classmethod
# def get_list_page(cls, html):
# print('获取下一页源码的方法')
def parse_list_page(self, html):
list_html = etree.HTML(html, parser=etree.HTMLParser(encoding='utf-8'))
# 根据list_html找到下一页的连接,然后将下一页的连接的请求也加入进程池
# next_url = 'list_html中提取'
# self.pool.apply_async(self.get_list_page, args=(next_url,), callback=self.parse_list_page)
divs = list_html.cssselect('.info-cont')
for div in divs:
"""获取详情页的url(用详情页的url请求详情页数据, 就不用打开新的窗口然后切换窗口拿数据, 是不是很方便了)"""
detail_url = 'https:'+div.cssselect('.product-title')[0].attrib['href']
self.pool.apply_async(self.get_detail_page, args=(detail_url,), callback=self.parse_detail_page)
@classmethod
def get_detail_page(cls, detail_url):
"""请求详情页的url"""
cls.driver.get(detail_url)
time.sleep(1)
"""提取详情页的数据"""
detail_html = etree.HTML(cls.driver.page_source, parser=etree.HTMLParser(encoding='utf-8'))
title = detail_html.cssselect('.panel-head > .spu-title')[0].text
price = detail_html.cssselect('.price > strong')[0].text
return {
'title': title,
'content': price
}
@classmethod
def parse_detail_page(cls, data):
print(data)
if __name__ == '__main__':
pool = Pool()
taobao = TaoBao(pool)
taobao.start()
pool.close()
pool.join()