Python爬虫【三】爬取PC网页版“微博辟谣”账号内容(selenium单页面内多线程爬取内容)

专题系列导引

  爬虫课题描述可见:

Python爬虫【零】课题介绍 – 对“微博辟谣”账号的历史微博进行数据采集

  课题解决方法:

微博移动版爬虫

Python爬虫【一】爬取移动版“微博辟谣”账号内容(API接口)

微博PC网页版爬虫

Python爬虫【二】爬取PC网页版“微博辟谣”账号内容(selenium同步单线程)
Python爬虫【三】爬取PC网页版“微博辟谣”账号内容(selenium单页面内多线程爬取内容)
Python爬虫【四】爬取PC网页版“微博辟谣”账号内容(selenium多线程异步处理多页面)



前言

  前面专题文章【二】中,我们编写了微博PC网页版单线程的爬虫方式。但执行速度较慢,需要约两个小时才能处理完成。
  此文我们对之前的程序略作改造,让其实现在每个页面内爬取45条数据时,进行多线程异步爬取,从而缩短每个页面的爬取时间,进而缩短整个爬取时间。

一.改造多线程

  对于整个设计流程,之前CrawlHandle串行方法内的逻辑没有问题,仍然是执行五个步骤。前面的启动driver、登录认证,后面的关闭driver、写入数据,都没有变化。

1. 入参改造

  唯一需要变化的是步骤三,此时Crawl类在爬取时,应该去选择是执行多线程还是单线程。因为是改造,所以我们用一个开关来控制Crawler是走单线程还是多线程,构造函数增加一个入参。

class Crawler:
    """
    爬取类,负责下拉、翻页、爬取,将最终结果存入df
    """

    def __init__(self, async_crawler_in_page=True):
        """
        初始化处理类所需必要的属性
        """
        self.async_crawler_in_page = async_crawler_in_page
        # 定义空df,以装载处理完的数据
        self.excel_df = DataFrame(columns=EXCEL_COLUMNS)
        # 声明driver
        self.driver = None

可以看到传入了一个新入参async_crawler_in_page,为true时走并发爬取,否则还是之前的串行爬取

2. 爬取逻辑改造

def __crawler_page_and_save_df()方法中,进行参数判断。改造如下:

    def __crawler_page_and_save_df(self):
        """
        使用selenium工具爬取当前微博页面信息
        :param page:
        :param driver:
        :return:
        """
        wb_page_start_time = time.time()  # 用于计时

        wb_list = []
        # print("开始爬取第 %i 页数据..." % self.page)
        try:
            # 1. 找出微博内容框架list,也就是每个微博内容块的集合
            # wb_cardwrap_list = driver.find_elements_by_xpath("//div[@action-type='feed_list_item']")  #尽量不用xpath,提高效率
            wb_cardwrap_list = self.driver.find_elements_by_class_name("WB_feed_type")

			# XXX 改造点:进行判断
            if self.async_crawler_in_page:
                # 多线程处理,每个线程解析一个微博内容框架,从中提取所需数据
                wb_list = self.__async_crawler_weibo_info(wb_cardwrap_list)
            else:
                # 单线程处理, 爬取框架list中的微博数据,返回wb_list
                wb_list = self.__sync_crawler_weibo_info(wb_cardwrap_list)
        except:
            print("爬取处理 第 %i 页html数据时出错! ", self.page)
            traceback.print_exc()
        else:
            print("成功爬取第 %i 页数据,爬取有效微博数:%s, 处理本页数据耗时:%s " % (self.page, len(wb_list), time.time() - wb_page_start_time))

        # 不为空则写入df中
        if wb_list:
            self.excel_df = self.excel_df.append(wb_list)
3. 新建异步爬取方法

上面调用了异步并行处理新写的方法:def __async_crawler_weibo_info()

    def __async_crawler_weibo_info(self, wb_cardwrap_list):
        """
        用多线程方式异步并发爬取微博内容
        :param wb_cardwrap_list:
        :return:
        """
        wb_list = []  # 爬取到的微博信息整理后的储存list
        thread_list = []
        for wb_count in range(len(wb_cardwrap_list)):
            # 多线程:约18秒左右处理完45条数据,比单线程串行30~36秒左右减少一半时间。 Python多线程是伪多线程
            thread = util.WBCrawlerThread(util.crawler_weibo_info_func,
                                          (wb_cardwrap_list[wb_count], self.page, wb_count))
            thread_list.append(thread)
            thread.start()
        # 取结果
        for thread in thread_list:
            thread.join()
            # 去除None,将结果存入list中
            if thread.result:
                wb_list.append(thread.result)

        return wb_list

这里用到了python的并行处理逻辑,自定义多线程工具类WBCrawlerThread,继承python自有类Thread,如下:


class WBCrawlerThread(threading.Thread):
    """
    多线程工具类
    """

    def __init__(self, func, args=()):
        super(WBCrawlerThread, self).__init__()
        self.func = func
        self.args = args

    def run(self):
        self.result = self.func(*self.args)

    def get_result(self):
        try:
            return self.result
        except Exception:
            return None

可见:

  1. 多线程类构造函数入参第一项为function函数类型,第二项为function所需要的传参。这里我们是希望多线程进行爬取每个微博块内的微博数据,因此传的入参函数就是之前已经定义好的def crawler_weibo_info_func()函数(参见前一篇博文);后面的参数即为此函数执行时所需要传入的参数。
  2. def __async_crawler_weibo_info()方法内,程序为每个wb_cardwrap_list内的对象创建一个WBCrawlerThread对象进行异步爬取,因此总共启动约45个多线程执行爬取操作
  3. 爬取完成后,将执行结果存入wb_list返回,与之前串行的数据格式和大小都一致
4. main调用改造
if __name__ == '__main__':
    # 网页版微博爬取,使用多线程
    crawler = line_crawl.Crawler(async_crawler_in_page=True)
    crawl_handle.crawl_wb_and_write_excel(crawler)

以上即为selenium单页面内多线程爬取内容的改造

二. 程序执行

  执行过程中,前面登录与单线程没区别;但当用户登录成功,下拉两次后,开始爬取时:可以看到后台不再是按微博上下顺序依次爬取、每条微博处理完成后打印日志的间隔时间也不再标准,而是有快有慢,符合并行处理的情况
在这里插入图片描述

三. 问题总结

  因为本地计算机性能有限,电脑CPU核心固定(一般都是2核到8核),因此实际上不可能45个多线程完全并发执行。并且有人分析python的Thread实现逻辑,发现其并非为真正的多线程,而是伪多线程并非真正做到多核高并发。因此最后爬取的结果来看,仍是存在先后排队 ,大多等待、少数执行的情况;但整体上多线程还是使用了更多的CPU资源,会提高整体的爬取效率。
  博主笔记本为酷睿i5二核,经测算每个页面的爬取效率可以提升一倍,原先一个页面串行爬取需要30s~36s,改造多线程后;用时约18s;结合下拉翻页,整个项目爬取时间缩短为1.7个小时


执行程序

项目工程编译了windows版本执行程序:微博数据采集python+selenium执行程序:WBCrawler.exe

  1. 执行项目前,需要下载selenium对应的浏览器驱动程序(driver.exe),并放在本机环境变量路径中,否则会报错。安装操作具体可见博客专题中的指导【二】

  2. 执行程序时,会在系统用户默认路径下,创建一个虚拟的python环境(我的路径是C:\Users\Albert\AppData\Local\Temp_MEI124882\),因此启动项目所需时间较长(约20秒后屏幕才有反应,打出提示),请耐心等待;也正因如此,执行电脑本身环境是可以无需安装python和selenium依赖包的;同时最后爬取保存的excel也在此文件夹下。

  3. 本项目采用cmd交互方式执行,因此等到屏幕显示:

     选择爬取方式:
     1. 移动版微博爬取
     2. PC网页版微博爬取(单线程)
     3. PC网页版微博爬取(页面内多线程)
     4. PC网页版微博爬取(多线程异步处理多页面)
    

后,用键盘输入1~4,敲回车执行

  1. 此exe编译时,工程代码内编写的最终excel记录保存地址为:相对工程根路径下的excel文件夹;因此当本exe执行到最后保存数据时,会因为此excel文件夹路径不存在而报错。若在工程中将保存地址改为绝对路径(例如D:\excel\),再编译生成exe执行,则最终爬取数据可以正确保存

项目工程

工程参见:微博数据采集python+selenium工程:WBCrawler.zip

本专题内对源码粘贴和分析已经比较全面和清楚了,可以满足读者基本的学习要求。源码资源为抛砖引玉,也只是多了配置文件和一些工具方法而已,仅为赶时间速成的同学提供完整的项目案例。大家按需选择

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭Albert

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值