4.3 下载房源图片和实现多线程爬虫
4.3.1 下载房源图片

爬虫实战-多线程和图片下载
这一节接着前面介绍爬取 Q 房网的例子来讲解如何下载图片和实现多线程爬虫。
现在希望下载二手房房源的图片。为了简单讲解,仅下载二手房列表页的展示图,也就是图 4-7 中左侧的小图片。

图 4-7 二手房房源信息
要下载图片,首先需要获取图片的 URL,其次请求这个 URL 获得图片内容,最后保存图片,下面用代码来演示。在上节 spider 函数代码中的 for 循环里增加如下代码。
#首先解析出图片的 URL
image_url = house.xpath('a/img/@data-original')[0]
#下载图片
img = requests.get(image_url, headers=headers)
with open('./Qfang_image/{}.jpg'.format(apartment), 'wb') as f:
f.write(img.content) #保存图片
... 在这里首先使用 XPath 路径获取图片的 URL,然后使用 requests.get 获取这张图片,最后新建了一张图片并把获取的图片保存起来。这里把获取到的图片保存在了当前程序运行目录下的 Qfang_image 文件夹里,并使用图片所属房源标题作为保存图片的名称。
以上就是下载图片的代码,比较简单,读者要注意给图片起个合适的名字,防止重名。
4.3.2 实现简单多线程爬虫
有时候会遇到爬虫的爬取速度太慢的情况,如上面抓取房源图片会大大降低爬虫的速度。这时可以通过使用多线程来加快爬取速度。下面介绍如何实现一个多线程的 Python 爬虫来提高爬虫的效率。
1. 基本知识
多进程和多线程在大部分情况下都能加快处理效率,缩短处理时间,但是会出现通信、数据共享及加锁问题等。为了降低使用门槛,可以使用 Python 的标准库 multiprocessing 模块,这个模块让人们很容易利用多进程和多线程来处理任务。
那么应该使用多进程还是多线程呢?一般计算(CPU)密集型任务适合多进程,IO 密集型任务适合多线程。所谓 IO 密集型任务,就是类似网络交互、文件读写、网络爬虫等任务,这些任务不依赖 CPU 的操作,因此可以通过使用多线程来大大提高爬虫程序的效率。
2. 线程实现
下面重点讲解与爬虫有关的线程池的用法。
首先了解一下线程实现的基本步骤。
(1)从 multiprocessing.dummy 导入线程池。
(2)创建一个线程池,完成对线程的初始化创建工作。
(3)把任务交给线程池。
(4)调用 join 方法等待这个线程结束工作。
下面使用多线程来实现 Q 房网二手房房源爬虫。
第一步,导入线程池和其他库。给导入的线程池起一个别名 pl。
from multiprocessing.dummy import Pool as pl
from lxml import etree
import requests
import csv
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) \
AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/46.0.2490.80 Safari/537.36'}
pre_url = 'http://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_ershoufang.csv', 'a',
encoding='utf-8', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(item) 第四步,定义图片保存函数 image_saver。
def image_saver(url, xiaoqu):
img = requests.get(url, headers=headers)
with open('./Qfang_image/{}.jpg'.format(apartment), 'wb') as f:
f.write(img.content) 第五步,定义爬取函数 spider。
def spider(url):
selector = download(url)
house_list = selector.xpath("//*[@id='cycleListings']/ul/li")
for house in house_list:
apartment = house.xpath("div[1]/p[1]/a/text()")[0]
house_layout = house.xpath("div[1]/p[2]/span[2]/text()")[0]
area = house.xpath("div[1]/p[2]/span[4]/text()")[0]
region = house.xpath("div[1]/p[3]/span[2]/a[1]/text()")[0]
total_price = house.xpath("div[2]/span[1]/text()")[0]
#构造详情页 URL
house_url = ('http://shenzhen.qfang.com'
+ house.xpath("div[1]/p[1]/a/@href")[0])
sel = download(house_url)
house_year = 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 = [apartment, house_layout, area, region,
total_price, house_years, mortgage_info]
data_writer(item)
print('正在抓取', xiaoqu)
image_url = house.xpath('a/img/@data-original')[0]
image_saver(image_url, xiaoqu) 第六步,编写主程序、初始化线程池。
if __name__ == '__main__': pool = pl(4) # 初始化线程池
初始化线程池有个参数,可以根据计算机的 CPU 核心数填写。例如计算机是四核的,这里就填写 4。
用列表推导式生成要爬取的页面 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()
这样就完成了整个多线程爬虫的编写。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论