1. 项目概述:Python第五次作业实战解析

这次作业的核心是综合运用Python三大核心技能:正则表达式处理文本、装饰器优化代码结构、爬虫技术获取数据。作为带过上百名初学者的Python老鸟,我发现这三个知识点正是新手从语法学习转向实际开发的关键转折点。下面就以电商数据爬取为例,带你打通这三个技能的任督二脉。

2. 正则表达式实战:数据清洗的瑞士军刀

2.1 京东手机价格提取实战

处理京东手机页面的HTML源码时,价格信息往往混杂在复杂的标签中。用这个正则模式可以精准提取:

import re
price_pattern = r'<em>¥</em><i>(\d+\.\d{2})</i>'
html = '<em>¥</em><i>3999.00</i>'
match = re.search(price_pattern, html)
if match:
    print(f"提取价格:{match.group(1)}")

关键技巧:正则测试推荐使用regex101.com在线调试,比反复运行脚本效率高10倍

2.2 金额验证的进阶技巧

验证变量金额是否大于0时,这个正则比单纯判断数字更可靠:

def validate_amount(amount_str):
    pattern = r'^[1-9]\d*(\.\d{1,2})?$|^0\.\d[1-9]$'
    return bool(re.fullmatch(pattern, amount_str))

3. 装饰器深度应用:给爬虫装上仪表盘

3.1 超时重试装饰器

网络请求不稳定时,这个装饰器能自动重试3次:

import time
from functools import wraps

def retry(max_tries=3, delay=1):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(max_tries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if i == max_tries - 1:
                        raise
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(max_tries=5, delay=2)
def fetch_url(url):
    # 爬取逻辑

3.2 性能统计装饰器

监控每个爬虫函数的执行耗时:

def timing(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__}耗时:{end-start:.2f}秒")
        return result
    return wrapper

4. 爬虫工程化实战:以同花顺为例

4.1 DrissionPage方案

比Selenium更轻量的选择:

from drissionpage import ChromiumPage

page = ChromiumPage()
page.get('https://www.10jqka.com.cn')
search_input = page.ele('#searchInput')
search_input.input('贵州茅台')
search_btn = page.ele('#searchBtn')
search_btn.click()

4.2 反爬应对策略

  • 随机User-Agent生成器:
from fake_useragent import UserAgent
headers = {'User-Agent': UserAgent().random}
  • IP代理池实现方案:
import random
proxy_pool = [
    'http://proxy1.example.com:8080',
    'http://proxy2.example.com:8080'
]

def get_proxy():
    return {'http': random.choice(proxy_pool)}

5. 环境配置避坑指南

5.1 Python 3.8+新特性利用

使用海象运算符简化爬虫代码:

if (match := re.search(pattern, html)):
    process_data(match.group(1))

5.2 VSCode高效配置

launch.json关键配置:

{
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "args": ["--proxy", "http://127.0.0.1:8080"],
            "console": "integratedTerminal"
        }
    ]
}

6. 常见问题排雷手册

  1. 正则匹配中文乱码:

    text = response.content.decode('utf-8')
    
  2. 装饰器导致函数签名丢失:

    • 务必使用 @wraps(func) 保留元数据
  3. 动态页面元素加载超时:

    page.wait.ele_loaded('#elementId', timeout=10)
    
  4. SSL证书验证失败:

    import ssl
    ssl._create_default_https_context = ssl._create_unverified_context
    

7. 性能优化实战技巧

  1. 正则表达式预编译:

    price_re = re.compile(r'¥(\d+\.\d{2})')
    
  2. 爬虫并行处理:

    from concurrent.futures import ThreadPoolExecutor
    
    with ThreadPoolExecutor(max_workers=5) as executor:
        executor.map(fetch_page, url_list)
    
  3. 装饰器链式调用:

    @retry(3)
    @timing
    def critical_task():
        pass
    

8. 数据存储方案选型

8.1 轻量级方案:SQLite

import sqlite3
conn = sqlite3.connect('mobile.db')
cursor = conn.cursor()
cursor.execute('''CREATE TABLE IF NOT EXISTS products
             (name TEXT, price REAL, date TEXT)''')

8.2 大数据量方案:MongoDB

from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
db = client['spider_data']
collection = db['jd_phones']
collection.insert_one({'model': 'iPhone15', 'price': 5999})

9. 项目安全注意事项

  1. 请求频率控制:

    import time
    time.sleep(random.uniform(0.5, 1.5))
    
  2. 敏感数据过滤:

    def sanitize_data(text):
        return re.sub(r'\b\d{4}[-\s]?\d{4}[-\s]?\d{4}\b', '[CARD]', text)
    
  3. 法律合规要点:

    • 严格遵守robots.txt规则
    • 单日请求量控制在1000次以内
    • 不爬取用户个人信息

10. 调试与日志记录

10.1 结构化日志配置

import logging
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO,
    handlers=[
        logging.FileHandler('spider.log'),
        logging.StreamHandler()
    ]
)

10.2 异常捕获最佳实践

try:
    response = requests.get(url, timeout=10)
    response.raise_for_status()
except requests.exceptions.RequestException as e:
    logging.error(f"请求失败 {url}: {str(e)}")
    raise SpiderException(f"请求异常: {e}") from e

11. 扩展学习路径

  1. 正则表达式进阶:

    • 学习(?:)非捕获组用法
    • 掌握(?P )命名捕获组
  2. 装饰器高级用法:

    • 类装饰器实现
    • 带参数的装饰器工厂
  3. 爬虫框架拓展:

    • Scrapy框架学习
    • Playwright自动化测试

12. 实战项目建议

  1. 证券信息爬虫增强版:

    • 添加K线图识别功能
    • 实现邮件预警系统
  2. 微博签到数据分析:

    • 用Pandas分析活跃时段
    • 生成可视化热力图
  3. 电商价格监控系统:

    • 搭建Flask监控面板
    • 实现价格异动报警

13. 开发环境维护

  1. 依赖管理:

    python -m pip freeze > requirements.txt
    
  2. 虚拟环境:

    python -m venv .venv
    source .venv/bin/activate  # Linux/Mac
    .\.venv\Scripts\activate   # Windows
    
  3. 代码质量检查:

    pylint spider.py
    black --check .
    

14. 部署方案对比

14.1 本地打包方案

pyinstaller --onefile spider.py

14.2 Docker容器化

Dockerfile示例:

FROM python:3.8-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "spider.py"]

15. 性能基准测试

使用timeit测试正则效率:

import timeit
setup = '''
import re
text = "价格:¥3999.00"
pattern = re.compile(r'¥(\d+\.\d{2})')
'''
stmt = 'pattern.search(text)'
print(timeit.timeit(stmt, setup, number=10000))

16. 代码优化实例

优化前:

def process_text(text):
    if re.search(r'\d+', text):
        return True
    return False

优化后:

has_digit = re.compile(r'\d').search
def process_text(text):
    return bool(has_digit(text))

17. 单元测试规范

测试装饰器功能:

import unittest

class TestRetryDecorator(unittest.TestCase):
    def test_retry_success(self):
        @retry(max_tries=3)
        def mock_func():
            return 42
            
        self.assertEqual(mock_func(), 42)

18. 文档字符串规范

装饰器文档示例:

def deprecated(message):
    """标记函数为已废弃
    
    Args:
        message (str): 替代方案说明
        
    Returns:
        装饰器函数
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            warnings.warn(f"{func.__name__}已废弃: {message}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

19. 类型提示实践

带类型提示的爬虫函数:

from typing import Optional, Dict, Union

def fetch_product(
    url: str, 
    headers: Optional[Dict[str, str]] = None
) -> Union[Dict[str, Union[str, float]], None]:
    """获取商品数据
    
    Args:
        url: 商品页URL
        headers: 可选请求头
        
    Returns:
        商品数据字典或None
    """

20. 异步IO优化

使用aiohttp提高并发:

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        return await asyncio.gather(*tasks)
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐