页面等待
现在的网页越来越多采用了 Ajax 技术,这样程序便不能确定何时某个元素完全加载出来了。如果实际页面等待时间过长导致某个dom元素还没出来,但是你的代码直接使用了这个WebElement,那么就会抛出NullPointer的异常。为了解决这个问题。所以 Selenium 提供了两种等待方式:一种是隐式等待、一种是显式等待
隐式等待:调用driver.implicitly_wait。那么在获取不可用的元素之前,会先等待10秒中的时间
driver.implicitly_wait(10)显示等待:显示等待是表明某个条件成立后才执行获取元素的操作。也可以在等待的时候指定一个最大的时间,如果超过这个时间那么就抛出一个异常。显示等待应该使用selenium.webdriver.support.excepted_conditions期望的条件和selenium.webdriver.support.ui.WebDriverWait来配合完成
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
driver = webdriver.Chrome()
driver.get("https://www.baidu.com/")
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "myDynamicElement"))
)
一些其他的等待条件
- presence_of_element_located:某个元素已经加载完毕了。
- presence_of_all_elements_located:网页中所有满足条件的元素都加载完毕了。
- element_to_be_clickable:某个元素是可以点击了。
更多条件请参考:http://selenium-python.readthedocs.io/waits.html
打开多窗口和切换页面
- 有时候窗口中有很多子tab页面。这时候肯定是需要进行切换的。selenium提供了一个叫做switch_to_window来进行切换,具体切换到哪个页面,可以从driver.window_handles中找到
- 打开一个新的页面
driver.execute_script("window.open('url')")
print(driver.current_url) - 切换到这个新的页面中
driver.switch_to_window(self.driver.window_handles[1]) - 同时打开两个URL,其中"https://www.douban.com/"仅是打开,可编辑的网址是https://www.baidu.com/
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.baidu.com/')
driver.execute_script('window.open("https://www.douban.com/")')
print(driver.current_url)
运行结果:
https://www.baidu.com/
- 同时打开两个URL,使"https://www.douban.com/"变为可编辑的网址,https://www.baidu.com/仅是打开。driver.switch_to.window(driver.window_handles[1])
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get('https://www.baidu.com/')
driver.execute_script('window.open("https://www.douban.com/")')
print(driver.current_url)
driver.switch_to.window(driver.window_handles[1])
time.sleep(1)
print(driver.current_url)
运行结果
https://www.baidu.com/
https://www.douban.com/
execute_script()记忆点
- 滚动到当前页面的最下端,目的是通过滚动让ajax把页面数据都加载出来。
driver.execute_script(
'window.scrollTo(0,document.body.scrollHeight)'
) - 在当前网页打开新的网页
driver.execute_script('window.open("https://www.douban.com/")') - input标签type="hidden"时,必须通过execute_script()解决
driver.execute_script('arguments[0].value="%s"'%from_station_code(把这个标签源标签),from_station_input(替换成这个标签目标标签) - 按钮不能够被点击btn代表按扭定位元素变量
driver.execute_script('arguments[0].click()',btn)
设置无界面
options = webdriver.ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
import csv
from selenium import webdriver
class MaoyanSprider(object):
def __init__(self):
self.options = webdriver.ChromeOptions()
self.options.add_argument('--headless')
self.driver = webdriver.Chrome(options = self.options)
self.driver.get('https://maoyan.com/board/4')
self.driver.maximize_window()
def Maoyanparse(self):
items = self.driver.find_elements_by_xpath('//*[@id="app"]/div/div/div[1]/dl/dd')
mao_list=[]
for item in items:
maoyanitem = item.text
maoitem = maoyanitem.split('\n')
# print(maoitem)
maoyandict = {}
try:
maoyandict['rank'] = maoitem[0]
maoyandict['name'] = maoitem[1]
maoyandict['actor'] = maoitem[2]
maoyandict['time'] = maoitem[3]
maoyandict['score'] = maoitem[4]
mao_list.append(maoyandict)
except:
pass
return mao_list
def WriteDate(self,mao_list):
hr = ['rank','name','actor','time','score']
with open('maoyan.csv','w',encoding='utf-8') as f:
writer = csv.DictWriter(f,hr)
writer.writeheader()
writer.writerows(mao_list)
def main(self):
mao_list=[]
while True:
mao_list += self.Maoyanparse()
self.WriteDate(mao_list)
try:
self.driver.find_element_by_link_text('下一页').click()
except:
self.driver.quit()
break
if __name__ == '__main__':
mao = MaoyanSprider()
mao.main()
案例-动态网页
第一步 页面分析 我们发现 所有的商品信息数据都是在一个ul标签 ul标签下面每一个li标签对应的就是一个商品完整信息。
我们拖动 拖动条(滚轮)的时候 页面又加载了数据,当我们进入这个页面的时候,把这个拖动条拖动一下,拖到最下面,然后等它加载一会儿,等页面元素加载完了之后,我们再去抓取,每页 上来先加载30个 当我们进行拖动的时候 它又会加载30个 也就是每页其实是60个数据。
第二步 如何拖动 拖动条? 我们会用到一个 加载js的方法driver.execute_script(
'window.scrollTo(0,document.body.scrollHeight)'
)
第三步数据解析:
items = self.driver.find_elements_by_xpath('//*[@id="J_goodsList"]/ul/li')
items_list=[]
for item in items:
price = item.find_element_by_xpath('.//div[@class="p-price"]/strong/i').text.strip()
title = item.find_element_by_css_selector('.p-name.p-name-type-2>a>em').text.strip().replace('京品手机','').replace('拍拍','').strip()
shop = item.find_element_by_xpath('.//div[@class="p-shop"]/span/a').text.strip()
commit = item.find_element_by_xpath('.//div[@class="p-commit"]/strong/a').text.strip()
jd_list =[price,title,shop,commit]
items_list.append(jd_list)
第四步 翻页处理
找规律 我们先看最后一页 发现这个 下一页的按钮还在
if self.driver.page_source.find("pn-next disabled")== -1:
self.driver.find_element_by_class_name("pn-next").click()
time.sleep(2)
else:
self.driver.quit()
break
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
import time
import csv
class JdSprider(object):
def __init__(self):
self.driver = webdriver.Chrome()
self.url = 'https://www.jd.com/'
self.driver.get(self.url)
self.driver.maximize_window()
self.element = WebDriverWait(self.driver, 1000).until(
EC.presence_of_element_located((By.ID, "key"))
)
self.element.send_keys('苹果手机')
self.botton = WebDriverWait(self.driver, 1000).until(
EC.presence_of_element_located((By.CLASS_NAME, "button"))
).click()
def jdparse(self):
self.driver.execute_script(
'window.scrollTo(0,document.body.scrollHeight)'
)
time.sleep(3)
items = self.driver.find_elements_by_xpath('//*[@id="J_goodsList"]/ul/li')
items_list=[]
for item in items:
price = item.find_element_by_xpath('.//div[@class="p-price"]/strong/i').text.strip()
title = item.find_element_by_css_selector('.p-name.p-name-type-2>a>em').text.strip().replace('京品手机','').replace('拍拍','').strip()
shop = item.find_element_by_xpath('.//div[@class="p-shop"]/span/a').text.strip()
commit = item.find_element_by_xpath('.//div[@class="p-commit"]/strong/a').text.strip()
jd_list =[price,title,shop,commit]
items_list.append(jd_list)
return items_list
def WriteDate(self,item_list):
hr =['price','title','shop','commit']
with open('jdsearch.csv','w',encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(hr)
writer.writerows(item_list)
def main(self):
items_list =[]
while True:
items_list += self.jdparse()
self.WriteDate(items_list)
if self.driver.page_source.find("pn-next disabled")== -1:
self.driver.find_element_by_class_name("pn-next").click()
time.sleep(2)
else:
self.driver.quit()
break
if __name__ == '__main__':
jd = JdSprider()
jd.main()