位置: 文档库 > Python > 使用 Python 编写多线程爬虫抓取百度贴吧邮箱与手机号

使用 Python 编写多线程爬虫抓取百度贴吧邮箱与手机号

DenDragon 上传于 2022-09-15 21:17

【使用Python编写多线程爬虫抓取百度贴吧邮箱与手机号】

在互联网数据采集领域,爬虫技术是获取公开信息的重要手段。本文将详细介绍如何使用Python编写一个高效的多线程爬虫,用于抓取百度贴吧帖子中的邮箱地址和手机号码。该方案通过模拟浏览器行为、解析HTML内容、结合正则表达式与多线程技术,实现高效稳定的数据采集。

一、技术选型与准备工作

1.1 核心库选择

Python生态中存在多个成熟的爬虫库,本方案选择以下组合:

  • requests:轻量级HTTP库,用于发送网络请求
  • BeautifulSoup4:HTML解析库,支持CSS选择器
  • re:Python内置正则表达式模块
  • threading:标准库多线程模块
  • queue:线程安全的任务队列

1.2 环境配置

pip install requests beautifulsoup4

建议使用虚拟环境管理依赖:

python -m venv baidu_spider
source baidu_spider/bin/activate  # Linux/Mac
.\baidu_spider\Scripts\activate  # Windows

二、单线程爬虫实现

2.1 基础请求模块

import requests
from bs4 import BeautifulSoup

def fetch_page(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        return response.text
    except requests.exceptions.RequestException as e:
        print(f"请求失败: {e}")
        return None

2.2 数据解析模块

import re

def extract_contacts(html):
    # 邮箱正则(支持常见邮箱格式)
    email_pattern = r'([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})'
    # 手机号正则(支持11位大陆手机号)
    phone_pattern = r'(1[3-9]\d{9})'
    
    soup = BeautifulSoup(html, 'html.parser')
    text_content = soup.get_text()
    
    emails = re.findall(email_pattern, text_content)
    phones = re.findall(phone_pattern, text_content)
    
    return {
        'emails': list(set(emails)),  # 去重
        'phones': list(set(phones))
    }

三、多线程架构设计

3.1 线程模型选择

采用生产者-消费者模式:

  • 主线程作为生产者,负责生成URL任务
  • 工作线程作为消费者,处理页面抓取和解析
  • 使用Queue实现线程间通信

3.2 完整实现代码

import threading
import queue
import time

class BaiduTiebaSpider:
    def __init__(self, base_url, max_threads=5):
        self.base_url = base_url
        self.task_queue = queue.Queue()
        self.result_queue = queue.Queue()
        self.max_threads = max_threads
        self.lock = threading.Lock()
        self.total_pages = 0
        self.collected_data = []
    
    def generate_urls(self, start_page, end_page):
        """生成分页URL"""
        for page in range(start_page, end_page + 1):
            url = f"{self.base_url}?pn={(page-1)*50}"
            self.task_queue.put(url)
            with self.lock:
                self.total_pages += 1
    
    def worker(self):
        """工作线程函数"""
        while True:
            url = self.task_queue.get()
            if url is None:  # 终止信号
                break
                
            html = fetch_page(url)
            if html:
                data = extract_contacts(html)
                self.result_queue.put(data)
            self.task_queue.task_done()
    
    def start(self, start_page=1, end_page=10):
        """启动爬虫"""
        # 启动工作线程
        threads = []
        for _ in range(self.max_threads):
            t = threading.Thread(target=self.worker)
            t.start()
            threads.append(t)
        
        # 生成任务
        self.generate_urls(start_page, end_page)
        
        # 等待所有任务完成
        self.task_queue.join()
        
        # 发送终止信号
        for _ in range(self.max_threads):
            self.task_queue.put(None)
        for t in threads:
            t.join()
        
        # 收集结果
        while not self.result_queue.empty():
            self.collected_data.append(self.result_queue.get())
    
    def get_results(self):
        """获取合并后的结果"""
        all_emails = []
        all_phones = []
        
        for data in self.collected_data:
            all_emails.extend(data['emails'])
            all_phones.extend(data['phones'])
        
        return {
            'unique_emails': list(set(all_emails)),
            'unique_phones': list(set(all_phones)),
            'total_pages': self.total_pages
        }

四、性能优化策略

4.1 请求间隔控制

import random
import time

def safe_request(url):
    """添加随机延迟的请求函数"""
    delay = random.uniform(0.5, 2.0)  # 0.5-2秒随机延迟
    time.sleep(delay)
    return fetch_page(url)

4.2 代理IP池集成

class ProxyManager:
    def __init__(self, proxies=None):
        self.proxies = proxies or []
        self.current_proxy_index = 0
    
    def get_proxy(self):
        if not self.proxies:
            return None
        proxy = self.proxies[self.current_proxy_index]
        self.current_proxy_index = (self.current_proxy_index + 1) % len(self.proxies)
        return {'http': proxy, 'https': proxy}
    
    def update_proxies(self, new_proxies):
        self.proxies = new_proxies

4.3 异常处理增强

def robust_fetch(url, max_retries=3):
    for attempt in range(max_retries):
        try:
            proxy = proxy_manager.get_proxy()
            response = requests.get(
                url,
                headers=headers,
                timeout=10,
                proxies=proxy if proxy else None
            )
            response.raise_for_status()
            return response.text
        except Exception as e:
            if attempt == max_retries - 1:
                print(f"最终请求失败: {url}, 错误: {e}")
                return None
            time.sleep(2 ** attempt)  # 指数退避

五、反爬虫应对方案

5.1 User-Agent轮换

USER_AGENTS = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...',
    # 更多UA字符串...
]

def get_random_ua():
    import random
    return random.choice(USER_AGENTS)

5.2 Cookie管理

class CookieManager:
    def __init__(self):
        self.session = requests.Session()
    
    def update_cookies(self, cookies_dict):
        self.session.cookies.update(cookies_dict)
    
    def get_cookie_header(self):
        return {'Cookie': '; '.join([f"{k}={v}" for k, v in self.session.cookies.items()])}

六、完整示例与运行

6.1 主程序入口

if __name__ == "__main__":
    # 配置参数
    BASE_URL = "https://tieba.baidu.com/f?kw=python"
    START_PAGE = 1
    END_PAGE = 5
    THREADS = 8
    
    # 初始化爬虫
    spider = BaiduTiebaSpider(BASE_URL, max_threads=THREADS)
    
    # 启动爬取
    print("开始爬取百度贴吧数据...")
    start_time = time.time()
    spider.start(START_PAGE, END_PAGE)
    
    # 获取结果
    results = spider.get_results()
    elapsed = time.time() - start_time
    
    # 输出统计信息
    print(f"\n爬取完成,耗时: {elapsed:.2f}秒")
    print(f"共处理页面: {results['total_pages']}个")
    print(f"获取邮箱数量: {len(results['unique_emails'])}个")
    print(f"获取手机号数量: {len(results['unique_phones'])}个")
    
    # 保存结果(示例)
    with open('emails.txt', 'w') as f:
        f.write('\n'.join(results['unique_emails']))
    with open('phones.txt', 'w') as f:
        f.write('\n'.join(results['unique_phones']))

七、法律与伦理注意事项

7.1 合法性声明

本代码仅用于学习Python爬虫技术,实际使用时需遵守:

  • 《中华人民共和国网络安全法》
  • 《中华人民共和国个人信息保护法》
  • 目标网站的robots.txt协议

7.2 推荐实践

  • 限制爬取频率(建议不超过1请求/秒)
  • 仅处理公开可访问的数据
  • 避免存储敏感个人信息
  • 获得网站授权后再进行大规模爬取

八、扩展功能建议

8.1 数据存储升级

import sqlite3

def save_to_db(data):
    conn = sqlite3.connect('contacts.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS contacts
                 (type TEXT, value TEXT UNIQUE)''')
    
    for email in data['unique_emails']:
        c.execute("INSERT OR IGNORE INTO contacts VALUES (?, ?)", ('email', email))
    for phone in data['unique_phones']:
        c.execute("INSERT OR IGNORE INTO contacts VALUES (?, ?)", ('phone', phone))
    
    conn.commit()
    conn.close()

8.2 分布式爬虫架构

对于大规模数据采集,可考虑:

  • 使用Scrapy框架的分布式模式
  • 结合Redis实现任务分发
  • 部署到Docker容器集群

【关键词】Python爬虫、多线程、百度贴吧、邮箱抓取手机号提取、正则表达式、BeautifulSouprequests库反爬虫策略、数据采集

【简介】本文详细介绍了使用Python开发多线程爬虫的技术方案,重点讲解了如何高效抓取百度贴吧中的邮箱地址和手机号码。内容涵盖基础爬虫实现、多线程架构设计、性能优化策略、反爬虫应对措施以及法律伦理注意事项,提供了完整的代码示例和扩展建议,适合Python开发者学习网络数据采集技术。

《使用 Python 编写多线程爬虫抓取百度贴吧邮箱与手机号 .doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档