运用selenium爬取知乎timeline动态加载内容


在前之前文章中尝试用简单的Requests爬取知乎timeline时发现动态加载内容无法成功爬取,尝试分析数据包来爬取也没有成功,于是最后在这里使用selenium来尝试,终于成功。

全部代码见于我的Git


selenium思路

网上关于selenium的教程有很多,也很详细,但还是推荐看官方文档,单就爬虫而言,看完官方文档的example够用了。
使用selenium的思路就是利用selenium对浏览器的操作,模拟人下拉页面,从而让网站实现动态加载。

首先要使用selenium必须安装对应浏览器的webdriver,官方文档1.3节中有相应说明,由于一些原因,在这里使用的是Microsoft edge浏览器,edge的webdriver与其他几家不同,下载下来是个exe文件,需要放到path中存在的文件夹中才能顺利杯selenium调用,我选择的是放在python文件夹中。

对于模拟人下拉页面,主要思路是,获取滚动条的高度,然后让滚动条的深度=高度,于是滚动条到底了,触发页面动态加载。

def down_page(driver, times):
    for i in range(0, times+1):
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(1)

实际上是在selenium中使用的javascript来对浏览器进行操作。
driver是通过webdriver.Edge()创建的drivers对象
times是向下滑动的次数
execute_script()是使用javascript脚本进行
使用方法如下:

def main():
    url = 'https://www.zhihu.com'
    driver = webdriver.Edge()
    driver.get(url)
    down_page(driver, 10)
    html = driver.page_source

几个要点

虽然参考了许多资料,但是还是遇到了许多问题,这里进行一下记录与说明。

注意cookies

selenium对cookies的保存方式与requests不同:
requests是直接以字典形式存储:

{'name':'value','name':'value'}

selenium则是以字典列表存储,其中每个字典存储一对name:value,形式为

[
    {'name':'namedata', 'value':'valuetat', 'path':'pathdata', 'domain':'domaindata', 'expiry': number, 'secure': False/Truse, 'httpOnly': False/Truse},
    {'name':'namedata', 'value':'valuetat', 'path':'pathdata', 'domain':'domaindata', 'expiry': number, 'secure': False/Truse, 'httpOnly': False/Truse},
]

所以,若想用driver.add_cookies()加入cookies需要先通过driver.get_cookies()获得path、domain等内容再去构建cookies。
在这里,因为edge的webdriver本身保留之前的cookies记录,所以只要之前在爬取前登录一次网页即可。
(网上有说selenium打开的窗口是全新的无cookies,可能在Firefox或者Chrome上存在,Edge不存在这个困扰)

获取网页代码

对网页代码的获取使用的是

html = driver.page_source

这个方法和requesrs.get()所获取的页面代码是有区别的(更详细)所以正则表达式得重写。

自动登录

尝试过进行自动登录,碰到了三个问题:
1.网上可查到的删除cookies的方法delete_all_cookies()和delete_cookie()是错误的,准确的说不是错的,而是这个方法在selenium中是用在Remote WebDriver中的,本地使用并没有删除cookie的方法。
猜想:都在本地了,自己在程序运行之前手动删一下,不就成了?
2.如果浏览器已经 保存密码 了,那么以下代码会出现问题:

account = driver.find_element_by_name('account')
password = driver.find_element_by_name('password')
account.clear()
password.clear()
time.sleep(1)
account.send_keys('account') #换成自己账号
password.send_keys('password')  #换成自己密码

问题在于 即使你清除了文本内容,只要你send_keys,浏览器就会自动填充密码,于是你再填充一次密码,在密码栏中就填了两遍密码,明显会密码错误。
删除浏览器记录的密码,或者干脆去掉password.send_keys语句
3.知乎的倒立汉字验证码真的很烦。。。。
怎么解决这个问题,查了一些资料,还在研究,但是并不影响本次程序的正常运行,所以留待下篇文章再说

自动登录代码(无验证码、无自动填充密码):

from selenium import webdriver
import time

def login(driver):
    time.sleep(2)
    driver.find_element_by_link_text('登录').click()
    time.sleep(1)
    driver.find_element_by_class_name('signin-switch-password').click()
    time.sleep(1)
    account = driver.find_element_by_name('account')
    password = driver.find_element_by_name('password')
    account.clear()
    password.clear()
    time.sleep(1)
    account.send_keys('account') #换成自己账号
    password.send_keys('password')  #换成自己密码
    time.sleep(5)
    driver.find_element_by_class_name('sign-button submit').click()

def main():
    url = "http://www.zhihu.com"
    driver = webdriver.Edge()
    driver.get(url)
    try:
        driver.find_element_by_link_text('登录')
    except Exception as e:
        print('已经登录')
    else:
        login(driver)

    cookie = driver.get_cookies()
    print(cookie)

if __name__ == '__main__':
    main()

这里,因为selenium中get element语句只要没有获得结果就会报错,所以不能用if判断,必须用try语句。