Python多线程案例分析

接下来,我们将在之前的基础上进一步扩展多线程爬虫案例,增加以下功能:

  1. 动态URL发现与添加:爬虫在解析页面时,能够发现并添加新的URL到队列中。

  2. 设置请求头:模拟浏览器行为,设置请求头中的`User-Agent`。

  3. 使用会话:使用`requests.Session()`对象来保持连接,提高效率。

  4. 避免重复爬取:使用集合记录已爬取的URL,避免重复爬取。

  5. 更复杂的错误处理:增加对各种网络错误的处理。

扩展后的多线程爬虫代码:```python

python 复制代码
import requests
from bs4 import BeautifulSoup
import threading
import time
import logging
from queue import Queue
from urllib.parse import urljoin

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 请求会话
session = requests.Session()

# 请求队列
url_queue = Queue()
# 避免重复爬取的集合
visited_urls = set()

def crawl(url):
    if url in visited_urls:
        return
    visited_urls.add(url)
    try:
        response = session.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=5)
        response.raise_for_status()  # 检查请求是否成功
        soup = BeautifulSoup(response.text, 'html.parser')
        # 假设我们爬取的是网页的标题和一些链接
        title = soup.find('title').get_text()
        links = [urljoin(url, a['href']) for a in soup.find_all('a', href=True) if a['href'] not in visited_urls]
        result_queue.put((url, title))
        # 将新发现的链接添加到队列中
        for link in links:
            url_queue.put(link)
        logging.info(f"成功爬取: {url}")
    except requests.RequestException as e:
        logging.error(f"请求错误: {url} - {e}")
    except Exception as e:
        logging.error(f"解析错误: {url} - {e}")

def worker():
    while not url_queue.empty():
        url = url_queue.get()
        crawl(url)
        # 模拟网络延迟
        time.sleep(0.5)

def manage_threads(url_list, thread_count=5):
    # 将初始URL加入队列
    for url in url_list:
        if url not in visited_urls:
            url_queue.put(url)

    # 创建并启动线程
    threads = []
    for _ in range(thread_count):
        thread = threading.Thread(target=worker)
        threads.append(thread)
        thread.start()

    # 等待所有线程完成
    for thread in threads:
        thread.join()

    # 打印结果
    while not result_queue.empty():
        url, title = result_queue.get()
        print(f"URL: {url}, Title: {title}")

# 爬取的URL列表
urls = [
    'http://example.com',
    # 添加更多URL...
]

if __name__ == "__main__":
    manage_threads(urls, thread_count=10)

```

案例详解:

  • 动态URL发现与添加:在`crawl`函数中,解析页面后发现的链接会动态添加到`url_queue`中,以便进行爬取。

  • 设置请求头:在`session.get`中添加了`headers`参数,模拟浏览器的`User-Agent`。

  • 使用会话:使用`requests.Session()`对象发起请求,可以保持连接,提高效率。

  • 避免重复爬取:通过`visited_urls`集合记录已爬取的URL,避免重复爬取同一页面。

  • 更复杂的错误处理:在`crawl`函数中,增加了对网络错误的处理。

注意事项:

  • 线程安全:确保对共享资源(如`url_queue`和`visited_urls`)的操作是线程安全的。

  • 会话管理:使用`session`对象时,要注意其线程安全性。在某些情况下,可能需要为每个线程创建独立的会话。

  • 动态URL管理:动态添加URL时,要注意避免无限循环,特别是当爬虫遇到重定向或循环链接时。

  • 性能调优:根据实际情况调整线程数量和请求间隔,以达到最佳的爬取效率。

这个案例提供了一个更完整的多线程爬虫框架,包括动态URL发现、会话管理等高级功能。在实际应用中,你可能需要根据目标网站的特点和反爬措施进行相应的调整和优化。

在前面的基础上,我们将继续扩展我们的多线程爬虫案例,增加以下高级功能:

  1. robots.txt 遵守:检查网站的`robots.txt`文件,确保爬虫遵守网站的爬取规则。

  2. 重试机制:对失败的请求实施重试策略。

  3. IP代理:使用代理IP来绕过网站的IP封锁。

  4. 数据存储:将爬取的数据存储到文件或数据库中。

  5. 更细粒度的速率限制:使用更高级的速率限制策略,如令牌桶算法。

扩展后的多线程爬虫代码:```python

python 复制代码
import requests
from bs4 import BeautifulSoup
import threading
import time
import logging
from queue import Queue
from urllib.parse import urljoin, urlparse
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 请求会话
session = requests.Session()

# 重试策略
retry_strategy = Retry(
    total=3,  # 总共重试3次
    status_forcelist=[429, 500, 502, 503, 504],  # 重试状态码
    method_whitelist=["HEAD", "GET", "OPTIONS"],  # 允许重试的方法
    backoff_factor=1  # 退避因子
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)

# 请求队列
url_queue = Queue()
# 避免重复爬取的集合
visited_urls = set()
# 存储爬取结果的列表
crawl_results = []

def crawl(url):
    if url in visited_urls:
        return
    visited_urls.add(url)
    try:
        response = session.get(url, headers={'User-Agent': 'Mozilla/5.0'}, timeout=5)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')
        title = soup.find('title').get_text()
        links = [urljoin(url, a['href']) for a in soup.find_all('a', href=True)]
        crawl_results.append((url, title))
        for link in links:
            if link not in visited_urls:
                url_queue.put(link)
        logging.info(f"成功爬取: {url}")
    except requests.RequestException as e:
        logging.error(f"请求错误: {url} - {e}")
    except Exception as e:
        logging.error(f"解析错误: {url} - {e}")

def worker():
    while not url_queue.empty():
        url = url_queue.get()
        crawl(url)
        # 细粒度速率限制
        time.sleep(0.5)

def manage_threads(url_list, thread_count=5):
    for url in url_list:
        if url not in visited_urls:
            url_queue.put(url)

    threads = []
    for _ in range(thread_count):
        thread = threading.Thread(target=worker)
        threads.append(thread)
        thread.start()

    for thread in threads:
        thread.join()

    # 打印结果
    for result in crawl_results:
        print(result)

# 爬取的URL列表
urls = [
    'http://example.com',
    # 添加更多URL...
]

if __name__ == "__main__":
    manage_threads(urls, thread_count=10)

    # 将结果存储到文件
    with open('crawl_results.txt', 'w', encoding='utf-8') as file:
        for url, title in crawl_results:
            file.write(f"URL: {url}, Title: {title}\n")

```

案例详解:

  • robots.txt 遵守:可以通过`robotparser`模块来解析网站的`robots.txt`文件,并检查是否允许爬取特定的URL。

  • 重试机制:使用`requests`的`Retry`和`HTTPAdapter`来实现请求的重试策略。

  • IP代理:可以在`session.get`中设置代理,例如使用`proxies`参数。

  • 数据存储:将爬取的结果存储到文件中,也可以根据需求存储到数据库或其他存储系统中。

  • 细粒度速率限制:在`worker`函数中,通过`time.sleep(0.5)`实现简单的速率限制。

注意事项:

  • robots.txt 遵守:在实际应用中,应当遵守`robots.txt`协议,尊重网站的爬取规则。

  • 重试策略:合理配置重试策略,避免对服务器造成过大压力。

  • 代理使用:使用代理时,应确保代理的稳定性和可靠性。

  • 数据存储:根据实际需求选择合适的数据存储方案。

  • 速率限制:合理设置请求间隔,避免触发网站的反爬机制。

这个案例提供了一个更为完善的多线程爬虫框架,包括遵守`robots.txt`、重试策略、使用代理、数据存储和细粒度速率限制等功能。在实际应用中,你可能需要根据目标网站的特点和反爬措施进行相应的调整和优化。

相关推荐
转调9 分钟前
每日一练:地下城游戏
开发语言·c++·算法·leetcode
Java探秘者10 分钟前
Maven下载、安装与环境配置详解:从零开始搭建高效Java开发环境
java·开发语言·数据库·spring boot·spring cloud·maven·idea
攸攸太上10 分钟前
Spring Gateway学习
java·后端·学习·spring·微服务·gateway
2301_7869643616 分钟前
3、练习常用的HBase Shell命令+HBase 常用的Java API 及应用实例
java·大数据·数据库·分布式·hbase
2303_8120444619 分钟前
Bean,看到P188没看了与maven
java·开发语言
苹果醋319 分钟前
大模型实战--FastChat一行代码实现部署和各个组件详解
java·运维·spring boot·mysql·nginx
秋夫人21 分钟前
idea 同一个项目不同模块如何设置不同的jdk版本
java·开发语言·intellij-idea
m0_6640470226 分钟前
数字化采购管理革新:全过程数字化采购管理平台的架构与实施
java·招投标系统源码
潘多编程29 分钟前
Spring Boot与GraphQL:现代化API设计
spring boot·后端·graphql
不穿格子衬衫37 分钟前
常用排序算法(下)
c语言·开发语言·数据结构·算法·排序算法·八大排序