<audio id="audio" title="19｜http库：如何批量下载在线内容，解放鼠标（下）？" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ea/dd/ea0d70ab6e088353460878ec263b56dd.mp3"></audio>

你好，我是尹会生。

在上节课，我们学习了怎么使用“requests-html”库来批量下载图片，这足以帮你应对大部分需要批量下载互联网资源的场景了。

但是除了批量下载这一典型场景外，还有两种场景“requests-html”工具无法覆盖，其中就包括一些网站的每日签到功能，明明登录网站后点击一个按钮就能领到虚拟货币，但是每次还要手动输入账户和密码，再用鼠标点击领取按钮。相信已经学会了如何用Python访问网页的你，早就想把签到进行自动化了吧。

那么今天，我就以京东自动签到领金豆为例，为你介绍一款比“requests-html”更加强大的浏览器工具“selenium”，通过selenium我将带你实现自动化的用户登录和模拟鼠标点击功能。

## selenium的适用场景

我刚才也提到了，在你把“requests-html”库应用到各种批量下载场景之后，你会发现有两种场景下使用“requests-html”无法实现批量下载。

一种场景是，有的网页为了防止你用工具下载，会对下载工具进行检测。如果你的HTTP客户端不是浏览器，那就不允许你访问该网站下的所有内容。

另一种场景是，一些网页为了更好的交互性，就使用了JavaScript脚本语言。而JavaScript脚本语言需要在浏览器运行，才能获得服务器的数据。所以如果使用“requests-html”来获取这些数据的话，你就必须再编写Javascript脚本。

基于这两种原因，我们可以使用“selenium”来解决。这也正是为什么有了“requests-html”库之后，我还要再带你学习“selenium”库。

它的实现原理，即：通过“WebDriver”组件，把Python与浏览器连接起来，让Python来控制浏览器的行为，向浏览器发送各种模拟用户操作的指令，此时，真实的浏览器会按照Python发的各种指令对服务器进行访问。

这样既可以解决网页加密的问题，又避免了再编写Javascript脚本，弥补了“requests-html”在批量下载场景中的缺憾。无论从操作的友好程度，还是实现功能的简单程度上，都要比“requests-html”强大。

那今天这节课，我们就来学习如何使用“selenium”来实现自动签到。不过考虑到你是第一次接触“selenium”，所以在学习实现方法之前，我还是先来带你学习怎么安装和执行它。

## “selenium” 的安装与初次运行

“selenium”是基于“Webdriver”连接Python和浏览器的，而“Webdriver”的组件比较多，所以它的安装过程也要比一般的Python第三方库复杂一点。

考虑到你是第一次使用它，我就把具体的安装步骤以及注意事项，拆分成了5个步骤，保证你能顺利安装，并且能用它访问网页。

第一步：根据浏览器版本来下载“Webdriver”。

由于“selenium”会通过“Webdriver”调用你当前电脑上的浏览器访问HTTP服务器，所以必须找到和浏览器版本一致的“Webdirver”程序，然后下载下来。

我这里以工作中最常用的Chrome浏览器为例，首先要通过Chrome的“设置”—“关于”菜单查看浏览器的版本。我的Chrome浏览器版本如下图：

<img src="https://static001.geekbang.org/resource/image/21/0c/211e46d304be4ec29709295396dbc70c.png" alt="">

接着，根据我的浏览器版本“89.0”访问“Webdrive”[国内的镜像站](http://npm.taobao.org/mirrors/chromedriver/)，下载与浏览器相匹配版本的“Webdriver”。这里要注意，不同的操作系统，“Webdriver”的安装包不同，所以需要你选择对应的操作系统版本。“Webdriver”的下载网页内容截图如下：

<img src="https://static001.geekbang.org/resource/image/72/e7/724055e74881a83f406fb4b777607be7.png" alt="">

我这里以“mac”操作系统为例，下载了“chromedriver_mac64.zip”压缩包后，解压缩之后，会得到“chromedriver”文件。这个文件就是连接Python和浏览器的工具。

第二步，把“Webdriver”放在命令的搜索路径下。

“chromedriver”文件是一个可执行文件，我们需要把它放入命令的搜索路径中，这样文件才能在Python调用浏览器时被直接运行，我们就不用输入它的完整路径。

我在这里解释一下命令的搜索路径。它是每个操作系统都事先定义好的，就像它的名字一样，你在命令行运行的任意一条命令，都可以从命令搜索路径中依次查找到。

那么在mac操作系统下，我们可以使用命令行运行“echo $PATH”，来查看当前系统的命令搜索路径都有哪些。

在这个命令行中，变量“PATH”就是专门用于指定命令搜索路径的环境变量。我电脑上的命令搜索路径和搜索顺序如下。

```
SHELL$ echo $PATH
/Library/Frameworks/Python.framework/Versions/3.7/bin
:/usr/local/bin:/usr/bin
:/bin
:/usr/sbin
:/sbin

```

基于操作系统会按照上面的目录对命令进行搜索，我可以把“chromedriver”放在上面的任意一个目录当中。不过我更推荐你把它放在Python相同的目录中，便于你后续版本更新时，对它进行文件替换。

我的Python安装目录是“/Library/Frameworks/Python.framework/Versions/3.7/bin”，因此我就把“chromedriver” 放在这个目录中。放入之后，我就可以在命令行运行“chromedirver”。如果能够正确运行，则会提示运行成功。否则会提示找不到这条命令，你需要再检查一下它被存放的目录。正确运行的截图如下，供你参考。

<img src="https://static001.geekbang.org/resource/image/ca/9e/ca388b78ff7a7b2ae52bec099f78a99e.png" alt="">

在完成了最复杂的“Webdriver”安装之后，接下来就可以安装Python的库，并尝试使用Python库调用浏览器了。

第三步，安装“selenium”库。

安装“selenium”库可以继续使用pip命令。它的库和安装包同名，你可以在命令行运行以下命令，进行正确安装。

```
pip3 install selenium

```

第四步，使用“selenium”访问网页。

安装成功之后，我们可以通过任意一个网页的访问，来测试从Python到“Webdriver”再到浏览器，整个工具链是否能正确运行。我把代码贴在下方供你参考。

```
from selenium import webdriver
import time

browser =  webdriver.Chrome()

browser.get(&quot;http://www.jd.com&quot;)
time.sleep(10)
browser.quit()

```

这段代码实现了调用浏览器访问京东的网页，并在10秒后自动关闭浏览器的功能。如果能够正确运行，那么说明整个工作链是配置正确的。

这段用来访问京东来验证工作链的代码，由**导入库、浏览器的初始化、控制浏览器行为**三个部分组成。我来解释一下。

**首先是导入库。**我在代码的前2行，除了导入“selenium”库用来调用“Webdriver”外，还导入了“time”库。

导“time”库的原因就在于，我们需要使用“selenium”库模拟手工操作浏览器的流程，而手工操作会有一定的延迟。所以我就基于“time”库的“sleep()”函数，来模仿用户查看网页的行为，在打开网页等待了10秒后，再关闭网页。

**其次是浏览器的初始化。**浏览器的初始化在Python内部做了两件事情，第一个是找到之前安装的“chromedriver”文件，检查它是否可执行。第二件事情是根据调用的“Chrome()”函数，找到当前电脑中的浏览器并运行它。

**最后控制浏览器的行为**。我使用了“get()和quit()”两个函数，它们分别实现的是向服务器发起HTTP的“GET”请求，以及关闭浏览器的功能。

请求如果成功发送， 你会在“Webdriver”打开的浏览器看到网页内容，我把正确执行的结果截图放在下面供你参考。

<img src="https://static001.geekbang.org/resource/image/bc/43/bc0cc885646c5e26c3b1ced7e03b7443.png" alt="">

**第五步，****取得网页的源代码并打印。**

通过“selenium”打开浏览器之后，除了控制浏览器的行为，你还需要像上一讲的批量下载图片一样，批量获取网页的资源。这时你可以使用如下代码来获取当前浏览网页的源代码。这样在关闭浏览器之后，你仍然可以对网页的内容进行操作。代码如下：

```
content = browser.page_source
print(content)

```

掌握了“selenium”和“Webdriver”的安装和运行，接下来我们就可以利用“selenium”来实现京东的自动签到功能了。通过这一场景，来为你演示如何通过“selenium”模拟浏览器并实现自动签到。

## 如何实现京东自动签到

要想实现自动签到，你既要实现用户登录，又要模拟用户点击按钮。随着技术的日新月异，很多登录和按钮都不再使用简单的“POST”请求了。这时你就需要用“selenium”基于标签名字找到按钮的链接，像普通用户请求网站链接一样，不必理解登录和签到的内部的原理，就能实现自动化访问。

### 自动签到的原理与思路

“selenium”之所以能替代手动操作，达到自动化的效果，核心原因在于，我们可以通过“selenium”来模拟浏览器的操作。而我们可以通过对浏览器的功能拆解，把浏览器的交互行为，一一对应到“selenium”的非交互命令，之后就能实现自动化操作了。

这也正是自动签到的原理。再进一步说，“selenium”的行为越像浏览器，自动签到的功能就模拟得越完整。换言之，签到功能也就更自动化。那我们就来看看浏览器是如何实现登录和签到工作的。

使用浏览器进行登录有三个步骤，分别为：

1. 打开登录页面；
1. 切换到用户密码登录选项卡；
1. 点击登录按钮。

我们依次来分析一下这三个步骤如果用“selenium”，各自是怎么实现的。

首先，打开登录页面，在浏览器中是鼠标点击“登录”按钮实现的，它的实现是向指定的URL发送“GET”请求，发送后服务端返回网页，就是你见到的登录页面了。

其次，打开登录页面后，默认的登录方式是二维码登录。

为了能让“selenium”能够模拟，我们需要通过用户名和密码方式登录。那接下来就切换到用户密码登录这一选项卡，这一切换动作在浏览器中可以通过使用鼠标实现。不过在“selenium”中，我们需要通过“GET”请求来替代它，才能实现非交互式的登录。

登录切换页面的网页源代码以及网页，我把相关截图一并放在下方，供你参考。

<img src="https://static001.geekbang.org/resource/image/fb/31/fbde90ae5cacc859ac05d09b63159a31.png" alt="">

最后，你需要在浏览器输入用户名密码，而后点击“登录”按钮，实现登录。

由于京东的登录界面是经过加密的，所以不能使用“requests-html”直接发送“POST”请求来实现登录，这里仍然要继续使用“selenium”获取用户名、密码的输入框，输入之后，模拟点击按钮的“GET”请求到服务器之后，才能实现登录。

### 使用“selenium”模拟浏览器，实现自动登录

在了解了浏览器的原理之后，你就可以使用“selenium”模拟浏览器实现登录了。我把“selenium”登录京东的代码写在下方供你参考。

```
from selenium import webdriver
import time

browser =  webdriver.Chrome()

# 访问主页
browser.get(&quot;http://www.jd.com&quot;)
time.sleep(2)

# 访问登录页
browser.get(&quot;https://passport.jd.com/new/login.aspx?ReturnUrl=https%3A%2F%2Fwww.jd.com%2F&quot;)
time.sleep(2)

# 切换为用户密码登录
r = browser.find_element_by_xpath(
    '//div[@class=&quot;login-tab login-tab-r&quot;]')
browser.execute_script('arguments[0].click()', r)
time.sleep(2)

# 发送要输入的用户名和密码
browser.find_element_by_xpath(
    &quot;//input[@id='loginname']&quot;).send_keys(&quot;username&quot;)
time.sleep(1)
for i in &quot;password&quot;:
    browser.find_element_by_xpath(
         &quot;//input[@id='nloginpwd']&quot;).send_keys(i)
    time.sleep(1)

# 点击登录按钮
browser.find_element_by_xpath(
    '//div[@class=&quot;login-btn&quot;]/a').click()
time.sleep(10)

# 退出浏览器
browser.quit()

```

我在代码中，为了模拟浏览器的登录行为，一共使用了5个函数，每个函数都对应着“selenium”的一种操作。按照代码的运行顺序，我分别使用了请求网页、使用XPath查找标签、执行脚本、模拟键盘输入以及模拟鼠标点击五个动作，来实现登录的模拟操作。接下来我们先从请求网页开始，来为你逐个分析这五个函数。

第一个函数get() ，使用“GET”方式请求网页。当你需要点击超链接来到达新的页面时，都可以使用“get()”函数来实现。所以在代码中，请求主页和请求登录页面都是用了这个函数。

第二个函数find_element_by_xpath() 。在网页中，你需要对某一标签进行操作时，可以使用“XPath”先找到该标签。类似地，在浏览器中，“find_element_by_xpath() ”就是找到你想操作的标签。

比如我在代码中找到用户名和密码的选项卡、用户名密码的输入框、登录按钮等元素，都是用过它进行定位的。定位的方法和上节课讲的方式相同，通过浏览器的调试页面找到网页元素对应的代码，再把代码利用“XPath”提取出来即可。

第三个函数execute_script() ，是用来执行网页中自带的JavaScript脚本的。当你切换用户密码选项卡时，会发现网站的代码是用JavaScript来实现的，而你又不想再去手动编写脚本（执行“class”属性为“login-tab login-tab-r”的“div”标签下面的JavaScript脚本），那就可以使用这个函数，对第一个参数的JavaScript进行点击。

第四个函数send_keys()，用于在输入框填写内容。我在脚本中把“username”和“password”作为用户名和密码自动填入输入框中，你可以把它们替换为真正的用户名和密码。

这里需要注意的是，我对密码的填入增加了间隔时间，否则登录时会弹出图形验证码，阻止你进行自动登录。

第五个函数click() ，用于模拟鼠标点击超链接的动作，它和取得超链接中的href属性，并用“GET”方式访问是相同的功能，但是“click()”函数会比“get()”函数更直接，减少了从“a”标签再提取“href”属性的麻烦。

以上就是使用“selenium”模拟浏览器实现登录的主要思路。登录之后，我们想要实现签到的障碍就全都解决了。那么接下来我来带你继续实现自动签到功能。

### 利用“selenium”，实现自动签到

要想实现自动签到，还需要访问签到页面和点击签到按钮。这两个功能相信你在我为你分析了登录的代码之后，一定会想到访问页面可以使用“get()”函数，签到按钮可以使用“click()”函数。

我把自动签到的代码放在下面，一并提供给你。通过和登录代码的整合，你就可以利用“selenium”实现自动签到的功能了。

```
# 访问签到页面
browser.get(&quot;https://mall.jd.com/index-1000002826.html&quot;)
time.sleep(2)

# 签到并领金豆
browser.find_element_by_xpath('//div[@class=&quot;jSign&quot;]/a').click()
time.sleep(10)

```

这段代码就是访问签到页面并领取金豆的代码。需要你注意的是，访问页面和签到两个操作之间需要等待几秒，否则会因为网页加载过慢，导致签到按钮还没加载完就发送了点击动作，让自动签到失败。

## 总结

最后，我来为你总结一下本节课的主要内容。在这一讲中，我通过“selenium”实现了浏览器的模拟，并把浏览器的点击链接、用户登录、切换标签等常用功能使用“selenium”的函数和XPath转换为用Python可以控制的操作。转换之后，你就可以利用Python控制浏览器的行为，把这些需要鼠标的交互式操作编写为非交互式的代码，达到自动化控制浏览器的目的了。

除了实现自动签到以外，通过“selenium”的函数组合，你就能模拟浏览器的绝大部分操作。我也希望你能够在学完这节课后，想一想你的工作当中是否也使用了浏览器进行重复的工作，希望你能通过对本讲的学习，把它们改为自动化。

## 思考题

按照惯例，最后我来为你留一道思考题，你能否利用今天讲的模拟浏览器的方法，自动登录Github，并以“Python”作为关键字进行搜索，然后把搜索的结果保存到一个文件呢？

欢迎把你的想法和思考分享在留言区，我们一起交流讨论。也欢迎你把课程分享给你的同事、朋友，我们一起做职场中的效率人。我们下节课再见！
