:点击上方[Python爬虫数据分析挖掘]→右上角[...]→[设为星标⭐]
多线程爬取表情包
有一个网站,叫做“斗图啦”,网址是:https://www.doutula.com/
。这里面包含了许许多多的有意思的斗图图片,还蛮好玩的。有时候为了斗图要跑到这个上面来找表情,实在有点费劲。于是就产生了一个邪恶的想法,可以写个爬虫,把所有的表情都给爬下来。这个网站对于爬虫来讲算是比较友好了,他不会限制你的headers
,不会限制你的访问频率(当然,作为一个有素质的爬虫工程师,爬完赶紧撤,不要把人家服务器搞垮了),不会限制你的IP地址,因此技术难度不算太高。但是有一个问题,因为这里要爬的是图片,而不是文本信息,所以采用传统的爬虫是可以完成我们的需求,但是因为是下载图片所以速度比较慢,可能要爬一两个小时都说不准。因此这里我们准备采用多线程爬虫,一下可以把爬虫的效率提高好几倍。
文章目录
1、爬取准备
2、完整代码
3、图片辅助分析
4、运行结果
1、爬取准备
爬取目标
https://www.doutula.com/article/list/
批量爬取
温馨提示:爬取过程中保持网络通畅,不然会爬取失败!
这里多线程我们使用的是Python
自带的threading
模块。并且我们使用了一种叫做生产者和消费者的模式,生产者专门用来从每个页面中获取表情的下载链接存储到一个全局列表中。而消费者专门从这个全局列表中提取表情链接进行下载。并且需要注意的是,在多线程中使用全局变量要用锁来保证数据的一致性。
ps:感兴趣的小伙伴可以试试线程池
使用线程池
线程池或进程池是用于在程序中优化和简化线程/进程的使用。通过池,你可以提交任务给executor。池由两部分组成,一部分是内部的队列,存放着待执行的任务;另一部分是一系列的进程或线程,用于执行这些任务。池的概念主要目的是为了重用:让线程或进程在生命周期内可以多次使用。它减少了创建创建线程和进程的开销,提高了程序性能。重用不是必须的规则,但它是程序员在应用中使用池的主要原因。
2、完整代码
import requests
from threading import Thread
from queue import Queue
from lxml import etree
import time
import os
import random
# 请求头
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36"
}
path = "./图片"
#数据采集
class CrawlInfo(Thread):
def __init__(self ,url_queue ,html_queue):
Thread.__init__(self)
self.url_queue = url_queue
self.html_queue = html_queue
def run(self):
# 不存在就创建
if not os.path.exists(path):
# mkdir是创建文件夹
os.mkdir(path)
while self.url_queue.empty() == False:
url = self.url_queue.get()
response = requests.get(url,headers=headers)
#请求成功状态码为200则进行put
if response.status_code == 200:
self.html_queue.put(response.text)
# 数据解析、保存
class ParseCrawl(Thread):
def __init__(self, html_queue):
Thread.__init__(self)
self.html_queue = html_queue
def run(self):
#队列不为空则继续遍历
while self.html_queue.empty() == False:
data = etree.HTML(self.html_queue.get())
#查看图1,根据class定位获取其下的所有a标签
a_list = data.xpath("//div[@class='col-sm-9 center-wrap']/a")
#遍历a标签
for a_singer in a_list:
#查看图2,重新写xpath根据class定位
#text():获取文本值
name = a_singer.xpath(".//div[@class='random_title']/text()")[0]
#替换掉name中的u200bu200bu200b字符串
name = str(name).replace("u200bu200bu200b","")
#拼接新的路径
new_path = path + "/" + name
#新路径不存在就创建
if not os.path.exists(new_path):
os.mkdir(new_path)
#获取图片url,查看图3
#根据class值定位到所有img的父标签,在根据img的class拿到所有img的data-original属性即图片url,这里不拿src属性是因为爬取时拿到的是图片还未加载完毕的url
img_url_list = a_singer.xpath(".//div[@class='random_article']//img[@class='lazy image_dtb img-responsive']/@data-original")
#遍历img_url_list
for img in img_url_list:
#由于图片有jpg、png、gif格式,这里我们根据'.'分割获取图片格式后缀
suffix = "." + str(img).split(".")[-1]
#发起请求,content为二进制
re = requests.get(img,headers=headers).content
#图片命名为随机数str(random.randint(1,500))
with open(new_path + "/" + str(random.randint(1,500)) + str(suffix), "wb") as f:
#保存图片
f.write(re)
#开始
if __name__ == '__main__':
start = time.time()
url_queue = Queue()
html_queue = Queue()
base_url = "https://www.doutula.com/article/list/?page={}"
#我们这只爬取13页
for i in range(1,14):
print("正在爬取第{}页".format(i))
new_url = base_url.format(i)
url_queue.put(new_url)
crawl_list = []
for i in range(3):
Crawl = CrawlInfo(url_queue, html_queue)
crawl_list.append(Crawl)
Crawl.start()
for crawl in crawl_list:
crawl.join()
parse_list = []
for i in range(3):
parse = ParseCrawl(html_queue)
parse_list.append(parse)
parse.start()
for parse in parse_list:
parse.join()
end = time.time()
print("爬取时间:{}".format(end- start))
以上这份代码。可以完整的运行了。采用多线程,在一张图片下载的时候,就完全可以去请求其他图片,而不用继续等待了。因此效率比较高。
3、图片辅助分析
图1
图2
图3
4、运行结果
写在最后:
本教程采用多线程来完成表情的爬取,可以让爬取效率高出很多倍。Python
的多线程虽然有GIL
全局解释器锁,但在网络IO
处理这一块表现还是很好的,不用在一个地方一直等待。以上这个例子就很好的说明了多线程的好处。
【各种爬虫源码获取方式】
识别文末二维码,回复:爬虫源码
- END -
欢迎关注公众号:Python爬虫数据分析挖掘,方便及时阅读最新文章
记录学习python的点点滴滴;
回复【开源源码】免费获取更多开源项目源码;