目录
当数据的爬虫量越来越大的时候,除了要考虑存储的方式外,还需要考虑爬虫的速度问题。前面的爬虫都是串行爬取,只有当一次爬取完之后才进行下一次爬取,这样极大的限制了爬取的速度和效率。本章主要讲解多线程和多进程的概念,并通过案例对串行爬虫和多进程爬虫的性能进行对比,最后通过综合案例,讲解多进程爬取得方法和技巧。
7.1多线程与多进程
7.1.1多线程和多进程概述
当计算机运行程序时,就会创建包含代码和状态得进程。这些进程会通过计算机的一个和多个CPU执行。不过,同一时刻每个CPU只会执行一个进程,然后再不同进程间快速切换,这样就给人以多个程序同时运行的感觉。同理,在一个进程中,程序的执行也是在不同线程间进行切换的,每个线程执行程序的不同部分。
这里简单的做个类比:有一个大型工厂,该工厂负责生产玩具:同时工厂下又有多个车间,每个车间负责不同的功能,生产不同玩具的零件:每个车间里又有多个车间工人,这些工人互相合作,彼此共享资源来共同生产某个玩具零件等。这里的工厂就相当于一个网络爬虫,而每个车间相当于一个进程,每个车间工人相当于线程。这样,通过多线程和多进程,网络爬虫就能高效、快速的进行下去。
7.1.2多进程的使用方法
Python进行多进程爬虫使用了multiprocessing库,此处使用multiprocessing库的进程池方法进行多进程爬虫,使用方法的代码如下。
from multiprocessing import Pool
pool = Pool(processes=4) #创建进程池
pool.map(func,iterable[,chunksize]) #map函数运行进程,func参数为需要运行的函数,在爬虫实战中为爬虫函数。iterable为迭代参数,为url列表进行迭代
7.2综合案例-爬取简书网热评文章
7.2.1爬虫思路分析
(1)此次爬取的为简书网“首页投稿”热评文章的信息(首页投稿(暂停使用,暂停投稿) - 专题 - 简书)
(2)当手动浏览该网页时,会发现没有分页的界面,可以一直浏览下去,这说明该网页使用了异步加载技术。
打开Chrome浏览器开发者工具(F12),选择Network选项卡,如下图
通过鼠标手动下滑浏览网页,发现选项卡中加载了一些网址,后面明显标有page,所以就可以通过这个来构造URL
(3)需要爬取的信息有:标题文章、文章内容、钻石数、用户ID、评论数、点赞数、打赏数
7.2.2爬虫代码及分析
import requests
import pymongo
from lxml import etree
from multiprocessing import Pool
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'
}
client = pymongo.MongoClient('localhost',27017)
mydb = client['mydb']
jianshu_shouye = mydb['jianshu_shouye'] #创建数据库和集合
def get_info(url):
html = requests.get(url, headers=headers)
selector = etree.HTML(html.text)
infos = selector.xpath('//ul[@class="note-list"]/li') # 取大标签循环
for info in infos:
title = info.xpath('div/a/text()')[0]
content = info.xpath('div/p/text()')[0]
zuanshis = info.xpath('div/div/span[1]/text()')
if len(zuanshis) == 0:
zuanshi = 0
else:
zuanshi = zuanshis[1].strip()
author = info.xpath('div/div/a[1]/text()')[0]
comments = info.xpath('div/div/a[2]/text()')
if len(comments) == 0:
comment = 0
else:
comment = comments[1].strip()
likes = info.xpath('div/div/span[2]/text()')
if len(likes) == 0:
like = 0
else:
like = likes[0].strip()
rewards = info.xpath('div/div/span[3]/text()')
if len(rewards) == 0:
reward = "无"
else:
reward = rewards[0].strip()
data = {
'title':title,
'content':content,
'zuanshi':zuanshi,
'author':author,
'comment':comment,
'like':like,
'reward':reward
}
print(data)
jianshu_shouye.insert_one(data)
if __name__=='__main__':
urls = ['https://www.jianshu.com/c/bDHhpK?order_by=top&page={}'.format(str(i)) for i in range(1,10)]
pool = Pool(processes=2)
pool.map(get_info,urls)
代码与平常的代码没什么好说的,就是针对数据而言,我们要对爬取的东西作为判断,在简书这个网页,举个栗子,有的文章有钻石数而有的却没有,这时就需要做出来明确判断的代码,否则在某个环节会出现INdexError,如果在整个代码块上使用try···except可能就会跳过很多文章。对于不同的网页构造出合理的代码,那才是我们学习的初衷。