python3的爬虫笔记9——Selenium的用法
继续介绍下selenium的用法,一共以两个例子来呈现。分别对应的是QQ空间模拟登录(如何传送登录信息和确认,iframe如何切换),爬取简书7日热门(页面如何拉到底)。
浏览器用的是chrome。
(1)QQ空间模拟登录(如何传送登录信息和确认,iframe如何切换)
我们是如何一般登录的:
第一步:点击账号密码登录
第二步:输入账号密码
第三步:点击登录。
然后我们就进入登录界面了。
因此,任务的关键就是能用selenium模拟浏览器的一些点击操作和输入数据。
①首先我们看下如何实现点击
我们用开发者工具先获取“账号密码登录”按键对应的元素
#调用查找id的方法定位“账号密码登录”按键的位置
switcher_plogin = driver.find_element_by_id('switcher_plogin')
#调用click的方法实现鼠标点击
switcher_plogin.click()
②接下来看看如何传登录信息
我们用开发者工具先获取帐号输入框对应的元素
#调用查找id的方法定位帐号输入框的位置
username = driver.find_element_by_id('u')
#先把输入框清空,防止一些预填充的情况
username.clear()
#填写QQ号码
username.send_keys('你的QQ号')
准备就绪,直接上代码走起。
from selenium import webdriver
import time
url = 'http://user.qzone.qq.com/'
driver = webdriver.Chrome()
driver.get(url)
time.sleep(5)
#点击“帐号密码登录”按钮
driver.find_element_by_id('switcher_plogin').click()
#定位帐号输入框
username = driver.find_element_by_id('u')
#清空帐号输入框内容
username.clear()
#填写帐号
username.send_keys('你的qq号')
password = driver.find_element_by_id('p')
password.clear()
password.send_keys('你的密码')
#点击“登录”按钮
driver.find_element_by_id('login_button').click()
#driver.quit()
结果发现报错
提示
“no such element”
错,代码中明明有id = "switcher_plogin"
的,为什么会报错呢?有的页面由几个frame组成,如果要访问的元素不在当前的frame中,那么必须先切换到该元素所在的frame,才能进一步选定元素。
frame框架是网页中常用的技术,可以让多个URL的内容显示在一个页面中。常用标签FRAMESET,FRAME实现。FRAMESET是用以划分框窗,每一框窗由一个FRAME标记所标示,FRAME必须在FRAMESET范围中使用。iframe在frame的基础上提供了更多好用的特性。
又看了下页面的代码,整个登录方块,果然是一个iframe。
它相当于又插了个新的html
因此在我们的执行点击或填写信息之前,还需要切换到这个frame上。
driver.switch_to.frame('login_frame')
因此,正确的代码是:
from selenium import webdriver
import time
url = 'http://user.qzone.qq.com/'
driver = webdriver.Chrome()
driver.get(url)
time.sleep(5)
#切换到登录的frame上
driver.switch_to.frame('login_frame')
#点击“帐号密码登录”按钮
driver.find_element_by_id('switcher_plogin').click()
#定位帐号输入框
username = driver.find_element_by_id('u')
#清空帐号输入框内容
username.clear()
#填写帐号
username.send_keys('你的qq号')
password = driver.find_element_by_id('p')
password.clear()
password.send_keys('你的密码')
#点击“登录”按钮
driver.find_element_by_id('login_button').click()
#driver.quit()
登录成功:
因此(重要):我们以后分析源代码时,首先要注意有没有frame模块。
(2)爬取简书7日热门(页面如何拉到底)
进入简书7日热门,我们发现一开始页面并没有把全部的信息都显示出来,随着我们鼠标滚轮下拉,我们发现页面会新增一些文章信息,同时在开发者工具中看到,相应的元素有所增加,这就是异步加载。
并且在增加了2次文章信息后,页面不能再往下拉了,页面底部出现了阅读更多的横条:
相应的开发者工具的element中出现与其相对应的代码:
点击几次阅读更多后,会继续加载几次文章,最终完全加载完成。此时页面不能拉动,“阅读更多”的按键也消失了。
因此我们看到了问题的关键,就是页面的下拉和点击“阅读更多”,直到页面完全加载。之后我们就可以愉快的用正则或BeautifulSoup来提取信息了。
如何实现页面下拉呢,官方文档8.3中有提及(点我查阅)
方法如下
#用execute_script方法调用JavaScript API在一个加载完成的页面中去执行js代码
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
无须知道它太多的含义,只需要知道执行了这句话就相当于把浏览器右边的滚动条拉到底。
实际中一次下拉后,页面会刷新,滚动条又会回到大概中间的位置,因此我们需要多次下拉,直到满足我们的需求。页面下拉的模板如下(为了能直观的看到浏览器进行到哪个步骤,这边用了很多的print):
from selenium import webdriver
def scroll_down(driver, times):
for i in range(times):
print("开始执行第", str(i + 1), "次下拉操作")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # 执行JavaScript实现网页下拉倒底部
print("第", str(i + 1), "次下拉操作执行完毕")
print("第", str(i + 1), "次等待网页加载......")
time.sleep(5) # 等待5秒(时间可以根据自己的网速而定),页面加载出来再执行下拉操作
有了第一个QQ空间登录的例子,我们已经知道如何点击页面了。
driver.find_element_by_css_selector('a.load-more').click()
点击阅读更多的模板如下:
from selenium import webdriver
def load_more_click(driver, times):
for i in range(times):
print("第", str(i + 1), "次点击阅读更多")
driver.find_element_by_css_selector('a.load-more').click()# 执行阅读更多的点击
print("第", str(i + 1), "次等待网页加载......")
time.sleep(5)
在简书7日热门中,无论是页面下拉还是点击阅读更多的次数都是有限的,我们并不能一开始就知道实际次数times要几次,因此为了配合本次任务的实际情况,这两个模板都应该进行调整,调整为我们不需要输入执行次数times,它也能自己执行正确的次数。
from selenium import webdriver
def scroll_down(driver):
i = 1
while True:
print("开始执行第", str(i), "次下拉操作")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # 执行JavaScript实现网页下拉倒底部
print("第", str(i), "次下拉操作执行完毕")
print("第", str(i), "次等待网页加载......")
i += 1
time.sleep(4) # 等待4秒(时间可以根据自己的网速而定),页面加载出来再执行下拉操作
if '阅读更多' in driver.page_source:
break
from selenium import webdriver
def load_more_click(driver):
i = 1
while True:
if '阅读更多' not in driver.page_source:
break
print("第", str(i), "次点击阅读更多")
driver.find_element_by_css_selector('a.load-more').click()
print("第", str(i), "次等待网页加载......")
i += 1
time.sleep(5)
主要是增加了停止的条件判断,这里都是以是否出现“阅读更多”这个按钮为判断条件。
看一下完整的代码
from bs4 import BeautifulSoup as BS
from selenium import webdriver
import time,csv
#下拉操作函数
def scroll_down(driver):
i = 1
while True:
print("开始执行第", str(i), "次下拉操作")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # 执行JavaScript实现网页下拉倒底部
print("第", str(i), "次下拉操作执行完毕")
print("第", str(i), "次等待网页加载......")
i += 1
time.sleep(4) # 等待4秒(时间可以根据自己的网速而定),页面加载出来再执行下拉操作
if '阅读更多' in driver.page_source:
break
#点击阅读更多的函数
def load_more_click(driver):
i = 1
while True:
if '阅读更多' not in driver.page_source:
break
print("第", str(i), "次点击阅读更多")
driver.find_element_by_css_selector('a.load-more').click()
print("第", str(i), "次等待网页加载......")
i += 1
time.sleep(3)
#存储到csv中的函数
def csv_write(tablelist):
tableheader = ['标题', '作者', '发表日期', '阅读量']
with open('weekly.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(tableheader)
for row in tablelist:
#由于编码问题有些内容无法存储,暂时没找到解决方法,用try的方法先跳过
try:
writer.writerow(row)
except:
print('编码问题导致无法正常存储', row)
continue
#获得文章相关信息的列表
def get_tablelist(html):
soup = BS(html, 'lxml')
namelist = soup.select('a.blue-link')
titlelist = soup.select('a.title')
timelist = soup.select('span.time')
readlist = soup.select('div.meta > a:nth-of-type(1)')
tablelist = []
for a, b, c, d in zip(namelist, titlelist, timelist, readlist):
name = a.get_text()
title = b.get_text()
time = c.get_text()
read = d.get_text()
#不知道为什么用了replace后,还是没办法去除\n,好在存到csv中显示没啥问题,有没有大佬帮忙看看
read.replace('\n', '')
tablelist.append([title, name, time, read])
#print(tablelist)
return tablelist
#主函数
def mainfun():
driver = webdriver.Chrome()
url = 'http://www.jianshu.com/trending/weekly'
driver.get(url)
time.sleep(2)
scroll_down(driver=driver)
load_more_click(driver=driver)
html = driver.page_source
tablelist = get_tablelist(html)
csv_write(tablelist)
driver.quit()
if __name__ == '__main__':
mainfun()
输出框如下,不知道为啥含有\n的那个字符串去不掉\n。。输出确实有一些奇怪的符号,难怪存储时会报错。
csv文件如下:
看了下,确实是全部都保存了。
参考:
(1) http://www.jianshu.com/p/6ba5b0786a64
(2) http://zmister.com/archives/98.html