Python爬虫入门:基于关键词的百度网页信息抓取实战
1. Python爬虫入门:为什么选择百度作为练习对象
刚开始接触Python爬虫时,很多人会纠结该从哪个网站开始练习。我建议从百度入手,原因很简单:它的反爬机制相对友好,页面结构稳定,特别适合新手练手。记得我第一次写爬虫时,尝试抓取某电商网站数据,结果不到10分钟就被封了IP,那种挫败感至今难忘。
百度搜索结果的HTML结构清晰,关键信息都包裹在class为"result"的div中。这种规律性强的页面,能让我们把精力集中在爬虫核心逻辑上,而不是花大量时间处理各种反爬陷阱。不过要注意,虽然百度相对宽容,我们仍需遵守基本的爬虫礼仪——这也是为什么代码中要设置随机User-Agent和访问间隔。
import requests
from bs4 import BeautifulSoup
import random
import time
# 基础请求函数示例
def simple_request():
url = "https://www.baidu.com"
response = requests.get(url)
print(response.status_code) # 200表示请求成功
这个最简单的请求示例展示了爬虫的核心三要素:目标URL、请求发送和响应处理。当你能看到输出200这个数字时,说明已经成功迈出了第一步。接下来我们会在这个基础上,逐步添加更多实用功能。
2. 环境准备与基础工具链配置
2.1 必备库的安装与验证
工欲善其事,必先利其器。在开始写爬虫代码前,需要确保环境配置正确。我推荐使用Python 3.6+版本,太老的版本可能会遇到兼容性问题。安装依赖库时,新手常犯的错误是直接使用系统Python,这可能导致权限问题。我的经验是先用virtualenv创建隔离环境:
python -m venv spider_env
source spider_env/bin/activate # Linux/Mac
spider_env\Scripts\activate # Windows
pip install requests beautifulsoup4
安装完成后,建议写个简单的测试脚本验证库是否可用。有次我帮同事调试爬虫,折腾两小时才发现他装的bs4版本太旧,导致CSS选择器语法不支持。这个小测试能避免很多无谓的折腾:
def test_libraries():
print(requests.__version__) # 应该≥2.22.0
print(BeautifulSoup.__version__) # 应该≥4.9.0
2.2 开发工具的选择技巧
虽然可以用记事本写Python,但好的IDE能事半功倍。VS Code和PyCharm都不错,我个人更推荐VS Code,因为它轻量且插件丰富。安装Python扩展后,记得开启自动补全和linting功能。调试爬虫时,这几个功能特别有用:
- 请求拦截查看(使用F12开发者工具)
- XPath/CSS选择器测试窗
- 变量实时监控
刚开始可以先用print调试,等熟悉后建议学习使用debugger。有次我遇到个诡异的问题,明明浏览器能看到元素,但爬虫就是抓不到。后来用debugger逐步跟踪,才发现是动态加载的内容,这个经验让我意识到工具的重要性。
3. 核心爬取逻辑实现详解
3.1 关键词搜索URL的构造技巧
百度搜索的URL构造很有规律,基础格式是https://www.baidu.com/s?wd=关键词。但实际使用时要注意几个细节:
- 中文关键词需要URL编码
- 可以添加pn参数控制分页(每页10条,pn=0第一页,pn=10第二页)
- 特殊符号需要正确处理
from urllib.parse import quote
def build_search_url(keyword, page=1):
base_url = "https://www.baidu.com/s?wd="
encoded_keyword = quote(keyword)
page_param = f"&pn={(page-1)*10}" if page > 1 else ""
return f"{base_url}{encoded_keyword}{page_param}"
这个改进版的URL构造器可以处理中文和分页。我曾经踩过坑,直接拼接中文关键词导致请求失败,后来才明白URL编码的重要性。分页参数也是个实用技巧,当需要抓取更多结果时非常有用。
3.2 反爬策略的实战应用
即使百度相对友好,基本的反爬措施还是必要的。我们的武器库包括:
- User-Agent轮换:准备多个主流浏览器的UA
- 请求间隔:随机延迟0.5-3秒
- 请求头完善:添加Referer等常见头信息
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15...',
# 至少准备5-10个不同的UA
]
def get_random_headers():
return {
'User-Agent': random.choice(user_agents),
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': 'https://www.baidu.com/'
}
在实际项目中,我发现Referer头特别重要。有次爬虫在服务器上跑不起来,本地却正常,最后发现是缺少Referer头导致的。这种细节问题最容易浪费调试时间。
4. 数据解析与存储方案
4.1 BeautifulSoup的进阶用法
解析HTML时,新手常犯的错误是过度依赖固定的class名。百度的结果容器class确实是"result",但其他网站可能变化。更健壮的做法是结合标签和属性多重过滤:
def parse_results(soup):
items = []
for item in soup.select('div[class*="result"]'):
try:
title = item.find('h3').get_text(strip=True)
link = item.find('a')['href']
# 提取摘要
abstract = item.find('div', class_=re.compile('c-abstract'))
abstract = abstract.get_text(strip=True) if abstract else ''
items.append({
'title': title,
'link': link,
'abstract': abstract
})
except Exception as e:
print(f"解析错误: {e}")
continue
return items
这个解析器更加健壮,使用了CSS选择器和正则表达式组合匹配。get_text(strip=True)能自动清理多余空白字符,比直接.text更干净。我还添加了异常处理,避免因个别元素解析失败导致整个程序中断。
4.2 数据存储的几种选择
抓取到的数据通常需要持久化存储。根据数据量和使用场景,有几种常用方案:
-
本地文件:适合小规模数据
import json def save_to_json(data, filename): with open(filename, 'a', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) -
CSV文件:适合表格型数据
import csv def save_to_csv(items, filename): with open(filename, 'a', newline='', encoding='utf-8') as f: writer = csv.DictWriter(f, fieldnames=['title', 'link', 'abstract']) writer.writerows(items) -
数据库:适合大规模数据
import sqlite3 def init_db(): conn = sqlite3.connect('search_results.db') c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS results (title text, link text, abstract text)''') conn.commit() return conn
我个人的经验是,小型项目用JSON足够,中型项目考虑CSV,当数据量超过10万条时再上数据库。过早优化是万恶之源,这句话在爬虫存储方案选择上尤其适用。
5. 常见问题排查与优化技巧
5.1 请求失败的常见原因
即使代码看起来完美,实际运行中还是会遇到各种问题。这些是我遇到最多的错误及解决方案:
-
403 Forbidden:通常是被网站识别为爬虫
- 解决方案:完善请求头,添加Cookie
-
连接超时:目标服务器响应慢或网络问题
- 解决方案:设置timeout参数并重试
try: response = requests.get(url, headers=headers, timeout=10) except requests.exceptions.Timeout: print("请求超时,正在重试...") response = requests.get(url, headers=headers, timeout=15) -
SSL证书错误:常见于某些特定环境
- 解决方案:添加verify=False参数(但会降低安全性)
5.2 性能优化实战建议
当需要抓取大量数据时,效率就成为关键问题。经过多次优化实践,我总结出这些有效方法:
-
会话保持:使用Session对象减少连接开销
session = requests.Session() session.headers.update(get_random_headers()) response = session.get(url) -
并发控制:适当使用多线程(但别太激进)
from concurrent.futures import ThreadPoolExecutor def crawl_page(page): url = build_search_url(keyword, page) # 爬取逻辑... with ThreadPoolExecutor(max_workers=3) as executor: executor.map(crawl_page, range(1, 6)) -
增量爬取:记录已抓取URL避免重复
seen_urls = set() if link not in seen_urls: process_link(link) seen_urls.add(link)
记住一个原则:对目标网站要温柔。我见过有人开50个线程狂爬,结果几分钟就被封IP。合理的做法是从小规模开始,逐步增加并发量,找到那个不触发反爬的甜蜜点。
更多推荐


所有评论(0)