本文系统梳理了 Python 爬虫的基础知识,从最基础的 urllib 和正则表达式,到解析库 BeautifulSoup,再到更加人性化的 Requests 库,以及进阶的多进程/异步并发抓取,最后介绍了应对动态网页的 Selenium。非常适合想要入门爬虫的新手学习。
⚖️ 合规声明:本文示例均使用 example.com、httpbin.org 等公开测试站点(baidu.com 仅作 Selenium 演示),不针对任何真实业务站点。爬虫技术仅供学习,实际使用时请遵守目标站点的 robots.txt 与服务条款,合理控制请求频率,勿用于商业用途或侵犯他人权益。
1. 使用 re 和 urllib 进行基础爬取
在 Python 中,最基础的网络请求库是内置的 urllib。配合正则表达式 (re 模块),我们可以实现最原始的网页抓取和信息提取。
1.1 发送请求与读取响应
使用 urllib.request.urlopen() 可以向指定的 URL 发送请求,并读取响应的 HTML 源码。读取出来的通常是字节 (bytes),我们需要使用 decode('utf-8') 将其转换为字符串。
1 2 3 4 5 6 7 8
| import urllib.request
url = 'http://www.example.com'
response = urllib.request.urlopen(url) html_content = response.read().decode('utf-8')
print(html_content)
|
1.2 结合正则表达式提取数据
一旦我们获取了 HTML 源码,就可以使用 re 模块来匹配和提取我们需要的数据,例如所有的图片链接或特定格式的文本。
1 2 3 4 5 6 7 8
| import re
img_pattern = re.compile(r'src="(http://.*?\.jpg)"') image_urls = re.findall(img_pattern, html_content)
for img in image_urls: print("找到图片链接:", img)
|
提示:虽然 urllib + re 是爬虫的基础,但使用正则表达式来解析复杂的 HTML 极其容易出错且难以维护。在实际开发中,我们更多使用下文介绍的专门解析库。
2. 使用 BeautifulSoup 解析网页
BeautifulSoup 是 Python 中最常用的 HTML/XML 解析库之一。它能把复杂的 HTML 文档转换成一个复杂的树形结构,每个节点都是 Python 对象,方便我们提取数据。
2.1 安装与基本使用
安装:pip install beautifulsoup4 lxml (推荐使用 lxml 作为解析器,速度更快)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from bs4 import BeautifulSoup
html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"><b>The Dormouse's story</b></p> <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a> <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> </body> </html> """
soup = BeautifulSoup(html_doc, 'lxml')
print(soup.title) print(soup.title.string)
|
2.2 查找元素
BeautifulSoup 提供了两种最常用的查找方法:find() 和 find_all()。
find():返回匹配的第一个元素。
find_all():返回一个包含所有匹配元素的列表。
1 2 3 4 5 6 7 8 9
| all_links = soup.find_all('a') for link in all_links: print(link.get('href'), link.text)
specific_link = soup.find('a', id='link1') class_links = soup.find_all('a', class_='sister')
|
3. 使用 Requests 优雅地发送请求
相比于内置的 urllib,第三方库 requests 的 API 设计更加人性化,被称为“HTTP for Humans”。
3.1 安装与基本请求
安装:pip install requests。
1 2 3 4 5 6 7
| import requests
url = 'http://httpbin.org/get' response = requests.get(url)
print(response.status_code) print(response.text)
|
许多网站会根据请求头(尤其是 User-Agent)来判断请求是否来自于真实的浏览器。如果不带 User-Agent,很容易被网站的反爬虫机制拦截。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| url = 'http://httpbin.org/get'
params = { 'name': 'weilai', 'age': 20 }
headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' }
response = requests.get(url, params=params, headers=headers) print(response.json())
|
3.3 处理 POST 请求
登录或者提交表单通常需要使用 POST 请求。
1 2 3 4 5
| post_url = 'http://httpbin.org/post' data = {'username': 'admin', 'password': '123'}
response = requests.post(post_url, data=data) print(response.text)
|
4. 多进程、分布式与异步并发爬取
当需要抓取大量数据时,单线程的爬虫速度极慢。我们可以利用 Python 的并发特性来加速爬取。
4.1 多进程 (multiprocessing)
Python 的多线程受到 GIL(全局解释器锁)的限制,无法充分利用多核 CPU。对于爬虫这种 I/O 密集型 + 数据处理的任务,使用多进程池 (Pool) 是一个不错的加速方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import requests from multiprocessing import Pool
def fetch_url(url): try: response = requests.get(url, timeout=5) print(f"成功抓取 {url}, 状态码: {response.status_code}") except Exception as e: print(f"抓取失败 {url}: {e}")
if __name__ == '__main__': urls = [f'http://httpbin.org/get?page={i}' for i in range(10)]
with Pool(4) as pool: pool.map(fetch_url, urls)
|
4.2 异步 I/O (asyncio + aiohttp)
相比多进程,异步 I/O 更加轻量级,它在单线程下通过事件循环 (Event Loop) 在等待网络 I/O 时切换任务,能够支持极高的并发量。
通常我们需要配合 aiohttp 库来发起异步请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import asyncio import aiohttp import time
async def fetch(session, url): async with session.get(url) as response: return await response.text()
async def main(): urls = [f'http://httpbin.org/get?page={i}' for i in range(10)] async with aiohttp.ClientSession() as session: tasks = [asyncio.create_task(fetch(session, url)) for url in urls] results = await asyncio.gather(*tasks) print(f"成功获取 {len(results)} 个页面的内容。")
if __name__ == '__main__': start = time.time() asyncio.run(main()) print(f"异步爬取耗时: {time.time() - start:.2f} 秒")
|
5. 应对动态网页:Selenium 的使用
传统的爬虫库(如 requests)只能获取服务器返回的初始 HTML 源码。如果网页的数据是通过 JavaScript 异步加载(Ajax)渲染出来的,requests 抓到的源码里就不会包含这些数据。
为了解决这个问题,我们可以使用 Selenium。它是一个自动化测试工具,可以驱动真实的浏览器(如 Chrome)打开网页,执行 JavaScript 代码,从而获取渲染后的最终页面。
5.1 安装与配置
- 安装库:
pip install selenium
- 下载驱动:需要下载与你本地 Chrome 浏览器版本对应的
chromedriver,并将其加入系统环境变量。
5.2 基础用法示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| from selenium import webdriver from selenium.webdriver.common.by import By import time
driver = webdriver.Chrome()
try: driver.get("https://www.baidu.com")
search_box = driver.find_element(By.ID, "kw") search_box.send_keys("Python 爬虫")
submit_btn = driver.find_element(By.ID, "su") submit_btn.click()
time.sleep(3)
page_source = driver.page_source print("页面源码长度:", len(page_source))
finally: driver.quit()
|
5.3 无头模式 (Headless)
在服务器或者后台运行爬虫时,我们不需要弹出可视化的浏览器窗口。可以开启 Chrome 的无头模式。
1 2 3 4 5 6 7 8 9 10
| from selenium.webdriver.chrome.options import Options
chrome_options = Options() chrome_options.add_argument("--headless") chrome_options.add_argument("--disable-gpu")
driver = webdriver.Chrome(options=chrome_options) driver.get("https://www.baidu.com") print(driver.title) driver.quit()
|
总结:爬虫技术从发送请求 (urllib / requests),到解析数据 (re / BeautifulSoup / XPath),再到突破并发瓶颈 (multiprocessing / asyncio) 和应对动态渲染 (Selenium),构成了一个完整的技能树。熟练掌握这些工具,你就能应对绝大多数的数据采集需求。
附录:课程笔记截图
📌 说明:本附录几乎无正文,为 慕课网(imooc)数据采集课程 的学习笔记,主体是课程视频的截图整理,内容版权归课程方所有,仅供个人学习复习之用。
https://www.imooc.com/video/12637 课程地址











