前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >外行学 Python 爬虫 第七篇 开启多线程加快爬取速度

外行学 Python 爬虫 第七篇 开启多线程加快爬取速度

作者头像
keinYe
发布2019-08-01 11:17:44
1.1K0
发布2019-08-01 11:17:44
举报
文章被收录于专栏:keinYe

经过上一篇文章外行学 Python 爬虫 第六篇 动态翻页我们实现了网页的动态的分页,此时我们可以爬取立创商城所有的原件信息了,经过几十个小时的不懈努力,一共获取了 16万+ 条数据,但是软件的效率实在是有点低了,看了下获取 10 万条数据的时间超过了 56 个小时,平均每分钟才获取 30 条数据。

注:软件运行的环境是搬瓦工的虚拟主机,CPU: 2x Intel Xeon , RAM: 1024 MB,Debian 9

软件的运行效率不高,那么时间都花费在什么上面了,爬虫软件本身并不是计算密集型软件,时间大多数花费在与远程主机的通信上了,要想提高软件的运行效率,就要减少等待时间,此时你想到了什么?没错就是多线程,在非计算密集型应用中,使用多线程可以最大程度的节省资源同时提高软件的效率,关于线程的基本应用可以参考前面的文章 python 之进程与线程

针对多线程的修改

使用多线程后,每个线程执行的应该是不同的任务,如果是相同的任务那就是两个程序而不能说是多线程了。每个线程执行不同的任务「即爬取不同的网页」,需要线程间共享数据「在本程序中需要共享待爬队列、已获取 url 的布隆滤波器等」。因此我们需要多当前的软件进行修改,以使待爬队列和布隆滤波器可以在多个线程之间共享数据。

要想在多线程之间共享待爬队列和布隆滤波器,需要将其从当前的实例属性修改为类属性,以使其可以通过类在多个线程中访问该属性。关于类属性和实例属性可以参考 Python 类和实例 这篇文章。

将待爬队列和布隆滤波器设置为类属性的代码如下:

代码语言:javascript
复制
class Crawler:
    url_queue = Queue()
    bloomfilter = ScalableBloomFilter()
    ...

在使用的过程中通过类名来访问类属性的值,示例代码如下:

代码语言:javascript
复制
def __init__(self, url_count = 1000, url = None):
        if (Crawler.max_url_count < url_count):
            Crawler.max_url_count = url_count

        Crawler.url_queue.put(url)

在多线程中,当前的类属性有多个线程共享,任何一个类属性都有可能被任何线程修改,因此线程之间共享数据最大的危险在于多个线程同时修改一个数据,把数据给修改乱了。由于 Queue 是一个适用于多线程编程的先进先出的数据结构,可以在生产者和消费者线程之间安全的传递消息或数据,因此我们无需对队列进行操作,但是布隆滤波器是非线程安全的数据,此时我们就需要在修改布隆滤波器的地方加上线程锁,以保证在同一时刻只有一个线程能够修改布隆滤波器的数据,代码如下:

代码语言:javascript
复制
def url_in_bloomfilter(self, url):
        if url in Crawler.bloomfilter:
            return True
        return False
    def url_add_bloomfilter(self, url):
        Crawler.lock.acquire()
        Crawler.bloomfilter.add(url)
        Crawler.lock.release()

在所有需要判断 url 是否已经爬取过的地方调用 urlinbloomfilter,当需要向布隆滤波器中添加 url 时调用 urladdbloomfilter 方法,保证布隆滤波器的数据不会被错误修改。

对爬虫类 Crawler 修改完成后,就是真正启动多线程的时候,在 main.py 文件中将代码修改为如下内容:

代码语言:javascript
复制
def main():
    with open('database.conf','r') as confFile:
        confStr = confFile.read()
    conf = json.JSONDecoder().decode(confStr)
    db.init_url(url=conf['mariadb_url'])

    crawler1 = Crawler(1000, url='https://www.szlcsc.com/catalog.html')
    crawler2 = Crawler(1000, url='https://www.szlcsc.com/catalog.html')
    thread_one = threading.Thread(target=crawler1.run)
    thread_two = threading.Thread(target=crawler2.run)
    thread_one.start()
    thread_two.start()
    thread_one.join()
    thread_two.join()

以上代码中首先建立了对数据库的连接,然后创建了两个 Crawler 类的的实例,最后创建了两个线程实例,并启动线程。

修改后的执行结果

本次软件开启了两个线程同时运行,同样获取 10 万条数据,一共花费了 29 个小时,平均每分钟获取了 57.5 条数据,相比单线程效率提高了 191.7%,总体来说效率提高还是非常明显的。

最终在花费 50 小时 30 分钟,从立创商城上获取十六万五千条数据后,程序执行完成。

从立创商城商品目录页面可知立创商城上共计有十六万七千个元件。程序执行完成后共计获取十六万五千条数据,可以说完成了预期设计目标。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 keinYe 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 针对多线程的修改
  • 修改后的执行结果
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档