前言
在实现爬虫时,解决反爬问题是我们经常要面对的。如果使用传统的 Requests 模块进行爬虫,我们要详细研究请求方法、请求参数等内容。而如果我们使用 Selenium 进行爬虫,我们只需要关注用户的操作,我们可以模拟人操作浏览器。这样我们就可以减少很多应付反爬的内容。
文章内容如下:
- 下载 Web Driver
- Selenium 的简单使用
- Selenium 中的选择器
- Selenium 中的动作链
- 使用 Selenium 实现自动登录
- 使用 Selenium 实现图片爬虫
下载 WebDriver
Selenium 原本是用来进行网站自动化测试的工具,后来人们发现了 Selenium 在爬虫方面的潜力,因此它也能胜任一些爬虫工作。
在我们使用 Selenium 时,会打开一个浏览器,然后模拟人鼠标和键盘操作,因此这种方式的爬虫难以被察觉。
Selenium 支持许多浏览器,本次 Chat 将使用 Chrome 浏览器作为例子。首先我们需要下载浏览器驱动,下载地址如下:
http://chromedriver.storage.googleapis.com/index.html
下载驱动时需要对应你的浏览器版本,比如我的版本如下:
可以看到版本是 87,因此我们需要找到如下链接:
选择对应版本的浏览器驱动,放在 Python 的根目录下即可。
接下来我们还需要下载 Selenium 模块,只需要执行下面语句就好了:
pip install selenium
下面我们看看如何使用。
Selenium 的简单使用
下载好驱动后,我们就可以开始使用了。如果你把驱动放在 Python 的根目录下,那你可以直接运行下面的代码:
from selenium.webdriver import Chrome
# 创建浏览器(浏览器驱动)
chrome = Chrome()
# 打开百度的页面
chrome.get('https://www.baidu.com')
运行后程序会帮我们自动打开浏览器,并打开百度的页面。我们可以看一下每句代码的意思。
首先我们需要导入需要使用的浏览器驱动类:
from selenium.webdriver import Chrome
因为要使用的是 Chrome,所有这里导入 Chrome。我们可以创建一个浏览器对象(驱动),任何调用 get 方法,传入页面 url。
我们还可以找到百度的搜索框进行搜索:
from selenium.webdriver import Chrome
# 导入 selenium 中的键
from selenium.webdriver.common.keys import Keys
# 创建一个浏览器
chrome = Chrome()
# 打开百度页面
chrome.get('https://www.baidu.com')
# 找到输入框并输入关键词,再按 ENTER
chrome.find_element_by_xpath('//*[@id="kw"]').send_keys('星际穿越', Keys.ENTER)
上面的代码在后面我们会详细说,我们先看一下效果:
可以看到我们打开了一个页面。但是我们是怎么找到元素的呢?其中 find\_element\_by\_xpath 是寻找元素的操作,而 send_keys 对元素的具体操作。下面我们先来看看寻找元素的操作。
Selenium 中的选择器
在 Selenium 中有很多种选择器,我们可以通过 css、tag、xpath 等属性进行选择。在上面的例子种我们就是通过 xpath 进行选择。
下面我们看看具体代码吧:
from selenium.webdriver import Chrome
from selenium.webdriver.common.keys import Keys
chrome = Chrome()
chrome.get('https://www.baidu.com')
# 通过 xpath 查找元素
chrome.find_element_by_xpath('xpath')
# 通过 id 查找元素
chrome.find_element_by_id('id')
# 通过 class_name 查找元素
chrome.find_element_by_class_name('class')
# 通过标签名查找数据
chrome.find_element_by_tag_name('tag')
# 通过 name 属性查找数据
chrome.find_element_by_name('name')
上面我们没有实际查找内容,只是写了一些方法。我们只需要在网页源码中找到需要查找的元素的属性就可以调用相应的方法。
比如下面我们查看一下百度输入框的属性:
可以看到输入框的 class 是 s_ipt,这样我们就可以通过 class 找到输入框:
from selenium.webdriver import Chrome
from selenium.webdriver.common.keys import Keys
chrome = Chrome()
chrome.get('https://www.baidu.com')
# 通过类名找到输入框
chrome.find_element_by_class_name('s_ipt').send_keys('星际穿越', Keys.ENTER)
可以看到这次也成功了:
我们还可以通过下面的方法找到元素列表:
from selenium.webdriver import Chrome
from selenium.webdriver.common.keys import Keys
chrome = Chrome()
chrome.get('https://www.baidu.com')
chrome.find_elements_by_xpath('xpath')
chrome.find_elements_by_id('id')
chrome.find_elements_by_class_name('class')
chrome.find_elements_by_tag_name('tag')
chrome.find_elements_by_name('name')
这次我们只是将方法名加了个 s,但是这次返回的是元素集合,我们可以通过循环拿到单个元素的内容,比如下面的代码:
from selenium.webdriver import Chrome
from selenium.webdriver.common.keys import Keys
chrome = Chrome()
chrome.get('https://www.baidu.com')
imgs = chrome.find_elements_by_tag_name('img')
for img in imgs:
print(img.get_attribute('src'))
我们找到百度页面的所有 img 标签,然后把 img 的 src 输出:
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/baiduyun@2x-e0be79e69e.png
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/zhidao@2x-e9b427ecc4.png
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/baike@2x-1fe3db7fa6.png
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/tupian@2x-482fc011fc.png
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/baobaozhidao@2x-af409f9dbe.png
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/wenku@2x-f3aba893c1.png
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/jingyan@2x-e53eac48cb.png
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/yinyue@2x-c18adacacb.png
https://www.baidu.com/img/fddong_e2dd633ee46695630e60156c91cda80a.gif
https://www.baidu.com/img/fddong_e2dd633ee46695630e60156c91cda80a.gif
https://www.baidu.com/img/flexible/logo/pc/result.png
https://www.baidu.com/img/flexible/logo/pc/result@2.png
https://www.baidu.com/img/flexible/logo/pc/peak-result.png
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/qrcode/qrcode@2x-daf987ad02.png
https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/qrcode/qrcode-hover@2x-f9b106a848.png
Process finished with exit code 0
可以看到效果达到了。不过还有许多选择方式,这里就不细讲了。
Selenium 中的动作链
动作链就是模仿人的动作,比如你要登录需要先输入用户名,然后输入密码,然后再点击登录按钮。这就是一系列动作,我们可以通过动作链来实现这些动作,下面我们看看一个简单的例子:
from selenium.webdriver import Chrome
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
chrome = Chrome()
chrome.get('https://www.baidu.com')
# 创建动作链
ac = ActionChains(chrome)
# 找到要操作的标签
input = chrome.find_element_by_class_name('s_ipt')
ac.move_to_element(input).send_keys('星际穿越', Keys.ENTER).perform()
我们先创建了一个 ActionChians(动作链),我们需要将浏览器传入,表示对浏览器进行的一些列动作。
然后调用 move\_to\_element 方法,把光标移动到 input 框。该方法需要一个元素对象,因此在移动光标前我们先通过 class\_name 找到了 input 框,再把 input 框传入 move\_to_element 方法。
然后再输入内容,并点击 ENTER 键。这正好对应了我们搜索时的操作。
最后我们需要执行这些操作,这需要我们调用 perform 方法。动作链中还有其它一些操作,我们可以简单看一下:
# 点击元素
ac.click()
# 双击元素
ac.double_click()
# 点击鼠标右键
ac.context_click()
# 按住鼠标不放
ac.click_hold()
# 释放鼠标左键
ac.release()
# 将元素拖拽然后松开
ac.drag_and_drop()
从上面的方法可以看出,我们很多操作都需要先找到元素,然后再执行。当然我们可以选择使用动作连来实现对浏览器的操作,也可以使用元素自身的方法实现对浏览器的操作,比如:
# 找到元素
elem = chrome.find_element_by_class_name('name')
# 点击元素
elem.click()
现在我们知道了通过动作链实现浏览器的一些操作,我们实现一个简单的爬虫。
使用 Selenium 实现自动登录
首先我们需要知道登录的具体步骤,我们可以看一下登录页面:
可以看到并没有直接显示用户名密码的 input 框,这个时候就需要我们自己找了。在二维码下方可以看到一个账号密码登录的字样,我们点击之后发现了输入框,然后后面的操作就很简单了:
- 找到了“账号密码登录”,并点击
- 找到用户名密码的 input 框,并输入用户名密码
- 点击登录(或者点击回车)
下面我们用动作连来实现一个模拟登录的操作:
from selenium.webdriver import Chrome
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
chrome = Chrome()
# 打开 QQ 空间
chrome.get('https://qzone.qq.com/')
# 切换 iframe
chrome.switch_to.frame('login_frame')
# 点击登录
chrome.find_element_by_xpath('//*[@id="switcher_plogin"]').click()
# 找到用户名和密码输入框
chrome.find_element_by_xpath('//*[@id="u"]').send_keys('qq 号')
chrome.find_element_by_xpath('//*[@id="p"]').send_keys('密码', Keys.ENTER)
如果你运行上面的代码会发现找不到元素 //*[@id="switcher_plogin"]
,我们可以查看一下源码:
会发现二维码部分被包含在一个 iframe 标签里面,因此我们是无法直接找到元素的,我们需要先切换到 iframe 中才能找到我们需要的元素,于是我将代码修改为下面:
from selenium.webdriver import Chrome
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
chrome = Chrome()
# 打开 QQ 空间
chrome.get('https://qzone.qq.com/')
# 切换 iframe
chrome.switch_to.frame('login_frame')
# 点击登录
chrome.find_element_by_xpath('//*[@id="switcher_plogin"]').click()
# 找到用户名和密码输入框
chrome.find_element_by_xpath('//*[@id="u"]').send_keys('qq 号')
chrome.find_element_by_xpath('//*[@id="p"]').send_keys('密码', Keys.ENTER)
会发现这次我们成功实现了自动登录。
使用 Selenium 实现图片爬虫
下面我们再来看一个爬虫,我们使用 Selenium 实现图片爬虫。其实图片爬虫不是 Selenium 擅长的部分,因为 Selenium 是基于浏览器的爬虫。因此会打开浏览器,当我们爬取的页面较多时 Selenium 的效率就比较低。但是我们还是可以学习一下。
我们可以把上面通过 tag_name 查找的代码修改一下:
import requests
from selenium.webdriver import Chrome
chrome = Chrome()
# 打开 QQ 空间
chrome.get('https://www.fabiaoqing.com/')
imgs = chrome.find_elements_by_tag_name('img')
name = 0
for img in imgs:
url = img.get_attribute('src')
try:
with open('img/%s.jpg' % name, 'wb') as f:
f.write(requests.get(url).content)
except Exception as e:
pass
name += 1
这里我们借助了 requests 模块,需要额外安装:
pip install requests
我们使用 request 发送请求,获取图片的二进制内容,然后写入到一个文件当中。对于 requests 的操作,我们可以详细看一下:
import requests
resp = requests.get('url')
resp.content
我们首先导入了 requests 模块,然后调用 get 方法,传入要请求的 url,然后服务器会给我们返回一个响应信息。其中 resp.content 就是响应的二进制数据,因为是图片文件,因此我们需要使用二进制写入的模式打开文件:
with open('1.jpg', 'wb') as f
运行成功后就会发现本地多了许多图片,这就是我们爬取的图片。当然我们上面的操作非常简单,只是单纯寻找 img 标签,对于更复杂的网站,我们可以分析元素结构然后找到自己需要的标签并获取 url 进行爬取。