7.4 爬取新浪微博网站
7.4.1 新浪微博网站爬取分析
爬虫实战-Selenium 登录并爬取微博首页
这一节通过爬取新浪微博网站这个实例来讲解使用 Selenium 爬取动态网页的技巧。在编写代码之前,还是先分析一下新浪微博网站。
要爬取个人首页,首先需要登录新浪微博,由于 Selenium 使用浏览器登录,浏览器会自动帮用户处理 Cookies 并保持登录,因此,登录之后就可以直接打开微博的个人页面并爬取最新的微博了。
在浏览微博的时候,如果希望查看更多微博,需要向下拉动网页的滚动条,网页会自动加载更多的微博内容,如果希望爬虫下载更多微博,就需要模拟拉动滚动条这个动作。如果希望爬虫能够不断地爬取最新发布的微博,可以在等待一段时间后直接重新打开首页,然后爬取即可。
总结起来,爬取新浪微博页面,需要 3 步:第一步,登录新浪微博网站;第二步,解析微博网站页面;第三步,定时重新打开微博首页,爬取最新发布的微博或拉动滚动条爬取更多以前的微博。
下面按照这 3 步编写新浪微博网站爬虫代码。
7.4.2 新浪微博网站爬虫实现
第一步,导入需要的模块、编写保存函数。因为微博内容可能包含各种各样的表情符号从而引起保存错误,可以进行简单处理,使用 try 和 except 忽略保存错误。对于本书前面几章的代码,如果想让代码在遇到错误时能继续爬行,也可以像这样简单处理,增加 try 和 except。
from selenium import webdriver import csv import time def csv_writer(item): with open('weibo.csv', 'a', encoding='gbk', newline='') as csvfile: writer=csv.writer(csvfile) try: writer.writerow(item) except: print('写入失败')
第二步,编写登录新浪微博网站的函数。
def login(): driver.get('https://weibo.com') time.sleep(5) driver.set_window_size(1920, 1080) #设置浏览器大小 #找到用户名输入框 username = driver.find_element_by_xpath('//*[@id="loginname"]') username.send_keys('your username') #填写用户名 password = driver.find_element_by_name('password') password.send_keys('your password') #填写密码 submit = driver.find_element_by_xpath( '//*[@class="W_btn_a btn_32px"]') #找到登录按钮 print('准备登录....') submit.click() #单击登录 time.sleep(4)
在登录函数中,首先使用 driver.get 方法打开微博网站首页,然后使用 set_window_size 方法设置窗口的大小,这样做的目的是防止默认的窗口大小下有些元素显示不全;然后使用 XPath 路径分别查找和填写登录用户名和密码;最后允许用户单击登录按钮提交登录。这里使用 time.sleep() 这种固定时长的等待方式。
第三步,定义爬取、解析页面函数。这里爬取微博发布者、发布者个人主页 URL 和发布的微博内容。
def spider(): driver.get('https://weibo.com') #刷新微博网站首页 time.sleep(4) #先获取页面中所有微博代码段,然后使用循环从每一段中提取数据 all_weibo = driver.find_elements_by_xpath( '//div[@class="WB_cardwrap WB_feed_type S_bg2 WB_feed_like"]') for weibo in all_weibo: pub_id = weibo.find_elements_by_xpath( 'div[1]/div[3]/div[1]/a[1]')[0].text #解析出微博用户 id pub_id_url = weibo.find_elements_by_xpath( 'div[1]/div[3]/div[1]/a[1]')[0].get_attribute('href') #解析发布者 URL pub_content = weibo.find_elements_by_xpath( 'div[1]/div[3]/div[3]')[0].text #解析微博内容 item = [pub_id, pub_id_url, pub_content] print('成功抓取', pub_id) csv_writer(item)
下面详细解释一下 spider 函数。现在页面中有很多条微博,这里还是先抓取每一条微博的代码段,然后从这一代码段中解析发布者名称、发布者 URL、微博内容。Selenium 中使用.text 这种方法提取文本信息,使用.get_attribute 提取属性信息,最后使用 csv_write 函数保存抓取到的内容。
第四步,编写主函数。主要是初始化 driver 对象,然后每隔 5 分钟刷新一次页面,抓取最新发布的微博。
if __name__ == '__main__': driver = webdriver.Chrome(r'd:/selenium/chromedriver.exe') login() #先执行登录 while True: spider() time.sleep(300) #每隔 5 分钟执行 spider 函数
以上是简单编写的新浪微博网站爬虫,它会登录新浪微博并每隔 5 分钟刷新一次页面,爬取最新发布的微博。这样并不是很完善,有可能 5 分钟没有刷新出来几条新微博,持续的抓取就会发生重复抓取的问题。同时,上面代码没有考虑去重,爬取过程中可能产生大量重复数据。
7.4.3 爬虫的简单去重
爬虫实战-Phontomjs 的使用
如何实现爬取数据的去重呢?可以借助 Python 的 set 数据结构实现对内容的简单去重,也可以基于微博时间线去重,甚至可以爬取下来后统一去重。下面演示最常见的使用 Python 的 set 数据结构进行去重的方法。
这里选择使用微博内容进行去重判断,毕竟一个微博用户可以多次发微博,不能用微博用户 id 或者用户 URL 进行去重判断。可以直接将爬取到的微博内容加进 set 中,然后每次爬取新的微博后进行是否重复的判断。这样做有一个坏处,那就是微博内容一般比较多,直接使用微博内容去重会占用大量的内存,影响爬虫效率。建议是使用摘要算法把微博内容转换成固定长度的摘要,然后对摘要是否重复进行判断。
什么是摘要算法呢?摘要算法又被称为哈希算法,它通过一个函数把任意长度的数据转换为一个长度固定的数据串(通常用 16 进制的字符串表示)。例如微博内容是一个很长的字符串,使用摘要算法会得到一个固定长度的摘要,如果一段时间后再次爬取到了这条微博,对它使用摘要算法会计算出相同的摘要;如果再次爬取的微博内容有变化,计算出的摘要会不同于原始微博的摘要。
可见摘要算法就是通过摘要函数对任意长度的数据(微博)计算出固定长度的摘要,然后就可以对摘要进行去重判断了。摘要算法之所以能指出数据是否相同,就是因为摘要函数是一个单向函数,计算摘要很容易,但通过摘要反推原始内容是非常困难的。同时,对原始数据做一个 bit 的修改,都会导致计算出的摘要完全不同。对于爬虫去重来说,使用摘要算法能大大减少内存占用,因为无论多长的微博内容,假如使用最常见的 MD5 摘要算法,都会被映射到固定的 128 bit。
这里以 MD5 摘要算法为例,计算出一个字符串的 MD5 值,导入 hashlib。hashlib 是一个提供了一些流行的 hash 算法的 Python 标准库。
>>>import hashlib >>>str_md5 = hashlib.md5('life is short you need python'.encode('utf-8')).hexdigest() #计算字符串 MD5 值 >>>print(str_md5) '97c1a34bf007432144dfb14bbfb4fbb6'
可以看到计算 MD5 值比较简单,唯一要注意的是在计算摘要之前,需要将字符串编码为二进制形式。
下面改造新浪微博爬虫代码,使它具有去重的功能。主要改两个地方,一是程序运行开始时,初始化一个 set。
if __name__ == '__main__': is_dup = set() #新建一个空的 set driver = webdriver.Chrome(r'd:/selenium/chromedriver.exe') login() while True: spider() time.sleep(300)
二是改造 spider 函数。爬取到数据后,在调用 csv_write 保存之前增加去重判断。
def spider(): driver.get('https://weibo.com') time.sleep(5) all_weibo = driver.find_elements_by_xpath( '//div[@class="WB_cardwrap WB_feed_type S_bg2 WB_feed_like"]') for weibo in all_weibo: pub_id = weibo.find_elements_by_xpath( 'div[1]/div[3]/div[1]/a[1]')[0].text pub_id_url = weibo.find_elements_by_xpath( 'div[1]/div[3]/div[1]/a[1]')[0].get_attribute('href') pub_content = weibo.find_elements_by_xpath( 'div[1]/div[3]/div[3]')[0].text #下面计算爬取到的 pub_content 摘要 hash_content = hashlib.md5(pub_content.encode('utf-8')).hexdigest() #如果摘要不在 set 中,就将爬取到的微博保存下来 if hash_content not in is_dup: item = [pub_id, pub_id_url, pub_content] print('成功抓取:', pub_id) csv_writer(item) is_dup.add(hash_content) #最后把保存过的微博摘要加进 set 中
经过上面的改造,爬虫增加了去重判断,仅会对新的微博内容进行保存。这种去重并不是很完美,如果爬虫程序遇到问题中断或者服务器重启,set 中的内容会全部丢失,不能继续进行去重判断。如果希望将 set 中的内容持久化,可以考虑使用 Redis 缓存服务器去重,后面的 Scrapy 章节将介绍使用 Redis 缓存服务器去重的方法。
7.4.4 使用 Chrome 浏览器的 headless 模式
上面的爬虫在运行的时候会实际打开 Chrome 浏览器,这种方式执行效率不高,Chrome 浏览器 59 以上版本可以使用 headless 模式。所谓 headless 模式,就是在无界面模式下运行谷歌浏览器,本质就是将由 Chromium 和 Blink 渲染引擎提供的所有现代网页平台的特征,都转化成命令行模式执行。为了开启 Chrome 浏览器的 headless 模式,可以使用如下代码初始化 webdriver。
if __name__ == '__main__': chrome_options = ChromeOptions() chrome_options.add_argument('--ignore-certificate-errors') chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-gpu') #binary_location 指向谷歌浏览器的安装路径 chrome_options.binary_location = (r'C:\Program Files(x86)\Google' + r'\Chrome\Application\chrome.exe') driver = webdriver.Chrome( executable_path=r'd:\selenium\chromedriver.exe', options=chrome_options)
上面代码中的 chrome_options.binary_location 要指向 Chrome 浏览器的安装路径,不同的系统有不同的路径,这里要根据自己使用的系统设置一下。--disable-gpu 是用来处理一些 bug 时暂时需要的命令,在之后的 Chrome 浏览器版本中就不需要了。
这样初始化 webdriver 后,就可以进入 headless 模式了。用户可以继续运行上面新浪微博的代码,但不会看到实际打开浏览器等一系列的操作。这种模式下爬虫的执行效率更高,如果使用隐式等待或者显式等待替换掉固定时长等待,整个爬虫的爬取效率可大大提高。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论