Python学习之多进程并发爬虫

时间:2022-03-30 03:39:19

以前做过Python的爬虫,不过那只爬取贴吧内容,比较简单,只是用来刚开始练练手的。这段时间又重新看Python,看到了正则表达式,于是想对爬虫再深入的了解下,主要是对爬虫的线程以及进程学习。


爬虫是io密集型,所以使用多线程会提高效率,但是懂点Python的人都知道,gil的存在导致Python的多线程有点坑,这里简单的介绍下gil。
Gil 是全局解释器锁,为了数据的安全。而在Python的多线程下,每个线程的执行方式为:

  • 获取Gil
  • 执行代码知道sleep或者是Python虚拟机将其挂起
  • 释放Gil

从这里可以看得出来线程要想执行,必须要拿到Gil,但是在一个Python中只有一个Gil,而每次释放Gil锁,线程进行锁竞争、切换线程,会消耗资源,并且一个进程永远只能同时执行一个线程(拿到Gil的线程才能执行),所以Python中的多线程效率并不高。所以想要了解的话可以看下这篇博客多线程验证学习下。


下面来说下多进程的爬虫,Python中使用多进程需要multiprocessing,以及提供了process、queue、pipe、lock、pool等组件,这里主要使用的是pool,可以叫做进程池,想要了解其他的组件可以看下这篇博客http://cuiqingcai.com/3335.html


使用pool先了解下:

  • pool = Pool(n) #建立进程池,n就是代表了建立几个进程,这个n的设定一般与cpu的核数一样
  • pool.map(def,list)#把列表list里面的每一项映射到你所定义的def函数内,有点通过这句话做list各项循环的意味
  • pool.close()#关闭进程池,不再接受新的进程
  • pool.join()#主进程阻塞等待子进程安全退出,父子进程同步

    多进程爬虫实例:

这里以爬取猫眼电影中的top100电影为例子。

import json
from multiprocessing import Pool
import requests
from requests.exceptions import RequestException
import re

def get_one_page(url): #判断页面的请求状态来做异常处理
try:
response = requests.get(url)
if response.status_code == 200:#200是请求成功
return response.text
return None
except RequestException:
return None

def parse_one_page(html):
pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?<p class="name">.*?data-val.*?>(.*?)</a>'#正则表达式
+'.*?star">(.*?)</p>.*?releasetime">(.*?)</p>'
+'.*?integer">(.*?)</i>.*?fraction">(\d+)</i>.*?</dd>',re.S)
items = re.findall(pattern, html) #返回正则结果
for item in items: #对结果进行迭代,修饰
yield{
'排名:':item[0],
'电影:':item[1],
'主演:':item[2].strip()[3:],
'上映时间:':item[3].strip()[5:],
'评分:':item[4]+item[5]
}
def write_to_file(content): #写入文件“result.txt”中
with open('result.txt', 'a', encoding='utf-8') as f: #以utf-8的编码写入
f.write(json.dumps(content, ensure_ascii=False) + "\n") #json序列化默认使用ascii编码,这里禁用ascii
f.close()

def main(page):
url = "http://maoyan.com/board/4?offset=" + str(page) #page 为页码数
html = get_one_page(url)
for item in parse_one_page(html):
print(item)
write_to_file(item)

if __name__ == '__main__':
'''
for i in range(10):
main(i*10)
'''

pool = Pool() #建立进程池
pool.map(main, (i*10 for i in range(10)))#映射到主函数中进行循环

在使用多进程的时候还可以加入异步,这个还没有学习,之后会加入进来,在爬取大量的数据时候或者图片的时候会大大的提高效率。


在学习Python的爬虫可以到这上面去学习:

虫师
静觅