网站:https://www.doutula.com/photo/list/?page=1
爬取的是最新的表情包这个页面的图片
思路
1.分析需求,创建解析线程和下载线程
因为按照顺序爬取速度比较忙,所以也是第一次使用多线程来爬取,加快爬取速度。
2.空列表的预先准备
把初始url地址构造好后统一放入到一个空列表中,方便调用多个线程解析
把线程解析后的url统一放入另一个空列表中,方便调用多个线程下载
3.构造解析图片url地址函数
这个网站数据解析相对比较简单,就是普通的页面。
因为是多线程,所以函数里面在获取列表里面的值的时候要加上线程锁,获取完毕后把锁揭开。
用list.pop()方法,每次只请求列表最后一个的数据,如果列表里面是空值,则结束循环。
这里有个注意问题:最开始启动线程的时候,放url地址的列表最开始是空的,如果直接同时,会导致线程获取不到数据而直接结束。
所以解决的办法是在下载线程启动前停顿个几秒钟,让列表里面有写入值,然后才能解析。
4、构造下载图片函数
下载图片的函数原理跟解析图片函数的原理基本一致
5.线程之间尽量间隔一点时间
解析太快有时候可能会出现问题,所以尽量加一点间隔时间。
最终代码如下:
```python
import requests
from lxml import etree
import urllib.request
import os
import threading
import time
URL = 'https://www.doutula.com/photo/list/?page={}'
URL_LIST = []
PAGE_LIST = []
LOCK = threading.Lock()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3704.400 QQBrowser/10.4.3587.400'
}
# 创建文件夹,判断如果不存在,则创建文件夹
dirpath = 'images'
if not os.path.exists(dirpath):
os.mkdir(dirpath)
# 解析线程
def parse_thread():
while 1:
LOCK.acquire()
if len(URL_LIST) == 0:
LOCK.release()
break
else:
url = URL_LIST.pop() # 每次拿出最后一个
LOCK.release()
response = requests.get(url,headers = headers,timeout=100)
tree = etree.HTML(response.content)
img_list = tree.xpath('//div[@class="page-content text-center"]/div/a')
for li in img_list:
img_link = li.xpath('.//img/@data-original')[0]
# 把img_link保存到列表里面,给下载线程去下载
PAGE_LIST.append(img_link)
time.sleep(1)
# 下载线程
def download_thread():
while 1:
LOCK.acquire()
if len(PAGE_LIST) == 0:
LOCK.release()
break
else:
url = PAGE_LIST.pop()
LOCK.release()
# 保存图片
filename = url.split('/')[-1]
filepath = os.path.join(dirpath,filename)
urllib.request.urlretrieve(url,filepath)
time.sleep(1)
def main():
# 把所有url放到列表里面,方便使用多个线程解析
for i in range(1,5):
url = URL.format(i)
URL_LIST.append(url)
# 创建3个解析线程
for i in range(3):
parse = threading.Thread(target = parse_thread)
parse.start()
print('正在下载图片中。。。')
# 创建3个下载线程
for i in range(3):
download = threading.Thread(target = download_thread)
time.sleep(3) #
download.start()
if __name__ == '__main__':
main()
PS :可能少了线程销毁的工作…