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`、重试策略、使用代理、数据存储和细粒度速率限制等功能。在实际应用中,你可能需要根据目标网站的特点和反爬措施进行相应的调整和优化。

相关推荐
aworkholic7 分钟前
opencv sdk for java中提示无stiching模块接口的问题
java·c++·opencv·jni·opencv4android·stiching
字节程序员11 分钟前
四种自动化测试模型实例及优缺点详解
开发语言·javascript·ecmascript·集成测试·压力测试
程序员老冯头12 分钟前
第十六章 C++ 字符串
开发语言·c++
爱学习的白杨树12 分钟前
什么是MVCC?
java·服务器·数据库
終不似少年遊*13 分钟前
美国加州房价数据分析02
人工智能·python·机器学习·数据挖掘·数据分析·回归算法
hnmpf19 分钟前
flask-admin modelview 中重写get_query函数
后端·python·flask
平行线也会相交26 分钟前
云图库平台(三)——后端用户模块开发
数据库·spring boot·mysql·云图库平台
灵槐梦28 分钟前
【速成51单片机】2.点亮LED
c语言·开发语言·经验分享·笔记·单片机·51单片机
想睡觉 . 我也想睡觉 .29 分钟前
【C++算法】1.【模板】前缀和
开发语言·c++·算法
天天要nx29 分钟前
D105【python 接口自动化学习】- pytest进阶参数化用法
python·pytest