本篇记录的是软件测试方向的项目实战,使用 Selenium 实现爬虫与自动化测试。具体来说则是自动化测试运行了合成大西瓜游戏以及使用 Selenium 爬取京东商城的商品。
一. Selenium 常规操作 (A) 基本请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from selenium import webdriverbrowser = webdriver.Chrome() browser = webdriver.Firefox() browser = webdriver.xxx() browser.maximize_window() browser.get("https://movie.douban.com/top250" ) browser.close() browser.quit()
(B) 常用操作 (a) 浏览器方法
浏览器方法
作用
browser.get(url)
请求url
browser.quit()
关闭浏览器
browser.close()
关闭当前页面
browser.page_source
获取源码
browser.page_source.find(‘keyword’)
在源码中查找
browser.maximize_window() - 浏览器窗口最大化
窗口最大化
(b) 节点操作
节点操作
作用
node.send_keys(‘’)
在文本框填写内容
node.click()
点击
node.get_attribute(‘href/id/name’)
获取节点属性
node.text
获取节点文本
(C) 查找节点 (a) 单个节点 做一个展示:
有多种方式,深入的方法可以通过官方文档进行系统的学习。
1 2 3 4 5 6 7 8 find_element_by_name('sunrisecai' ) find_element_by_id('sunrisecai' ) find_element_by_xpath('sunrisecai' ) find_element_by_css_selector('sunrisecai' )
等价:
find_element()
,里面填写需要查找的节点即可。
1 2 3 4 5 6 7 8 9 10 from selenium.webdriver.common.by import Byfind_element(By.NAME,'sunrisecai' ) find_element(By.ID,'sunrisecai' ) find_element(By.XPATH,'sunrisecai' ) find_element(By.CSS_SELECTOR,'sunrisecai' )
(b) 多个节点 单个节点与多个节点的区别如下所示:
节点
区别
区别
单个节点
find_element
find_element_by_xxx
多个节点
find_elements
find_elements _by_xxx
可以看到基本上是一致的,不同点在于多个节点的element多了一个s,即elements。
(D) 模拟填写表单与点击 填写表单:
1 2 3 4 5 6 7 8 text_box = browser.find_element_by_xpath('xxx' ) text_box.clear() text_box.send_keys('xxx' )
模拟点击:
1 2 browser.find_element_by_xpath('xxx' ).click()
二. 自动化测试案例 这里自动化测试运行了“合成大西瓜”游戏
(A) 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from selenium import webdriverfrom time import sleep from selenium.webdriver.common.action_chains import ActionChains import randomwd = webdriver.Chrome(r'D:\chromedriver.exe' ) wd.get('https://dushusir.com/xigua' ) wd.implicitly_wait(5 ) wd.set_window_rect(0 ,0 ,700 ,700 ) while (True ): randomX = random.randint(20 , 300 ) randomY = random.randint(200 , 300 ) print('click' ) ActionChains(wd).move_by_offset(randomX, randomY).click().perform() ActionChains(wd).move_by_offset(-randomX, -randomY).perform() sleep(1 )
三. 爬取京东商城商品 (A) 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 from selenium import webdriverfrom selenium.webdriver.support.wait import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byimport selenium.common.exceptionsimport jsonimport csvimport timeclass JdSpider (): def open_file (self ): self.fm = input('请输入文件保存格式(txt、json、csv):' ) while self.fm!='txt' and self.fm!='json' and self.fm!='csv' : self.fm = input('输入错误,请重新输入文件保存格式(txt、json、csv):' ) if self.fm=='txt' : self.fd = open('Jd.txt' ,'w' ,encoding='utf-8' ) elif self.fm=='json' : self.fd = open('Jd.json' ,'w' ,encoding='utf-8' ) elif self.fm=='csv' : self.fd = open('Jd.csv' ,'w' ,encoding='utf-8' ,newline='' ) def open_browser (self ): self.browser = webdriver.Chrome(r'D:\chromedriver.exe' ) self.browser.implicitly_wait(10 ) self.wait = WebDriverWait(self.browser,10 ) def init_variable (self ): self.data = zip() self.isLast = False def parse_page (self ): try : skus = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//li[@class="gl-item"]' ))) skus = [item.get_attribute('data-sku' ) for item in skus] links = ['https://item.jd.com/{sku}.html' .format(sku=item) for item in skus] prices = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="gl-i-wrap"]/div[2]/strong/i' ))) prices = [item.text for item in prices] names = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="gl-i-wrap"]/div[3]/a/em' ))) names = [item.text for item in names] comments = self.wait.until(EC.presence_of_all_elements_located((By.XPATH,'//div[@class="gl-i-wrap"]/div[4]/strong' ))) comments = [item.text for item in comments] self.data = zip(links,prices,names,comments) except selenium.common.exceptions.TimeoutException: print('parse_page: TimeoutException' ) self.parse_page() except selenium.common.exceptions.StaleElementReferenceException: print('parse_page: StaleElementReferenceException' ) self.browser.refresh() def turn_page (self ): try : self.wait.until(EC.element_to_be_clickable((By.XPATH,'//a[@class="pn-next"]' ))).click() time.sleep(1 ) self.browser.execute_script("window.scrollTo(0,document.body.scrollHeight)" ) time.sleep(2 ) except selenium.common.exceptions.NoSuchElementException: self.isLast = True except selenium.common.exceptions.TimeoutException: print('turn_page: TimeoutException' ) self.turn_page() except selenium.common.exceptions.StaleElementReferenceException: print('turn_page: StaleElementReferenceException' ) self.browser.refresh() def write_to_file (self ): if self.fm == 'txt' : for item in self.data: self.fd.write('----------------------------------------\n' ) self.fd.write('link:' + str(item[0 ]) + '\n' ) self.fd.write('price:' + str(item[1 ]) + '\n' ) self.fd.write('name:' + str(item[2 ]) + '\n' ) self.fd.write('comment:' + str(item[3 ]) + '\n' ) if self.fm == 'json' : temp = ('link' ,'price' ,'name' ,'comment' ) for item in self.data: json.dump(dict(zip(temp,item)),self.fd,ensure_ascii=False ) if self.fm == 'csv' : writer = csv.writer(self.fd) for item in self.data: writer.writerow(item) def close_file (self ): self.fd.close() def close_browser (self ): self.browser.quit() def crawl (self ): self.open_file() self.open_browser() self.init_variable() print('开始爬取' ) self.browser.get('https://search.jd.com/Search?keyword=%E7%AC%94%E8%AE%B0%E6%9C%AC&enc=utf-8' ) time.sleep(1 ) self.browser.execute_script("window.scrollTo(0,document.body.scrollHeight)" ) time.sleep(2 ) count = 0 while count<=3 : count += 1 print('正在爬取第 ' + str(count) + ' 页......' ) self.parse_page() self.write_to_file() self.turn_page() self.close_file() self.close_browser() print('结束爬取' ) if __name__ == '__main__' : spider = JdSpider() spider.crawl()
(B) 数据元素简介 我们需要解析每一个网页来获取我们需要的数据,具体包括(可以使用 Selenium 选择元素):
1 browser.find_elements_by_xpath('//li[@data-sku]' )
1 browser.find_elements_by_xpath('//div[@class="gl-i-wrap"]/div[2]/strong/i' )
1 browser.find_elements_by_xpath('//div[@class="gl-i-wrap"]/div[3]/a/em' )
1 browser.find_elements_by_xpath('//div[@class="gl-i-wrap"]/div[4]/strong' )
(C) Tips 代码有几个需要注意的地方,现在记录下来便于以后学习。
(1) self.fd = open('Jd.csv','w',encoding='utf-8',newline='')
再打开 csv 文件时,最好加上参数 newline=’’,否则我们写入的文件会出现空行,不利于后续的数据处理。
(2) self.browser.execute_script("window.scrollTo(0,document.body.scrollHeight)")
在模拟浏览器向下拖动网页时,由于数据更新不及时,所以经常出现 StaleElementReferenceException 异常。一般来说有两种处理方法: 在完成操作后使用 time.sleep() 给浏览器充足的加载时间;捕获该异常进行相应的处理。
(3) skus = [item.get_attribute('data-sku') for item in skus]
在 Selenium 中使用 xpath 语法选取元素时,无法直接获取节点的属性值,而需要使用 get_attribute() 方法。
(4) 无头启动浏览器可以加快爬取速度,只需在启动浏览器时设置无头参数即可
1 2 3 opt = webdriver.chrome.options.Options() opt.set_headless() browser = webdriver.Chrome(chrome_options=opt)