Python多线程爬虫——数据分析项目实现详解

前言

在这里插入图片描述
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:https://www.captainbed.cn/z

「个人网站」:雪碧的个人网站
请添加图片描述

ChatGPT体验地址

请添加图片描述

文章目录

  • 前言
  • 爬虫
  • 获取cookie
    • 网站爬取与启动
      • CSDN爬虫
      • 爬虫启动
      • 将爬取内容存到文件中
  • 多线程爬虫
    • 选择要爬取的用户
  • 线程池

爬虫

爬虫是指一种自动化程序,能够模拟人类用户在互联网上浏览网页、抓取网页内容、提取数据等操作。爬虫通常用于搜索引擎、数据挖掘、网络分析、竞争情报、用户行为分析等领域。
在这里插入图片描述
我们以爬取某个用户的博文列表并存储到文件中实现多线程爬虫为例,带大家体验爬虫的魅力

获取cookie

首先我们在爬取网站的时候首先获取cookie
在这里插入图片描述

拿我的博客主页为例,用F12打开控制台,点击网络,找到cookie
在这里插入图片描述
创建一个cookie文件,复制进去
然后从给定的cookie_path文件中读取cookie信息,并将其存储在一个字典中。函数返回这个字典。
具体如下

def get_headers(cookie_path:str):
cookies = {}
with open(cookie_path, "r", encoding="utf-8") as f:
cookie_list = f.readlines()
for line in cookie_list:
cookie = line.split(":")
cookies[cookie[0]] = str(cookie[1]).strip()
return cookies

网站爬取与启动

CSDN爬虫

class CSDN(object):
def init(self, username, folder_name, cookie_path):
# self.headers = {
# "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36"
# }
self.headers = get_headers(cookie_path)
self.s = requests.Session()
self.username = username
self.TaskQueue = TaskQueue()
self.folder_name = folder_name
self.url_num = 1
  1. headers: 这是一个字典,用于存储请求头信息。
  2. s: 这是一个会话对象,用于保持与CSDN网站的连接。
  3. username: 这是一个字符串,表示CSDN用户的用户名。
  4. TaskQueue: 这是一个任务队列对象,用于管理待访问的URL。
  5. folder_name: 这是一个字符串,表示保存爬取结果的文件夹名称。
  6. _name: 这是一个整数,表示当前保存的文件夹编号。
  7. _num: 这是一个整数,表示当前爬取的页面编号。

爬虫启动

def start(self):num = 0articles = [None]while len(articles) > 0:num += 1url = u'https://blog.csdn.net/' + self.username + '/article/list/' + str(num)response = self.s.get(url=url, headers=self.headers)html = response.textsoup = BeautifulSoup(html, "html.parser")articles = soup.find_all('div', attrs={"class":"article-item-box csdn-tracking-statistics"})for article in articles:article_title = article.a.text.strip().replace('        ',':')article_href = article.a['href']with ensure_memory(sys.getsizeof(self.TaskQueue.UnVisitedList)):self.TaskQueue.InsertUnVisitedList([article_title, article_href])
  1. 初始化一个变量num,用于表示当前访问的文章页码。
  2. 初始化一个列表articles,用于存储待处理的文章信息。
  3. 使用一个while循环,当articles列表中的文章数量大于0时,执行循环体。
  4. 更新num变量,表示当前访问的文章页码。
  5. 构造一个URL,该URL包含当前用户名、文章列表和页码。
  6. 使用requests库发送请求,并获取响应。
  7. 使用BeautifulSoup库解析HTML内容,并提取相关的文章信息。
  8. 遍历提取到的文章列表,提取文章标题和链接。
  9. 将文章标题和链接插入到任务队列TaskQueue的未访问列表中。

将爬取内容存到文件中

  1. 打印爬取开始的信息。
  2. 计算并获取存储博文列表的文件路径。
  3. 使用open函数以写入模式打开文件,并设置文件编码为utf-8
  4. 写入文件头,包括用户名和博文列表。
  5. 遍历任务队列TaskQueue中的未访问列表,将每篇文章的标题和链接写入文件。
  6. 在每篇文章标题和链接之间添加一个空行,以提高可读性。
  7. 更新一个变量_num,用于表示当前已写入的文章序号。

代码如下

def write_readme(self):print("+"*100)print("[++] 开始爬取 {} 的博文 ......".format(self.username))print("+"*100)reademe_path = result_file(self.username,file_name="README.md",folder_name=self.folder_name)with open(reademe_path,'w', encoding='utf-8') as reademe_file:readme_head = "# " + self.username + " 的博文\n"reademe_file.write(readme_head)for [article_title,article_href] in self.TaskQueue.UnVisitedList[::-1]:text = str(self.url_num) + '. [' + article_title + ']('+ article_href +')\n'reademe_file.write(text)self.url_num += 1self.url_num = 1

列表文件生成之后,我们要对每一个链接进行处理

def get_all_articles(self):try:while True:[article_title,article_href] = self.TaskQueue.PopUnVisitedList()try:file_name = re.sub(r'[\/::*?"<>|]','-', article_title) + ".md"artical_path = result_file(folder_username=self.username, file_name=file_name, folder_name=self.folder_name)md_head = "# " + article_title + "\n"md = md_head + self.get_md(article_href)print("[++++] 正在处理URL:{}".format(article_href))with open(artical_path, "w", encoding="utf-8") as artical_file:artical_file.write(md)except Exception:print("[----] 处理URL异常:{}".format(article_href))self.url_num += 1except Exception:pass
  1. 从任务队列TaskQueue中弹出未访问的文章链接和标题。
  2. 尝试获取一个文件名,该文件名由文章标题生成,以避免文件名中的特殊字符。
  3. 计算并获取存储文章的文件路径。
  4. 创建一个Markdown文件头,包括文章标题。
  5. 获取文章内容,并将其添加到Markdown文件头。
  6. 将处理后的Markdown内容写入文件。
  7. 打印正在处理的URL。
  8. 更新一个变量_num,用于表示已处理的文章数量。

多线程爬虫

实现多线程爬虫,以提高爬取速度。在循环中,会不断地创建新的线程来处理任务队列中的任务,直到任务队列为空。这样可以充分利用计算机的多核性能,提高爬取效率。

def muti_spider(self, thread_num):while self.TaskQueue.getUnVisitedListLength() > 0:thread_list = []for i in range(thread_num):th = threading.Thread(target=self.get_all_articles)thread_list.append(th)for th in thread_list:th.start()

我们在多线程爬虫的时候,要保证系统有足够的内存空间。通过使用contextlib库的contextmanager装饰器,可以轻松地实现上下文管理,确保内存分配和释放的正确性。

lock = threading.Lock()
total_mem= 1024 * 1024 * 500 #500MB spare memory
@contextlib.contextmanager
def ensure_memory(size):global total_memwhile 1:with lock:if total_mem > size:total_mem-= sizebreaktime.sleep(5)yield with lock:total_mem += size

__enter__方法中,使用with lock语句模拟加锁,确保在执行内存分配操作时,不会发生竞争条件。然后判断当前系统的总内存是否大于所需分配的内存空间,如果大于,则减少总内存,并跳出循环。

选择要爬取的用户

def spider_user(username: str, cookie_path:str, thread_num: int = 10, folder_name: str = "articles"):if not os.path.exists(folder_name):os.makedirs(folder_name)csdn = CSDN(username, folder_name, cookie_path)csdn.start()th1 = threading.Thread(target=csdn.write_readme)th1.start()th2 = threading.Thread(target=csdn.muti_spider, args=(thread_num,))th2.start()
  1. 检查文件夹folder_name是否存在,如果不存在,则创建该文件夹。
  2. 创建一个CSDN对象csdn,用于模拟用户登录和爬取文章。
  3. 创建一个线程th1,目标为_readme
  4. 创建一个线程th2,目标为_spider,并传入参数(thread_num,),用于指定线程数量。

这个函数的目的是爬取指定用户的CSDN博客文章,并将文章保存到文件夹folder_name中。通过创建线程,可以实现多线程爬虫,提高爬取速度。

线程池

线程池存储爬虫代理 IP 的数据库或集合。在网络爬虫中,由于目标网站可能会针对同一 IP 地址的访问频率进行限制,因此需要使用池来存储多个代理 IP 地址,以实现 IP 地址的轮换和代理。池可以提高爬虫的稳定性和效率,避免因为 IP 地址被封禁而导致的爬虫失效。
爬虫和池是爬虫领域中不可或缺的概念,池能够提高爬虫的稳定性和效率,同时帮助爬虫更好地适应目标的反爬虫策略。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/243276.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring boot项目java bean和xml互转

Spring boot项目实现java bean和xml互转 项目场景&#xff1a;互转方法使用jackson进行互转使用jaxws进行xml与bean的互转 搞定收工&#xff01; 项目场景&#xff1a; 工作中需要给下游第三方收费系统做数据挡板&#xff0c;由于下游系统使用的是soap webservice,里面涉及各种…

牛客周赛 Round 21 解题报告 | 珂学家 | 堆栈的妙用

前言 整体评价 从A题中的Baidu, 可以猜到这场有几道题来自于百度校招。 其实B题有点意思&#xff0c;如果把十字星的范围放大&#xff0c;那就可以成为一个hard题。 D题也挺意思的&#xff0c;大概有两种思路&#xff0c;一种是从左到右枚举右端点&#xff0c;增量累加&…

【C/C++】C/C++编程——为什么学习 C++?

当提到C的时候&#xff0c;很多人会觉得语法复杂、学习曲线陡峭&#xff0c;并且好像与C语言还有点"纠缠不清"。尽管如此&#xff0c;C仍然是当今世界上最受欢迎和最有影响力的编程语言之一。特别是在当今快速发展的人工智能&#xff08;AI&#xff09;领域&#xff…

利用GPU加速自定义风格图像生成-利用GPU加速结合了ControlNet/ Lora的Stable Diffusion XL

点击链接完成注册&#xff0c;参加本次在线研讨会 https://www.nvidia.cn/webinars/sessions/?session_id240124-31319 随着AI技术的发展, 数字内容创建业务也变得越来越火热。生成式AI模型的发布, 让我们看到了人工智能在各行各业的潜力。您只需要用语言简单描述自己希望看…

黑马苍穹外卖学习Day10

文章目录 Spring Task介绍cron表达式入门案例 订单状态定时处理需求分析代码开发功能测试 WebSocket介绍入门案例 来单提醒需求分析代码开发 客户催单需求分析代码开发 Spring Task 介绍 cron表达式 入门案例 订单状态定时处理 需求分析 代码开发 新建一个task包里面编写代码…

REVIT二次开发批量编号

步骤1 步骤2 步骤3 实现代码using System; using System.Collections.Generic; using System.Linq; using Syste

《Python数据分析技术栈》第03章 03 可视化各级数据(Visualizing various levels of data)

03 可视化各级数据&#xff08;Visualizing various levels of data&#xff09; 《Python数据分析技术栈》第03章 03 可视化各级数据&#xff08;Visualizing various levels of data&#xff09; Whenever you need to analyze data, first understand if the data is stru…

C++三剑客之std::variant(二):深入剖析

目录 1.概述 2.辅助类介绍 2.1.std::negation 2.2.std::conjunction 2.3.std::is_destructible 2.4.std::is_object 2.5.is_default_constructible 2.6.std::is_trivially_destructible 2.7.std::in_place_type和std::in_place_index 3.原理分析 3.1.存储分析 3.2.…

【蓝桥杯EDA设计与开发】资料汇总以及立创EDA及PCB相关技术资料汇总(持续更新)

[18/01/2024]&#xff1a;目前为了准备蓝桥杯做一些资料贴&#xff0c;于是写下这一篇博客。 各种资料均来源于网络以及部分书籍、手册等文档&#xff0c;参考不保证其准确性。 如果在准备蓝桥杯&#xff0c;可与我私信共同学习&#xff01;&#xff01;&#xff01;&#xf…

SCTP, TCP, UDP, IP, ICMP都在哪一层?(TCP/IP网络通信协议学习)

TCP/IP网络通信协议最早是由罗伯特卡恩&#xff08;Robert E. Kahn&#xff09;和文顿瑟夫&#xff08;Vinton G. Cerf&#xff09;于1972年提出的&#xff0c;它是一个实际的协议栈。 OSI七层网络通信协议最早是由国际标准化组织&#xff08;ISO&#xff09;于1977年提出的&am…

0基础转行做软件测试?一文教小白拿到初级岗位offer?

我认为入门软件测试需要四个方面的知识or技能&#xff0c;它们是&#xff1a;业务知识、职业素养、基础知识、技术知识。 职业素养是一切的根基&#xff0c;因为人在职场就必须拥有必要的职业素养&#xff0c;软件测试工程师也不例外。基础知识和技术知识是两大支柱&#xff0…

Kubernetes网络模型概述

Kubernetes网络模型设计的一个基础原则是&#xff1a;每个Pod都拥有一个独立的IP地址&#xff0c;并假定所有Pod都在一个可以直接连通的、扁平的网络空间中。所以不管这些Pod是否运行在同一个Node中&#xff0c;都要求它们可以直接通过对方的IP进行访问。由于Kubernetes的网络模…

分布式锁的产生以及使用

日常开发中&#xff0c;针对一些需要锁定资源的操作&#xff0c;例如商城的订单超卖问题、订单重复提交问题等。 都是为了解决在资源有限的情况限制客户端的访问&#xff0c;对应的是限流。 单节点锁问题 目前针对这种锁资源的情况采取的往往是互斥锁&#xff0c;例如 java 里…

顶顶通用户申请和安装 空号识别 模块流程

一、申请 空号识别 授权 打开网址&#xff1a;http://my.ddrj.com&#xff0c;注册并登录。 点击“我的授权” -> “申请授权” &#xff08;根据负责人的要求选择“在线”或是“离线”&#xff09;。 找到名称为空号识别的授权并点击“加号”图标打开授权&#xff0c;然…

Uni-App三甲医院、医保定点三甲医院在线预约挂号系统源码

医院在线预约挂号系统是一种方便患者预约挂号的系统&#xff0c;患者可以通过该系统进行预约挂号&#xff0c;省去了到医院现场排队等待的时间&#xff0c;提高了就诊效率。随着医院信息化水平的不断发展&#xff0c;医院在线预约挂号管理系统已成为医院管理中不可或缺的一部分…

SQL-窗口函数

什么是窗口函数 可以像聚合函数一样对一组数据进行分析并返回结果&#xff0c;二者的不同之处在于&#xff0c;窗口函数不是将一组数据汇总成单个结果&#xff0c;而是为每一行数据都返回一个结果。 窗口函数组成部分 1.创建数据分区 窗口函数OVER子句中的PARTITION BY选项用…

大师学SwiftUI第6章 - 声明式用户界面 Part 3

安全域视图 SwiftUI还内置了创建安全文本框的视图。这一视图会把用户输入的字符替换成点以及隐藏敏感信息&#xff0c;比如密码。 SecureField(String, text: Binding)&#xff1a;该初始化方法创建一个安全输入框。第一个参数定义占位文本&#xff0c;​​text​​参数为存储…

leetcode 013二维区域和检索---矩阵不可变

给定一个二维矩阵 matrix&#xff0c;以下类型的多个请求&#xff1a; 计算其子矩形范围内元素的总和&#xff0c;该子矩阵的左上角为 (row1, col1) &#xff0c;右下角为 (row2, col2) 。 实现 NumMatrix 类&#xff1a; NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进…

Quartus II使用小技巧

工程结构&#xff1a; 在建立完某项设计的文件后&#xff0c;依次在其里面新建四个文件夹&#xff0c;分别为&#xff1a;rtl、qprj、msim、doc。 rtl文件夹用于存放设计的源文件。 doc文件夹用于存放设计的一些文档性的资料。 qprj文件夹用于存放quaruts 工程以及quartus生…

Git入门详细教程

一、Git概述&#x1f387; Git官网 Git是一个开源的分布式版本控制系统&#xff0c;用于跟踪文件的变化和协作开发。它允许多个开发者在同一项目中共同工作&#xff0c;并能够有效地管理代码的版本和历史记录。Git可以帮助开发团队更好地协作&#xff0c;追踪代码变更&#xf…