Python网络爬虫从零基础到精通教程

1. 环境准备与PyCharm 2021配置

首先,您需要在计算机上安装Python和PyCharm 2021。以下是详细步骤:

1.1 安装Python

访问Python官网(https://www.python.org/downloads/)下载最新稳定版(推荐Python 3.8+),安装时务必勾选"Add Python to PATH"选项。

1.2 安装PyCharm 2021

  1. 访问JetBrains官网下载PyCharm 2021 Community版(免费)
  2. 运行安装程序,按默认设置完成安装
  3. 首次启动时,建议进行以下配置:
    • 选择深色主题(如Darcula)保护眼睛
    • 安装中文语言包(可选)
    • 设置合适的字体大小(推荐14-16px)

1.3 创建第一个Python项目

# 在PyCharm中创建新项目步骤:
# 1. File → New Project
# 2. 选择项目位置和解释器(自动检测已安装的Python)
# 3. 右键项目 → New → Python File
# 4. 输入文件名,如:first_crawler.py

2. 网络爬虫基础概念

网络爬虫(Web Crawler)是一种自动化程序,用于从互联网上抓取数据。其主要工作流程如下:

组件 功能描述 对应工具/库
URL管理器 管理待抓取和已抓取的URL队列 列表/集合
网页下载器 下载网页内容 requests库
网页解析器 提取所需数据 BeautifulSoup
数据存储器 保存抓取结果 文件/数据库

3. 核心库安装与使用

在PyCharm的Terminal中执行以下命令安装必要库:

# 安装requests库用于网页下载
pip install requests

# 安装BeautifulSoup4用于网页解析
pip install beautifulsoup4

# 安装lxml解析器(性能更好)
pip install lxml

4. 第一个简单爬虫实例

4.1 基础网页下载示例

import requests

# 设置请求头,模拟浏览器访问
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}

def download_page(url):
    """下载网页内容"""
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.encoding = 'utf-8'  # 设置编码
        if response.status_code == 200:
            return response.text
        else:
            print(f"请求失败,状态码:{response.status_code}")
            return None
    except Exception as e:
        print(f"下载出错:{e}")
        return None

# 测试下载功能
if __name__ == "__main__":
    url = "http://httpbin.org/get"
    html = download_page(url)
    if html:
        print("网页下载成功!")
        print(html[:500])  # 打印前500个字符

4.2 使用BeautifulSoup解析网页

from bs4 import BeautifulSoup
import requests

def parse_html_demo():
    """解析HTML示例"""
    html_doc = """
    <html>
        <head><title>测试页面</title></head>
        <body>
            <h1>主标题</h1>
            <p class="content">第一段内容</p>
            <p class="content">第二段内容</p>
            <a href="https://example.com">链接</a>
            <ul>
                <li>项目1</li>
                <li>项目2</li>
            </ul>
        </body>
    </html>
    """
    
    # 创建BeautifulSoup对象
    soup = BeautifulSoup(html_doc, 'lxml')
    
    # 提取标题
    title = soup.title.string
    print(f"页面标题:{title}")
    
    # 提取所有段落
    paragraphs = soup.find_all('p', class_='content')
    for i, p in enumerate(paragraphs, 1):
        print(f"段落{i}:{p.text}")
    
    # 提取链接
    link = soup.find('a')
    print(f"链接地址:{link['href']}")
    
    # 提取列表项
    items = soup.select('ul li')
    for item in items:
        print(f"列表项:{item.text}")

if __name__ == "__main__":
    parse_html_demo()

5. 完整爬虫项目架构

以下是一个完整的爬虫项目结构,包含五大核心组件:

# 1. URL管理器
class UrlManager:
    def __init__(self):
        self.new_urls = set()  # 待爬取的URL集合
        self.old_urls = set()  # 已爬取的URL集合
    
    def add_new_url(self, url):
        if url not in self.new_urls and url not in self.old_urls:
            self.new_urls.add(url)
    
    def add_new_urls(self, urls):
        for url in urls:
            self.add_new_url(url)
    
    def has_new_url(self):
        return len(self.new_urls) > 0
    
    def get_new_url(self):
        new_url = self.new_urls.pop()
        self.old_urls.add(new_url)
        return new_url

# 2. 网页下载器
class HtmlDownloader:
    def download(self, url):
        headers = {'User-Agent': 'Mozilla/5.0'}
        try:
            response = requests.get(url, headers=headers, timeout=10)
            if response.status_code == 200:
                return response.text
            return None
        except:
            return None

# 3. 网页解析器
class HtmlParser:
    def parse(self, page_url, html_cont):
        if page_url is None or html_cont is None:
            return
        
        soup = BeautifulSoup(html_cont, 'lxml')
        new_urls = self._get_new_urls(page_url, soup)
        new_data = self._get_new_data(page_url, soup)
        return new_urls, new_data
    
    def _get_new_urls(self, page_url, soup):
        # 提取页面中的所有链接
        new_urls = set()
        links = soup.find_all('a', href=True)
        for link in links:
            new_url = link['href']
            # 处理相对路径
            if new_url.startswith('/'):
                from urllib.parse import urljoin
                new_url = urljoin(page_url, new_url)
            new_urls.add(new_url)
        return new_urls
    
    def _get_new_data(self, page_url, soup):
        # 提取页面中的数据
        data = {}
        data['url'] = page_url
        data['title'] = soup.title.string if soup.title else ''
        data['content'] = soup.get_text()[:200]  # 取前200个字符
        return data

# 4. 数据输出器
class DataOutput:
    def __init__(self):
        self.datas = []
    
    def store_data(self, data):
        if data:
            self.datas.append(data)
    
    def output_html(self):
        with open('output.html', 'w', encoding='utf-8') as f:
            f.write('<html><head><meta charset="utf-8"></head><body>')
            f.write('<table border="1">')
            f.write('<tr><th>URL</th><th>标题</th><th>内容摘要</th></tr>')
            for data in self.datas:
                f.write('<tr>')
                f.write(f'<td>{data["url"]}</td>')
                f.write(f'<td>{data["title"]}</td>')
                f.write(f'<td>{data["content"]}</td>')
                f.write('</tr>')
            f.write('</table></body></html>')
        print(f"数据已保存到output.html,共{len(self.datas)}条记录")

# 5. 爬虫调度器(主控制器)
class SpiderMan:
    def __init__(self):
        self.manager = UrlManager()
        self.downloader = HtmlDownloader()
        self.parser = HtmlParser()
        self.output = DataOutput()
    
    def crawl(self, root_url):
        # 添加起始URL
        self.manager.add_new_url(root_url)
        
        # 控制爬取数量
        count = 0
        max_count = 10
        
        while self.manager.has_new_url() and count < max_count:
            try:
                # 获取新的URL
                new_url = self.manager.get_new_url()
                print(f"正在爬取第{count+1}个URL:{new_url}")
                
                # 下载网页
                html = self.downloader.download(new_url)
                
                # 解析网页
                new_urls, new_data = self.parser.parse(new_url, html)
                
                # 将新的URL添加到URL管理器
                self.manager.add_new_urls(new_urls)
                
                # 存储数据
                self.output.store_data(new_data)
                
                count += 1
                print(f"成功抓取第{count}个页面")
                
            except Exception as e:
                print(f"爬取失败:{e}")
        
        # 输出结果
        self.output.output_html()

# 主程序
if __name__ == "__main__":
    # 使用示例网站(请遵守robots.txt)
    root_url = "http://httpbin.org/html"
    spider = SpiderMan()
    spider.crawl(root_url)

6. 进阶技巧与最佳实践

6.1 处理动态加载内容(使用Selenium)

# 安装:pip install selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def crawl_dynamic_page():
    """爬取动态加载的页面"""
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')  # 无头模式
    driver = webdriver.Chrome(options=options)
    
    try:
        driver.get("https://example.com")
        # 等待元素加载
        element = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "content"))
        )
        # 获取页面源码
        html = driver.page_source
        soup = BeautifulSoup(html, 'lxml')
        # 解析数据...
    finally:
        driver.quit()

6.2 添加延迟和错误处理

import time
import random
from requests.exceptions import RequestException

def safe_download(url, retries=3):
    """带重试机制的下载函数"""
    for attempt in range(retries):
        try:
            time.sleep(random.uniform(1, 3))  # 随机延迟1-3秒
            response = requests.get(url, timeout=15)
            response.raise_for_status()
            return response.text
        except RequestException as e:
            print(f"第{attempt+1}次尝试失败:{e}")
            if attempt == retries - 1:
                return None
            time.sleep(2 ** attempt)  # 指数退避

6.3 遵守robots.txt

import urllib.robotparser

def check_robots_txt(url):
    """检查robots.txt协议"""
    rp = urllib.robotparser.RobotFileParser()
    base_url = '/'.join(url.split('/')[:3])  # 获取域名
    rp.set_url(base_url + "/robots.txt")
    rp.read()
    
    if rp.can_fetch("*", url):
        print(f"允许爬取:{url}")
        return True
    else:
        print(f"禁止爬取:{url}")
        return False

7. 实战项目:爬取新闻网站

以下是一个完整的新闻爬虫示例:

import requests
from bs4 import BeautifulSoup
import csv
import time

class NewsCrawler:
    def __init__(self):
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        }
    
    def fetch_news(self, url):
        """抓取新闻页面"""
        try:
            response = requests.get(url, headers=self.headers)
            soup = BeautifulSoup(response.text, 'lxml')
            
            # 假设新闻页面结构
            news_data = {
                'title': soup.find('h1').text.strip() if soup.find('h1') else '',
                'author': soup.find('span', class_='author').text if soup.find('span', class_='author') else '未知',
                'publish_time': soup.find('time')['datetime'] if soup.find('time') else '',
                'content': ' '.join([p.text for p in soup.find_all('p', class_='content')])
            }
            return news_data
        except Exception as e:
            print(f"抓取失败:{e}")
            return None
    
    def save_to_csv(self, data_list, filename='news.csv'):
        """保存到CSV文件"""
        if not data_list:
            return
        
        keys = data_list[0].keys()
        with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
            writer = csv.DictWriter(f, fieldnames=keys)
            writer.writeheader()
            writer.writerows(data_list)
        print(f"数据已保存到{filename}")

# 使用示例
if __name__ == "__main__":
    crawler = NewsCrawler()
    
    # 示例URL列表(实际使用时替换为真实URL)
    news_urls = [
        "http://example.com/news/1",
        "http://example.com/news/2"
    ]
    
    all_news = []
    for url in news_urls:
        print(f"正在处理:{url}")
        news = crawler.fetch_news(url)
        if news:
            all_news.append(news)
        time.sleep(2)  # 礼貌性延迟
    
    crawler.save_to_csv(all_news)

8. 学习路径建议

阶段 学习内容 预计时间 目标
第一阶段 Python基础语法、PyCharm使用 1-2周 能编写简单Python程序
第二阶段 requests、BeautifulSoup基础 1周 能爬取静态网页数据
第三阶段 爬虫架构设计、数据存储 2周 能构建完整爬虫项目
第四阶段 反爬虫对策、动态页面 2周 能处理复杂网站
第五阶段 Scrapy框架、分布式爬虫 3周 能开发企业级爬虫

9. 重要注意事项

  1. 法律与道德:始终遵守网站的robots.txt协议,尊重版权和个人隐私
  2. 频率控制:避免对同一网站过快请求,添加合理的延迟
  3. 错误处理:完善的异常处理机制确保程序稳定性
  4. 数据清洗:对爬取的数据进行清洗和验证
  5. 更新维护:网站结构变化时及时调整解析逻辑

10. 下一步学习建议

掌握基础爬虫后,建议深入学习:

  • Scrapy框架:专业的爬虫框架
  • 数据库存储:MySQL、MongoDB
  • 异步爬虫:aiohttp、asyncio
  • 验证码识别:PIL、OpenCV
  • 分布式爬虫:Redis、Scrapy-Redis

通过以上系统学习,您将能够从零基础逐步掌握Python网络爬虫开发,最终达到精通水平。建议每个阶段都动手实践,通过实际项目巩固所学知识。


参考来源

 

Logo

更多推荐