<audio id="audio" title="37丨数据采集实战：如何自动化运营微博？" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/78/aa/7828b7dee7210e9a89ab6cabb8a2bbaa.mp3"></audio>

今天我来带你做一个数据采集的实战。之前我讲到可以使用第三方工具，比如八爪鱼做数据采集，也可以自己编写脚本，比如使用Python。编写Python做数据采集和自动化最大的好处就是可控性强，每个步骤都可以调试，而且可以找到问题所在并一一突破。

今天我就带你使用Python自动化运营微博。关于今天的学习，希望你能达成以下的3个学习目标：

<li>
掌握Selenium自动化测试工具，以及元素定位的方法；
</li>
<li>
学会编写微博自动化功能模块：加关注，写评论，发微博；
</li>
<li>
对微博自动化做自我总结。
</li>

## Selenium自动化测试工具

当我们做Web自动化测试的时候，可以选用Selenium或者Puppeteer工具。我在[第10篇](https://time.geekbang.org/column/article/76001)的时候简单介绍过Selenium这个工具，你可以再回顾一下。Puppeteer通过控制无头Chrome来完成浏览器的工作。这两个工具之间的区别在于：Selenium更关注程序执行的流程本身，比如找到指定的元素，设置相应的值，然后点击操作。而Puppeteer是浏览者的视角，比如光标移动到某个元素上，键盘输入某个内容等。

今天我们继续使用Selenium工具自动化模拟浏览器，重点是学习对元素的定位。在第10篇讲到Selenium WebDriver的使用时，重点是对HTML进行获取和解析，然后通过HTML中的XPath进行提取，读取相应的内容。

在今天的实战里，我们需要在微博上自动登录，加关注，发评论，发微博等，这些操作都需要在浏览器上完成，所以我们可以使用Webdriver自带的元素定位功能。

如果我们想定位一个元素，可以通过id、name、class、tag、链接上的全部文本、链接上的部分文本、XPath或者CSS进行定位，在Selenium Webdriver中也提供了这8种方法方便我们定位元素。

<li>
通过id定位：我们可以使用find_element_by_id()函数。比如我们想定位id=loginName的元素，就可以使用browser.find_element_by_id(“loginName”)。
</li>
<li>
通过name定位：我们可以使用find_element_by_name()函数，比如我们想要对name=key_word的元素进行定位，就可以使用browser.find_element_by_name(“key_word”)。
</li>
<li>
通过class定位：可以使用find_element_by_class_name()函数。
</li>
<li>
通过tag定位：使用find_element_by_tag_name()函数。
</li>
<li>
通过link上的完整文本定位：使用find_element_by_link_text()函数。
</li>
<li>
通过link上的部分文本定位：使用find_element_by_partial_link_text()函数。有时候超链接上的文本很长，我们通过查找部分文本内容就可以定位。
</li>
<li>
通过XPath定位：使用find_element_by_xpath()函数。使用XPath定位的通用性比较好，因为当id、name、class为多个，或者元素没有这些属性值的时候，XPath定位可以帮我们完成任务。
</li>
<li>
通过CSS定位：使用find_element_by_css_selector()函数。CSS定位也是常用的定位方法，相比于XPath来说更简洁。
</li>

在我们获取某个元素之后，就可以对这个元素进行操作了，对元素进行的操作包括：

1. 清空输入框的内容：使用clear()函数；
1. 在输入框中输入内容：使用send_keys(content)函数传入要输入的文本；
1. 点击按钮：使用click()函数，如果元素是个按钮或者链接的时候，可以点击操作；
1. 提交表单：使用submit()函数，元素对象为一个表单的时候，可以提交表单；

了解WebDriver元素定位和功能之后，我们模拟一下微博的自动登录，具体代码如下：

```
from selenium import webdriver
import time
browser = webdriver.Chrome()
# 登录微博
def weibo_login(username, password):
     # 打开微博登录页
     browser.get('https://passport.weibo.cn/signin/login')
     browser.implicitly_wait(5)
     time.sleep(1)
     # 填写登录信息：用户名、密码
     browser.find_element_by_id(&quot;loginName&quot;).send_keys(username)
     browser.find_element_by_id(&quot;loginPassword&quot;).send_keys(password)
     time.sleep(1)
     # 点击登录
     browser.find_element_by_id(&quot;loginAction&quot;).click()
     time.sleep(1)
# 设置用户名、密码
username = 'XXXX'
password = &quot;XXXX&quot;
weibo_login(username, password)

```

需要说明的是，你需要填写自己的微博用户名和密码（对应username和password）。

通过HTML标签分析，我们能看到id=loginName和id=loginPassword对应着用户名和密码的输入框，获取这两个元素之后，使用send_keys()设置用户名和密码，然后通过id=loginAction找到登录按钮，获取该元素，使用click()函数点击提交。

这样我们就完成了微博的自动化登录，具体效果如下：

<img src="https://static001.geekbang.org/resource/image/38/71/380827a5713e41896dfe5436a4589471.gif" alt="">

## 微博自动化运营：加关注，写评论，发微博

我们已经基本了解了Selenium WebDriver是如何获取指定元素，并且对元素进行操作的。下面我们撰写一下微博自动加关注，写评论，发微博的功能代码，具体模块如下：

<img src="https://static001.geekbang.org/resource/image/61/4e/61ed79746527371c7bf1c173355f6c4e.jpg" alt=""><br>
我们刚才完成了weibo_login微博登录模块的编写，现在来看下加关注的功能，具体代码如下：

```
# 添加指定的用户
def add_follow(uid):
    browser.get('https://m.weibo.com/u/'+str(uid))
    time.sleep(1)
    #browser.find_element_by_id(&quot;follow&quot;).click()
    follow_button = browser.find_element_by_xpath('//div[@class=&quot;m-add-box m-followBtn&quot;]')
    follow_button.click()
    time.sleep(1)
    # 选择分组
    group_button = browser.find_element_by_xpath('//div[@class=&quot;m-btn m-btn-white m-btn-text-black&quot;]')
    group_button.click()
    time.sleep(1)
# 每天学点心理学UID
uid = '1890826225' 
add_follow(uid)

```

这里有两点你需要注意。

第一点是如何找到用户的UID呢？

在微博中，用户是用UID做唯一标识的。代码中我随机指定了一个UID，你也可以指定其他的UID。那么如何获取用户的UID的呢？

你可以点击任何一个微博用户，查看他的URL链接，比如链接是[https://weibo.com/u/5020181423](https://weibo.com/u/5020181423) ，那么u后面的数字5020181423即为用户的UID。你也可能遇到[https://weibo.com/ixinli](https://weibo.com/ixinli)（每天学点心理学）这样的链接情况，那么通过查看他的粉丝即可以显示出UID，比如这个微博的粉丝链接是[https://weibo.com/1890826225/fans](https://weibo.com/1890826225/fans)，那么UID对应的就是1890826225。

第二个需要注意的是使用XPath定位元素定位。

我们如何找到“关注”这个按钮的元素标识呢？在Chrome浏览器中，在“关注”按钮用鼠标右键点击，选择“检查”查看这个元素对应的代码。通过分析，你能看到这个元素的div标签中的class属性为m-add-box m-followBtn，那么你可以通过find_element_by_xpath(’//div[@class=“m-add-box m-followBtn”]’)获取这个元素。关注之后，程序会弹出选择分组的页面，如下所示。

通过同样的方法，你可以查看“取消”这个按钮对应的HTML标签特征，然后通过find_element_by_xpath定位，使用click()函数提交。这样我们就关注了一个指定的用户。

<img src="https://static001.geekbang.org/resource/image/8e/0a/8ed67303e83dd8e7aee9338802c2cb0a.png" alt=""><br>
接下来，我们继续完成写评论和发微博的模块代码：

```
# 给指定某条微博添加内容
def add_comment(weibo_url, content):
    browser.get(weibo_url)
    browser.implicitly_wait(5)
    content_textarea = browser.find_element_by_css_selector(&quot;textarea.W_input&quot;).clear()
    content_textarea = browser.find_element_by_css_selector(&quot;textarea.W_input&quot;).send_keys(content)
    time.sleep(2)
    comment_button = browser.find_element_by_css_selector(&quot;.W_btn_a&quot;).click()
    time.sleep(1)
 
# 发文字微博
def post_weibo(content):
    # 跳转到用户的首页
    browser.get('https://weibo.com')
    browser.implicitly_wait(5)
    # 点击右上角的发布按钮
    post_button = browser.find_element_by_css_selector(&quot;[node-type='publish']&quot;).click()
    # 在弹出的文本框中输入内容
    content_textarea = browser.find_element_by_css_selector(&quot;textarea.W_input&quot;).send_keys(content)
    time.sleep(2)
    # 点击发布按钮
    post_button = browser.find_element_by_css_selector(&quot;[node-type='submit']&quot;).click()
    time.sleep(1)
# 给指定的微博写评论
weibo_url = 'https://weibo.com/1890826225/HjjqSahwl'
content = 'Gook Luck!好运已上路！'
# 自动发微博
content = '每天学点心理学'
post_weibo(content)

```

这个环节里，同样也有一些需要说明的地方。

如何找到某条微博的链接呢？你可以在某个用户微博页面中点击时间的链接，这样就可以获取这条微博的链接。

<img src="https://static001.geekbang.org/resource/image/63/a9/633528f33648263152cd92b353030ba9.png" alt=""><br>
如何定位评论区中输入框元素的位置呢？

我们将鼠标移动到评论框中，在Chrome浏览器中点击右键“查看”，可以看到这个textarea的class是W_input，使用find_element_by_css_selector函数进行定位，然后通过send_keys设置内容。

最后就是发微博的流程。我们可以观察到点击微博页面的右上角的按钮后，会弹出一个发微博的文本框，设置好内容，点击发送即可。发微博的函数和写评论的代码类似，使用find_element_by_css_selector()函数定位，通过send_keys()设置内容的设置，最后通过click()发送。

上面的代码你可以自己模拟下，在实际运行过程中，你可能会遇到各种情况，比如下面这种情况：

<img src="https://static001.geekbang.org/resource/image/14/04/1453a3253b24aba642b6bf477b5add04.png" alt=""><br>
微博自动化运营是一个系统的工程，需要考虑到各种情况，比如相同的内容发布需要间隔10分钟以上；关注了一个用户之后，就无法对他二次关注，可以判断是否已经关注过，再关注操作；因为操作频繁导致需要输入验证码的情况等。

另外，微博自动化运营只是自动化运营的开始，实际上在微信上我们也可以做同样的操作。比如监听微信群的消息，自动发微信等。实际要考虑的问题比微博的要复杂。

## 总结

今天我带你进行了微博自动化运营的实战，通过实战你对使用Selenium WebDriver应该更有了解，包括如何定位指定的元素，对元素进行各种操作等。同时我们使用了implicitly_wait函数以及time.sleep()函数让浏览器和程序等待一段时间，完成数据加载之后再进行后续的操作，这样就避免了数据没有加载完，导致获取不到指定元素的情况。

通过三个模块（加关注、写评论、发微博）的编写，你能了解如何使用工具完成自动化的操作。在实际的过程中，可能会遇到各种复杂情况，这些都需要你在运行过程中慢慢体会。

自动化运营是个细致的活儿，我在之前的加餐文章中也提到过。如果真的想要实现自动化，还需要解决反垃圾的清理问题等，你可以再回顾一下。

<img src="https://static001.geekbang.org/resource/image/33/d5/33ee64c5a434e1a7093594499e9c05d5.png" alt=""><br>
最后留两道题吧。今天我们对微博加关注这一模块编写了代码，同样我们也可能会对某个指定用户的UID做取消关注的操作，请你使用今天讲的元素定位和操作功能，编写相应的代码。

通过今天自动化测试工具的学习，你有怎样的收获和总结呢？

欢迎在评论区与我分享你的答案，也欢迎点击”请朋友读“，把这篇文章分享给你的朋友或者同事，一起实战交流一下。


