Python爬虫python爬虫专题

python3的爬虫笔记9——Selenium的用法

2017-05-06  本文已影响678人  X_xxieRiemann

继续介绍下selenium的用法,一共以两个例子来呈现。分别对应的是QQ空间模拟登录(如何传送登录信息和确认,iframe如何切换),爬取简书7日热门(页面如何拉到底)。
浏览器用的是chrome。

(1)QQ空间模拟登录(如何传送登录信息和确认,iframe如何切换)

网址: http://user.qzone.qq.com/


我们是如何一般登录的:
第一步:点击账号密码登录


第二步:输入账号密码


第三步:点击登录。
然后我们就进入登录界面了。
因此,任务的关键就是能用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

上一篇下一篇

猜你喜欢

热点阅读