1. 项目概述:Python影视搜索接口爬虫实战
最近在开发一个影视资源搜索工具时,发现市面上公开的API要么收费昂贵,要么限制调用频率。于是决定用Python自己写一个爬虫方案,直接从各大影视网站抓取数据。这个方案不仅成本低,还能灵活定制搜索逻辑,特别适合需要批量获取影视信息的中小项目。
这个爬虫核心用了requests发请求,BeautifulSoup解析HTML,再配合正则表达式提取关键信息。整个过程涉及反爬应对、数据清洗和结果聚合,最终输出结构化的影视信息JSON。下面我就把完整实现过程和踩过的坑都分享出来,代码可以直接复用。
2. 技术方案设计
2.1 目标网站分析
首选了三个数据源:
- 豆瓣电影(评分权威但反爬严)
- 某视频门户(资源丰富但结构复杂)
- 某字幕站(更新及时但需要登录)
每个源都需要单独处理:
- 豆瓣用API+移动端UA绕过检测
- 视频门户需要处理动态加载内容
- 字幕站要维护cookie池
2.2 技术栈选型
python复制# 基础库
import requests
from bs4 import BeautifulSoup
import re
import json
# 进阶工具
from fake_useragent import UserAgent # 随机UA
from pyquery import PyQuery # 备用解析器
import execjs # 处理加密参数
选型理由:
- requests比urllib3更人性化
- BeautifulSoup容错性好于PyQuery
- 正则表达式处理不规则数据
- 随机UA库应对基础反爬
3. 核心实现细节
3.1 请求模块封装
python复制def get_html(url, retry=3):
headers = {
'User-Agent': UserAgent().random,
'Accept-Encoding': 'gzip' # 避免被ban
}
try:
resp = requests.get(url,
headers=headers,
timeout=10,
verify=False) # 忽略SSL验证
resp.encoding = resp.apparent_encoding
if resp.status_code == 200:
return resp.text
except Exception as e:
if retry > 0:
return get_html(url, retry-1)
print(f"请求失败: {str(e)}")
return None
关键点:
- 随机UA每次不同
- 超时设置避免卡死
- 自动重试机制
- 编码自动检测
3.2 解析模块实现
以豆瓣电影为例:
python复制def parse_douban(html):
soup = BeautifulSoup(html, 'lxml')
items = soup.select('.item') # 电影条目
results = []
for item in items:
try:
title = item.select_one('.title').text.strip()
rating = item.select_one('.rating_num').text
detail_link = item.select_one('a')['href']
# 正则提取年份
year = re.search(r'\((\d{4})\)', title).group(1)
results.append({
'title': title.replace(f'({year})', '').strip(),
'year': year,
'rating': float(rating),
'url': detail_link
})
except Exception as e:
print(f"解析异常: {str(e)}")
continue
return results
特别注意:
- 使用CSS选择器比XPath更稳定
- 重要字段要有异常处理
- 文本清洗需要多次strip()
4. 反爬应对策略
4.1 常见反爬手段
- IP限制:每5分钟更换代理IP
- UA检测:每次请求随机切换UA
- 验证码:使用打码平台接入
- 参数加密:逆向JS生成签名
4.2 代理IP池实现
python复制PROXY_POOL = [
'http://1.1.1.1:8888',
'http://2.2.2.2:9999'
]
def get_with_proxy(url):
for proxy in PROXY_POOL:
try:
resp = requests.get(url,
proxies={'http': proxy},
timeout=5)
if resp.status_code == 200:
return resp.text
except:
continue
return None
代理使用技巧:
- 优先使用隧道代理
- 每次失败后自动切换
- 配合延迟策略使用
5. 数据存储方案
5.1 结构化存储
python复制import sqlite3
def save_to_db(data):
conn = sqlite3.connect('movies.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS movies
(title TEXT, year INT, rating REAL, url TEXT)''')
for item in data:
c.execute("INSERT INTO movies VALUES (?,?,?,?)",
(item['title'], item['year'],
item['rating'], item['url']))
conn.commit()
conn.close()
5.2 文件存储方案
python复制import csv
def save_to_csv(data, filename):
with open(filename, 'a', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f,
fieldnames=['title','year','rating','url'])
if f.tell() == 0: # 如果是新文件写表头
writer.writeheader()
writer.writerows(data)
6. 完整调用示例
python复制if __name__ == '__main__':
# 1. 获取网页
url = 'https://movie.douban.com/top250'
html = get_html(url)
# 2. 解析数据
movies = parse_douban(html)
# 3. 存储结果
save_to_db(movies)
save_to_csv(movies, 'top250.csv')
# 4. 输出统计
print(f"共获取{len(movies)}部电影")
print("评分最高:", max(movies, key=lambda x:x['rating']))
7. 常见问题排查
7.1 返回空数据
可能原因:
- 触发反爬返回验证页
- 网页结构已更新
- 编码解析错误
解决方案:
- 检查实际返回的HTML内容
- 更新CSS选择器路径
- 强制指定编码格式
7.2 请求被阻断
典型表现:
- 返回403状态码
- 出现验证码页面
- 返回假数据
应对措施:
- 降低请求频率(加随机延迟)
- 更换更高匿名的代理
- 模拟浏览器行为(添加Cookie)
8. 性能优化建议
- 多线程采集:使用concurrent.futures
- 异步IO:aiohttp+asyncio组合
- 增量采集:记录最后采集位置
- 断点续传:保存任务状态
python复制# 多线程示例
from concurrent.futures import ThreadPoolExecutor
def batch_crawl(urls):
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(get_html, urls))
return results
特别提醒:线程数不要超过10个,否则容易被封
9. 法律合规提示
- 遵守robots.txt协议
- 设置合理采集间隔(建议≥3秒)
- 不采集敏感个人信息
- 商用前咨询法律意见
我在实际项目中会添加自动遵守robots.txt的逻辑:
python复制import urllib.robotparser
rp = urllib.robotparser.RobotFileParser()
rp.set_url("https://www.example.com/robots.txt")
rp.read()
can_fetch = rp.can_fetch("*", "/search")
10. 项目扩展方向
- 增加自动分类功能(NLP处理简介)
- 对接通知系统(邮件/微信推送)
- 开发可视化看板
- 构建影视知识图谱
这个爬虫框架其实可以复用到其他领域,比如:
- 商品价格监控
- 新闻聚合
- 招聘信息采集
关键是要根据目标网站特点调整解析逻辑。我后来用类似架构做了个电商比价工具,核心代码复用率超过70%。
