Python 多线程+队列:爬取某网站所有图片
实现思路
- download_image(url, image_dir, image_no):将图片下载页的主图下载到本地。
- get_image_url(url):拼接图片下载的 url(绝对路径)。由于网站中的图片 src 都是相对路径,因此需要在此函数中拼接图片的绝对路径。
- get_page_url(url):获取图片浏览页中的图片下载页 url 和翻页 url。
- task(queue):多线程的任务函数,调用上述 3 个函数。
代码
下载地址:https://github.com/juno3550/Crawler
1 import requests 2 import re 3 import os 4 import threading 5 import queue # Python的queue模块中提供了同步的、线程安全的队列类 6 from bs4 import BeautifulSoup 7 import traceback 8 9 10 # 队列,用来存放图片下载页url和翻页url 11 queue = queue.Queue() 12 # 种子url 13 result_urls = [] 14 # 图片命名的起始编号 15 image_no = 0 16 # 图片存储路径 17 image_dir = "e:\\crawl_image" 18 if not os.path.exists(image_dir): 19 os.makedirs(image_dir) 20 21 22 # 将图片下载到本地 23 def download_image(url, image_dir, image_no): 24 try: 25 # 访问图片并设置超时时间 26 r = requests.get(url, timeout=60) 27 # 获取图片的后缀名 28 image_ext = url.split(".")[-1] 29 # 设置下载路径与图片名称 30 image_name = str(image_no) + "." + image_ext 31 image_path = os.path.join(image_dir, image_name) 32 # 保存图片到本地 33 with open(image_path, "wb") as f: 34 f.write(r.content) 35 print("图片【%s】下载成功,保存路径【%s】" % (url, image_path)) 36 except: 37 print("图片下载失败【%s】" % url) 38 traceback.print_exc() 39 40 41 # 获取图片下载url(绝对路径) 42 def get_image_url(url): 43 # 由于网站中的图片url都是相对路径,因此需要在此函数中拼接图片的绝对路径 44 # 获取网站首页链接 45 try: 46 home_page = re.match(r"http[s]?://\w+.\w+\.com", url).group() 47 r = requests.get(url, timeout=60) 48 r.encoding = "gbk" 49 # 通过a标签获取其中的src下载路径 50 # 通过BeautifulSoup解析网页内容 51 soup = BeautifulSoup(r.text, "html.parser") 52 image_a = soup.find_all("a", attrs={"id": "img"}) # 找出id属性值为img的a标签,即主图 53 if image_a: 54 # 获得图片的相对路径 55 image_relative_url = re.search(r'src="(.+?)"', str(image_a[0])).group(1) 56 # 拼接绝对路径 57 image_abs_url = home_page + image_relative_url 58 return image_abs_url 59 except: 60 print("获取图片下载url失败【%s】" % url) 61 traceback.print_exc() 62 63 64 # 获取图片浏览页中的图片下载页url和翻页url 65 def get_page_url(url): 66 try: 67 home_page = re.match(r"http[s]?://\w+.\w+\.com", url).group() 68 r = requests.get(url, timeout=60) 69 r.encoding = "gbk" 70 soup = BeautifulSoup(r.text, "html.parser") 71 # 存储所有图片的a标签跳转url及翻页url 72 image_page_urls = [] 73 image_a_lists = soup.find_all("a") 74 for image_a in image_a_lists: 75 # 获取a标签中的相对url 76 relative_url = image_a["href"] 77 # 根据url特征,只需要图片跳转页url和翻页url 78 if relative_url.startswith("/tupian") and relative_url.endswith(".html") or "/index_" in relative_url: 79 # 拼接绝对路径 80 image_or_index_abs_url = home_page + relative_url 81 image_page_urls.append(image_or_index_abs_url) 82 return image_page_urls 83 except: 84 print("获取图片下载页url和翻页url失败【%s】" % url) 85 traceback.print_exc() 86 87 88 # 任务函数 89 def task(queue): 90 global result_urls 91 global image_dir 92 global image_no 93 while not queue.empty(): 94 url = queue.get() 95 try: 96 # 如果该页为主图页,则下载图片 97 image_download_url = get_image_url(url) 98 if image_download_url and image_download_url not in result_urls: 99 image_no += 1 100 # 下载图片到本地 101 download_image(image_download_url, image_dir, image_no) 102 result_urls.append(image_download_url) 103 except: 104 traceback.print_exc() 105 try: 106 # 获取图片下载页url和翻页url,加入队列中 107 image_page_urls = get_page_url(url) 108 while image_page_urls: 109 image_page_url = image_page_urls.pop() 110 if image_page_url not in result_urls: 111 queue.put(image_page_url) 112 result_urls.append(image_page_url) 113 except: 114 traceback.print_exc() 115 116 117 if __name__ == "__main__": 118 # 将种子页面放入队列中 119 image_resource_url = "http://pic.netbian.com" 120 queue.put(image_resource_url) 121 # 开启100个线程,执行任务函数 122 t_list = [] 123 for i in range(100): 124 t = threading.Thread(target=task, args=(queue,)) 125 t_list.append(t) 126 t.start() 127 # 等待所有子线程结束后,主线程才结束 128 for t in t_list: 129 t.join()
执行结果