绝对干货 excel中一对多查找问题解决思路汇总 附公式模型
731
2022-05-29
作为一个素材收集狂,遇到一个好的图片站,那是必须要盘它的。这次就碰到一个 PNG images 号称有 100000+ 免费的 PNG 图片。
免抠图片站点分析
目标站点:http://pngimg.com/
目标数据:全站 PNG 图片,一键下载。
该网站具备非常多的分类页,在采集过程中,优先采集该分类页数据。
分类页地址通过开发者工具查看,得到如下内容
该地址需要与域名信息进行拼接,得到详情页地址,例如 anaconda 地址如下:
http://pngimg.com/images/animals/anaconda
打开详情页,检查图片所在区域,目的获取图片地址,测试过程中发现图片列表页并未分页,即一页即可获取全部数据。
基于上述内容,整理逻辑如下:
获取所有图片分类标签;
基于分类进入图片列表页;
提取图片列表页图片地址;
下载图片
编码时间
由于本案例涉及文件的读写操作,所以采用多线程实现,代码分为 3 个部分,采集分类,采集图片地址,下载图片。
采集分类代码如下
import random import logging import threading from typing import Optional, Text import requests from bs4 import BeautifulSoup import lxml logging.basicConfig(level=logging.NOTSET) thread_lock = threading.Lock() def get_headers(): uas = [ "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)", "Mozilla/5.0 (compatible; Baiduspider-render/2.0; +http://www.baidu.com/search/spider.html)", ] ua = random.choice(uas) headers = { "user-agent": ua } return headers # 通用的 requests get 请求方法 def get_html(url: Text, headers: dict, timeout: int) -> Optional[Text]: try: res = requests.get(url=url, headers=headers, timeout=timeout) except Exception as e: logging.error(e) if res is not None: return res.text else: return None if __name__ == '__main__': url = "http://pngimg.com/" headers = get_headers() # 获取首页的 HTML 数据 html_str = get_html(url, headers, 5) # 解析首页的HTML数据,获取所有列表页链接 soup = BeautifulSoup(html_str, 'lxml') div_parents = soup.find_all(attrs={'class': 'sub_category'}) # 获取到所有的详情页地址 all_links = [] for div in div_parents: all_links.extend(div.find_all('a')) print("累计获取到", len(all_links), "个列表页数据")
上述代码中将 get_headers() 函数单独提炼,后续将函数直接作为参数,传递到对应类的构造方法中即可。
提取到所有的分类标签存储到 all_links 变量中,后续由于多线程需要共享全局变量,顾提前导入线程锁:
thread_lock = threading.Lock()
PNG 采集与下载类
class PngImg(threading.Thread): # 构造函数 def __init__(self, thread_name, headers_func, requests_func) -> None: threading.Thread.__init__(self) self._headers = headers_func() self._timeout = 5 self.requests_func = requests_func self._thread_name = thread_name def run(self) -> None: bast_host = "http://pngimg.com" while True: thread_lock.acquire() # 全局锁,获取地址 global all_links if all_links is None: break list_url = bast_host + all_links.pop().get('href') thread_lock.release() print(self._thread_name + " 正在运行,采集的地址是 " + list_url) list_html_str = self.requests_func(url=list_url, headers=self._headers, timeout=self._timeout) ret_imgs = self._get_imgs(list_html_str) self._save(ret_imgs) def _get_imgs(self, html) -> list: """获取所有的图片地址 :return: 图片 list """ soup = BeautifulSoup(html, 'lxml') # 获取图片所在 div 标签 div_imgs = soup.find_all(attrs={'class': 'png_imgs'}) # 图片地址为空,用来保存图片 tag imgs_src = [] for div_img in div_imgs: # 遍历 div 标签,检索后代标签中的 img 图片标签 imgs_src.append(div_img.a.img.get("src")) return imgs_src def _save(self, imgs): """保存图片 """ for img in imgs: img = img.replace('small/', '') # 去除 small 标记,获取大图 img_url = "https://pngimg.com{}".format(img) # 拼接完整图片访问地址 name = img[img.rfind('/') + 1:] # print(img_url) # print(name) try: res = requests.get(url=img_url, headers=self._headers, timeout=self._timeout) except Exception as e: logging.error(e) if res is not None: name = name.replace("/", "_") with open(f'./imgs/{self._thread_name}_{name}', "wb+") as f: f.write(res.content)
代码说明:
上述类为核心采集类,重写了 run 方法,在其中通过循环实现对所有列表页的采集,_get_imgs() 方法用于获取所有图片地址,用到的依旧是 BeautifulSoup 对象的 find_all() 方法,标签检索使用子集标签寻找方式 div_img.a.img.get("src"),_save() 方法用于存储图片,在这里橡皮擦第一次测试的时候,发现图片路径中带有 small 关键字,如希望获取大图,需要对其进行删除,后续测试发现已经移除该标记。
类的构造方法参数说明如下:
def __init__(self, thread_name, headers_func, requests_func) -> None:
thread_name:线程名;
headers_func:get_headers() 函数;
requests_func:请求函数;
线程的开启代码直接写在 main 代码块中即可,本案例使用 5 个线程。
threads = ["Thread A", "Thread B", "Thread C", "Thread D", "Thread E"] for t in threads: my_thread = PngImg(t, get_headers, get_html) my_thread.start()
代码运行过程效果图如下所示:
获取图片也是非常快速的。
写在后面
BeautifulSoup 模块的学习暂时告一段落,希望这3篇文章,能让你对 bs4 模块有一个初步的认识。
Python 任务调度
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。