手把手教学 - 编写多线程爬虫

知识简介

多线程是提高爬取速度和提高爬虫效率的主要方法之一。

多进程和多线程在大部分情况下都可以加快处理效率,缩短处理时间,但是会出现通信、数据共享和加锁问题等。为了降低使用的门槛,使操作更简单,这里直接使用Python的标准库multiprocessing模块,这个模块使别人很容易利用多线程和多进程处理任务。

一般计算(CPU)密集型任务适合多进程,IO密集型任务适合多线程。

IO密集型任务:就是类似网络交互、文件读写、网络爬虫等任务,这些任务不依赖CPU进行操作,因此可以通过使用多线程来大大提升爬虫程序的效率。

资源密集型:就是指应用需要非常多的CPU计算资源,如大型图片,视频下载

计算(CPU)密集型

IO密集型任务

计算密集型,顾名思义就是应用需要非常多的CPU计算资源,在多核CPU时代,我们要让每一个CPU核心都参与计算,将CPU的性能充分利用起来,这样才算是没有浪费服务器配置,如果在非常好的服务器配置上还运行着单线程程序那将是多么重大的浪费。 对于计算密集型的应用,完全是靠CPU的核数来工作,所以为了让它的优势完全发挥出来,避免过多的线程上下文切换,比较理想方案是:线程数= CPU核数+1 也可以设置成CPU核数*2,这还是要看JDK的使用版本,以及CPU配置(服务器的CPU有超线程)。对于JDK1.8来说,里面增加了一个并行计算,计算密集型的较理想线程数 = CPU内核线程数*2

对于IO密集型的应用,就很好理解了,我们现在做的开发大部分都是WEB应用,涉及到大量的网络传输,不仅如此,与数据库,与缓存间的交互也涉及到IO,一旦发生IO,线程就会处于等待状态,当IO结束,数据准备好后,线程才会继续执行。因此从这里可以发现,对于IO密集型的应用,我们可以多设置一些线程池中线程的数量,这样就能让在等待的这段时间内,线程可以去做其它事,提高并发处理效率。 那么这个线程池的数据量是不是可以随便设置呢?当然不是的,请一定要记得,线程上下文切换是有代价的。 目前总结了一套公式,对于IO密集型应用:线程数= CPU核心数/(1-阻塞系数) 这个阻塞系数一般为0.8~0.9之间,也可以取0.8或者0.9。套用公式,对于双核CPU来说,它比较理想的线程数就是20,当然这都不是绝对的,需要根据实际情况以及实际业务来调整。

实现线程基本步骤,分四步:

  1. 从multiprocessing.dummy 导入线程池。
  2. 创建一个线程池,完成对线程池的初始化创建工作。
  3. 把任务交给线程池。
  4. 调用join方法等待这个线程池结束工作。

下面举例:使用多线程来实现Q房网二手房源爬虫

第一步,导入线程池和其他库。给导入的线程池起一个别名为pl。

from multiprocessing.dummy import Pool as pl
import requests
from lxml import etree
import csv
import time
headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"
    }
pre_url = 'https://shenzhen.qfang.com/sale/f'

第二步,定义下载函数。

def download(url):
    html = requests.get(url,headers=headers)
    return etree.HTML(html.text)

第三步,定义数据写入函数。

def data_writer(item):
    with open('qfang_shenzhen_ershou.csv','a',encoding='utf-8',newline='') as f:
        writer = csv.writer(f)
        writer.writerow(item)

第四步,定义图片保存函数image_saver。

def image_saver(url,title):
    # 下载图片
    img = requests.get(url, headers=headers)
    with open('./Qfang_image/{}.jpg'.format(title), 'wb') as f:
        f.write(img.content)

第五步,定义爬取函数spider。

def spider(list_url):
    #下载列表页
    selector = download(list_url)
    house_list = selector.xpath("//div[@id='cycleListings']/ul//li[@class='clearfix']")
    #循环解析每套房源
    for house in house_list:
        title = house.xpath("/div[1]/p[1]/a/text()")[0]
        apartment = house.xpath("/div[1]/p[2]/span[2]/text()")[0]
        area = house.xpath("/div[1]/p[2]/span[4]/text()")[0]
        decoration_type = house.xpath("/div[1]/p[2]/span[6]/text()")[0]
        cenggao = house.xpath("/div[1]/p[2]/span[8]/text()")[0].strip()
        orientation = house.xpath("/div[1]/p[2]/span[10]/text()")[0]
        build_finishtime = house.xpath("/div[1]/p[2]/span[12]/text()")[0]
        location = house.xpath("/div[1]/p[3]/span[2]/a/text()")[0]
        total_price = house.xpath("//div[@class='show-price']")[0].strip()
        #解析并构造详情页URL
        house_url = ('http://shenzhen.qfang.com' + house.xpath("div[1]/p[1]/a/@href")[0])
        #下载详情页
        sel = download(house_url)
        time.sleep(1)
        house_years = sel.xpath("//div[@class='housing-info']/ul/li[2]/div/ul/li[3]/div/text()")[0]
        mortgage_info = sel.xpath("//div[@class='housing-info']/ul/li[2]/div/ul/li[5]/div/text()")[0]
        item = [title,apartment,area,decoration_type,cenggao,orientation,build_finishtime,location,total_price,house_years,mortgage_info]
        
        data_writer(item)
        print('正在爬取', title)
        # 解析出图片url
        image_url = house.xpath('a/img/@data-original')[0]
        image_saver(image_url,title)

第六步,编写主程序,初始化线程池。

if __name__ == '__main__':
    pool = pl(4)   #初始化线程池

初始化线程池这个参数是根据计算机的CPU核心数编写的。注意,计算机CPU核心数可以通过CPU-Z查看,也可以通过任务管理器或则 在命令行界面输入wmic进入命令行系统管理执行脚本界面, 然后我们通过cpu get *可以查看cpu的具体情况。

用列表推导式生成要爬取的URL列表,总共99页。

house_url = [pre_url+str(x) for x in range(1,100)]

接着使用线程池的map方法对要爬取的页面执行spider函数,其中线程池的map方法跟Python的map使用基本相同。

pool.map(spider,house_url)

最后,关闭线程池并等待所有线程结束。

pool.close()
pool.join()

这样就完成了整个Q房网多线程的改写,使用多线程实现了Q房网深圳市二手房房源数据的爬取。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值