新手必看:用Python的requests和BeautifulSoup写一个能绕过反爬的百度搜索爬虫(附完整代码)
·
Python爬虫入门:安全合规的百度搜索数据采集实践
第一次接触Python爬虫时,最让人困惑的不是技术实现,而是如何在法律允许的范围内获取数据。记得三年前我刚学习爬虫时,花了两周时间研究各种反爬措施,却忽略了最重要的合规性问题,直到收到某平台的警告邮件才恍然大悟。本文将从一个负责任的技术学习者角度,分享如何用requests和BeautifulSoup构建一个符合伦理规范的百度搜索采集工具。
百度搜索作为爬虫学习对象有几个独特优势:首先,它的HTML结构相对稳定,对初学者友好;其次,作为国内主流搜索引擎,其公开结果页面在法律允许范围内可供个人学习使用;最重要的是,适度的反爬机制能让我们学习到基本的规避技巧,又不会因过于复杂而打击学习积极性。
1. 环境准备与基础认知
1.1 必要的Python库安装
开始前需要确保已安装以下两个核心库:
pip install requests beautifulsoup4
- requests:用于发送HTTP请求和获取网页内容
- beautifulsoup4:用于解析HTML文档并提取所需数据
提示:建议使用Python 3.6+版本,这是大多数现代库支持的最低版本
1.2 爬虫伦理与法律边界
在编写第一行代码前,必须明确几个基本原则:
- robots.txt协议:百度允许爬取搜索结果页,但明确禁止商业性大规模采集
- 访问频率控制:单IP请求间隔不应低于2秒,日请求量控制在1000次以内
- 数据使用限制:采集结果仅用于个人学习,禁止转售或用于商业分析
- User-Agent规范:必须声明真实身份,不可伪装成搜索引擎蜘蛛
下表对比了合规与违规操作的关键区别:
| 行为特征 | 合规操作 | 违规风险 |
|---|---|---|
| 请求频率 | 2-5秒/次 | 高频并发请求 |
| 数据规模 | 少量关键词测试 | 全量数据抓取 |
| 身份标识 | 真实浏览器UA | 伪装搜索引擎bot |
| 数据用途 | 技术学习研究 | 商业盈利行为 |
2. 核心代码实现与反爬策略
2.1 基础请求框架构建
我们先创建一个基本的搜索函数骨架:
import requests
from bs4 import BeautifulSoup
import random
import time
def baidu_search(keyword):
"""百度搜索采集函数"""
# 构建搜索URL
url = f"https://www.baidu.com/s?wd={keyword}"
# 请求头设置
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
}
# 发送请求
response = requests.get(url, headers=headers)
# 解析响应
soup = BeautifulSoup(response.text, 'html.parser')
# 数据提取逻辑...
这个基础版本存在明显问题:固定User-Agent和缺乏延时控制,极易触发反爬机制。
2.2 反爬措施增强方案
2.2.1 User-Agent轮换策略
维护一个常见浏览器UA列表,每次请求随机选择:
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1'
]
headers = {
'User-Agent': random.choice(user_agents),
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': 'https://www.baidu.com/'
}
2.2.2 请求间隔与异常处理
添加随机延时和重试机制:
def baidu_search(keyword, max_retry=3):
for attempt in range(max_retry):
try:
time.sleep(random.uniform(1, 3)) # 1-3秒随机延时
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # 检查HTTP状态码
if "验证" in response.text: # 简单验证码检测
raise Exception("可能触发了验证码")
return response.text
except Exception as e:
if attempt == max_retry - 1:
raise
time.sleep(5) # 重试前延长等待
3. 数据解析与结果处理
3.1 HTML结构分析与定位
百度搜索结果页的主要结构如下:
<div class="result">
<h3 class="t">
<a href="真实链接">标题文本</a>
</h3>
<div class="c-abstract">摘要文本</div>
</div>
对应的解析代码:
results = []
for item in soup.find_all('div', class_='result'):
try:
title = item.find('h3').get_text(strip=True)
link = item.find('a')['href']
abstract = item.find('div', class_='c-abstract')
abstract = abstract.get_text(strip=True) if abstract else ''
results.append({
'title': title,
'link': link,
'abstract': abstract
})
except AttributeError:
continue
3.2 链接真实地址提取
百度搜索结果中的链接是跳转地址,需要提取真实URL:
import re
def get_real_url(redirect_url):
"""解析百度跳转链接"""
try:
resp = requests.head(redirect_url, allow_redirects=False, timeout=3)
return resp.headers.get('Location', redirect_url)
except:
return redirect_url
4. 完整实现与优化建议
4.1 最终整合代码
import requests
from bs4 import BeautifulSoup
import random
import time
from urllib.parse import quote
class BaiduSearcher:
def __init__(self):
self.user_agents = [...]
self.last_request_time = 0
def make_request(self, url):
"""带控制的请求方法"""
elapsed = time.time() - self.last_request_time
if elapsed < 1: # 确保至少1秒间隔
time.sleep(1 - elapsed)
headers = {
'User-Agent': random.choice(self.user_agents),
'Accept-Language': 'zh-CN,zh;q=0.9'
}
try:
response = requests.get(url, headers=headers, timeout=8)
self.last_request_time = time.time()
return response
except requests.exceptions.RequestException:
return None
def search(self, keyword, page=1):
"""执行搜索"""
encoded_keyword = quote(keyword)
url = f"https://www.baidu.com/s?wd={encoded_keyword}&pn={(page-1)*10}"
response = self.make_request(url)
if not response or response.status_code != 200:
return []
soup = BeautifulSoup(response.text, 'html.parser')
return self.parse_results(soup)
def parse_results(self, soup):
"""解析结果页"""
items = []
for result in soup.find_all('div', class_='result'):
try:
title_elem = result.find('h3')
if not title_elem:
continue
title = title_elem.get_text(strip=True)
link = self.get_real_url(title_elem.find('a')['href'])
abstract_elem = result.find('div', class_='c-abstract')
abstract = abstract_elem.get_text(strip=True) if abstract_elem else ''
items.append({
'title': title,
'link': link,
'abstract': abstract
})
except Exception:
continue
return items
def get_real_url(self, redirect_url):
"""获取跳转后的真实URL"""
try:
resp = requests.head(
redirect_url,
allow_redirects=False,
timeout=3,
headers={'User-Agent': random.choice(self.user_agents)}
)
return resp.headers.get('Location', redirect_url)
except:
return redirect_url
# 使用示例
searcher = BaiduSearcher()
results = searcher.search("Python爬虫入门", page=1)
for i, result in enumerate(results[:5], 1):
print(f"{i}. {result['title']}\n {result['link']}\n")
4.2 进一步优化方向
- IP代理池:当需要扩大采集规模时,应考虑使用优质代理服务
- 验证码识别:集成第三方验证码识别服务应对复杂情况
- 结果去重:使用Bloom Filter等算法高效去重
- 分布式架构:Scrapy-Redis框架适合大规模分布式采集
注意:所有优化必须建立在合规前提下,商业用途需获得平台官方授权
在实际项目中,我发现最容易被忽视的是Referer头的设置。合理模拟用户从百度首页发起搜索的行为(即设置Referer为baidu.com),能显著降低被屏蔽的概率。另一个实用技巧是在请求之间加入随机鼠标移动和滚动行为模拟,虽然我们的代码无法直接实现,但可以通过随机延时来近似这种人类操作节奏。
更多推荐


所有评论(0)