selenium通过人机验证爬虫

之前在学校曾经用过request+xpath的方法做过一些爬虫脚本来玩,从ios正式转前端之后,出于兴趣,我对爬虫和反爬虫又做了一些了解,然后发现了selenium这个神器。selenium原本是一款测试工具,但由于他可以较好的模拟浏览器的各种操作,完全无视对于user-agent的限制,所以现在被更多的用于爬虫脚本中。这里记录一下借助selenium库进行爬虫时碰到的一些问题以及解决方法。(拒绝恶意爬虫从我做起)

基本操作

selenium的安装不多说, pip install selenium就行。不过要注意自己的python版本,要是3.x才行。用它打开浏览器,然后通过dom操作获取需要的dom节点。

from selenium import webdriver
import time

browser = webdriver.Chrome()
browser.get("url")
node = browser.find_elements_by_id("id")
nodes = browser.find_elements_by_css_selector("css-selector")
nodelist = browser.find_elements_by_class_name("class-name")

如果需要登录的,也可以事先将账号密码写好,然后用send_keys方法进行自动输入。

browser.find_element_by_xpath('xpath-to-dom').send_keys('account')
browser.find_element_by_xpath('xpath-to-dom').send_keys('password')
browser.find_element_by_xpath('xpath-to-dom').click()

然后需要什么就直接通过dom方法来获取。不过现在很多网站的url有防爬处理,使用了不规律的url,无法像豆瓣排行榜那样直接遍历。但这个不难,用selenium就是要模拟人的操作的,真人操作的时候也不会直接输url来一页一页看,比如在线阅读的网站,一般都会有个目录页。先爬取目录页面的信息,先将正文url列表保存下来,然后再遍历列表就行。

browser.get("url")
time.sleep(5)
nodes = browser.find_elements_by_css_selector("css-selector")
urlList = []
for i in range(0,len(nodes)):
    url = nodes[i].get_attribute("href")
    urlList.append(url)

这里有个sleep,目的是是确保目录页能完全加载完。当然这个方法有点蠢,后面我使用了不同的方法来做页面加载完成的判断。

等待加载完成

页面加载完成需要时间,一定要等页面加载好了才能获取页面元素。而直接设置一个固定的sleep显然是效率极低且容易出错的。这里有几种不同的方法来自动判断页面加载的情况。

1、分析页面元素

监视我最终需要的元素有没有加载完成,加载完成了就开始后续操作。比如我要的dom节点有一个类名为'page-content',并且在整个页面中一共有两处,而我需要的是第二处。那就可以监视这个节点的加载情况。

browser.get(url)
    
    nodelist = browser.find_elements_by_class_name('page-content')
    timeout = 0.0
    while len(nodelist) < 2:
        time.sleep(0.5)
        nodelist = browser.find_elements_by_class_name('page-content')
        timeout += 0.5
        if timeout >= 10:
            break
    if timeout >= 10:
        continue
    node = browser.find_elements_by_class_name('page-content')[1]

这里设置了0.5秒的刷新周期,当然可以设置的更短,然后设置了10秒的timeout,超时自动打开下一章。

2、selenium隐性等待

browser.implicitly_wait(10)
browser.get(url)

这就很简单了,就一句话,最多等10秒,进行下一步。要是提前加载完就提前进行。这个方便是方便,但是不好用,他会等页面完全加载完才进行下一步,而事实上我只需要等正文加载完就行,所以效率上要差一点。

3、selenium显性等待

locator = (By.CLASS_NAME, 'page-content')
try:
    WebDriverWait(driver, 10, 0.5).until(EC.presence_of_element_located(locator))
finally:
    driver.close()

显性等待的好处就是可以在我需要的元素加载完的时候就进入下一步,获取元素内容,但是也有不好的地方,那就是还不够灵活。显性等待在超时的时候会抛出TimeoutException异常,在暴露的接口中没有给我定义异常处理的地方,这也是我选择自己实现一遍等待机制的原因,这样我可以对超时的异常进行处理。

人机验证

很多时候,我们会发现,在登录账号时,系统会要我们输入验证码。如果想要让脚本自动识别验证码,可能就涉及到图像识别技术,还有一些第三方的服务可以使用,也是可行的。
但有的时候,网站会使用更为复杂的人机验证。比如这样的:

阿里云智能人机验证

这样的人机验证操作非常简单,只要一下点击,但实际上更为复杂。它会对于点击的操作进行判断,参考了鼠标移动的轨迹和速度等条件,来进行人机的判断。这样的话纯代码执行还是会有些困难。
同时这类智能人机验证还会有反爬虫识别,会对浏览器头进行检测,即使设置sleep然后手动点击进行人机验证的话也会失败。这时候如果打开浏览器dev tool,在控制台输入window.navigator.webdriver,返回值会是“true”。
selenium打开的浏览器
而在正常打开的浏览器中输入这段命令,返回的会是“undefined”。
正常打开的浏览器
在这里,我找到了关于webdriver的描述:navigator.webdriver
navigator.webdriver
可以看到,webdriver属性就是用来表示用户代理是否被自动化控制,也就是浏览器最上面显示的“chrome正受到自动化测试软件的控制”,有它在,人机验证就无法通过。最关键的,这个属性还是只读的,那么就不能直接修改。所以这里要绕一下,曲线救国,通过修改目标属性的get方法,达到属性修改的目的。
修改方法
这时的webdriver属性就是undefined了,然后再进行智能人机验证,就可以通过了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容