本例通过多线程爬取http://www.doutula.com网站来讲解如何让使用多线程进行爬取。
首先打开这个网站,点击最新表情:
然后我们只需要找出最新表情相关的规律,比如我们点击一下第二页,url就变成了http://www.doutula.com/photo/list/?page=2,因此第几页page就等于几。
然后就要找出在一页当中这些表情是存储在哪里,先来看一下不用多线程的程序:
import requests
import os
from bs4 import BeautifulSoup
def download_img(page):
response = requests.get('http://www.doutula.com/photo/list/?page={}'.format(page))
text = response.text
soup = BeautifulSoup(text,'lxml')
div = soup.find_all('div',class_ = "page-content text-center")[0]
imgs = div.find_all('img')
for img in imgs:
try:
name = img.attrs['alt']#获取img标签的alt属性值,也就是名字
except KeyError:
pass
try :
img_url = img.attrs['data-backup']
except KeyError:
pass
try:
R = requests.get(img_url)
except UnboundLocalError:
pass
try:
with open('图片1/{}.jpg'.format(name),'wb') as f:
f.write(R.content)
except:
pass
if __name__ == '__main__':
if not os.path.exists('图片1'):
os.mkdir('图片1')
for i in range(2000):
download_img(i+1)
如果是用多线程,就用一下结构:
首先在主程序当中吧每一个待爬取页面的url定义好,然后生产者就从url队列当中去获取每一个url,再提取出每一个图片的url,拿到这些url以后再把url添加到全局的队列当中,这个队列是专门用来存储每个表情的url的,存储完了以后再使用消费者从这个队列当中去取出每个表情的url,然后下载到本地。
下面是其完整代码:
import requests
import os
from bs4 import BeautifulSoup
from queue import Queue
import threading
#定义生产者类,在创建生产者线程的时候就把两个队列传到这里面来
class Procuder(threading.Thread):
def __init__(self,page_queue,img_queue,*args,**kwargs):#*args,**kwargs代表包括任意参数
super(Procuder,self).__init__(*args,**kwargs)
self.page_queue = page_queue
self.img_queue = img_queue
#用于解析url
def run(self):
#因为每一个线程都需要不断地去从页面队列当中去取url来解析
while True:
#为了避免生产者线程一直处于死循环状态,也就是当把url解析完毕时就应该停止了。当队列为空时退出循环
if self.page_queue.empty():
break
url = self.page_queue.get()
#拿到url后就获取我们想要的图片的url
self.parse_page(url)#去解析每一页的url
def parse_page(self,url):
response = requests.get(url)
text = response.text
soup = BeautifulSoup(text,'lxml')
div = soup.find_all('div',class_ = "page-content text-center")[0]
imgs = div.find_all('img')
for img in imgs:
try:
name = img.attrs['alt']#获取img标签的alt属性值,也就是名字
except KeyError:
pass
try :
img_url = img.attrs['data-backup']
except KeyError:
pass
#拿到每个图片的名称和链接以后就要添加到队列当中
try:
self.img_queue.put((name,img_url))#传一个元组进去
except:
pass
#消费者
class Consumer(threading.Thread):
def __init__(self,page_queue,img_queue,*args,**kwargs):#*args,**kwargs代表包括任意参数
super(Consumer,self).__init__(*args,**kwargs)
self.page_queue = page_queue
self.img_queue = img_queue
#不断地从img_queue里面取出url然后下载下来
def run(self):
while True:
if self.img_queue.empty() and self.page_queue.empty():
break
filename,img_url = self.img_queue.get()#因为返回的是一个元组
try:
R = requests.get(img_url)
except UnboundLocalError:
pass
try:
with open('图片2/{}.jpg'.format(filename), 'wb') as f:
f.write(R.content)
print('{}下载完成'.format(filename))
except:
pass
if __name__ == '__main__':
#定义两个队列
page_queue = Queue(100)#100个页面的url队列
img_queue = Queue(1000)#图片的url
if not os.path.exists('图片2'):
os.mkdir('图片2')
for i in range(100):
url = 'http://www.doutula.com/photo/list/?page={}'.format(i+1)
page_queue.put(url)#将每一页的url添加进去
#然后创建生产者,把页面请求下来再获取到每个表情的url
for x in range(5):#创建5个生产者
t = Procuder(page_queue,img_queue)
t.start()
for x in range(5):#创建5个消费者
t = Consumer(page_queue,img_queue)
t.start()