在信息爆炸的互联网时代,人工从海量网页中提取数据早已不现实,而Python网络爬虫正是解决这一问题的核心利器。无论是数据分析的数据源获取、行业信息的自动化聚合,还是网站的自动化测试、内容监控,爬虫技术都能发挥巨大作用。

一、爬虫入门:先搞懂底层核心原理

很多人学爬虫一上来就写代码,却忽略了最基础的网络和网页原理,这也是后续遇到各种问题的根源。爬虫的本质,就是模拟浏览器向服务器发送请求,接收并解析响应数据,提取出我们需要的信息

1.1 HTTP协议:爬虫的通信基础

互联网的信息交互,本质上是基于HTTP/HTTPS协议的请求-响应模式。浏览器能看到的网页内容,都是先向服务器发送HTTP请求,服务器返回响应数据后渲染出来的,爬虫做的就是模拟这个过程。

一次完整的HTTP交互主要包含两部分:

  • 请求头(Request Headers):浏览器向服务器发送的身份、能力、偏好信息,比如User-Agent(客户端标识)、Cookie(会话信息)、Accept(可接收的数据类型)等,这也是反爬对抗的核心阵地。
  • 响应体(Response):服务器返回的内容,主要是HTML文档,也可能是JSON、图片、文件等二进制数据。

1.2 HTML结构:网页数据的载体

我们要抓取的文本、链接、图片地址,几乎都嵌套在HTML标签中。HTML是一种标记语言,通过标签的嵌套和属性定义网页的结构,比如:

  • <h1>-<h6>:标题标签
  • <p>:段落标签
  • <a>:超链接标签,核心属性href是链接地址
  • <div>/<span>:通用容器标签,通过id/class属性区分元素

爬虫的解析环节,核心就是从层层嵌套的HTML标签中,精准定位到包含目标数据的元素,提取出内容。
在这里插入图片描述
图注:HTML文档的树形结构,是爬虫定位元素的基础

二、基础爬虫三板斧:请求、解析、存储

掌握了基础原理后,我们就可以上手最核心的基础爬虫开发了。一个完整的基础爬虫,离不开网络请求、网页解析、数据存储三个核心环节,下面我们逐个拆解,附可直接运行的代码示例。

2.1 网络请求:让Python拿到网页数据

Python中发起HTTP请求的主流库有两个:Python内置的urllib,和第三方库requests。其中requests凭借极简的API、完善的功能,成为爬虫开发的首选。

安装requests
pip install requests
基础GET请求:抓取静态网页

以抓取Python官网页面为例,几行代码就能拿到完整的HTML内容:

import requests

# 发起GET请求
response = requests.get("https://www.python.org/")
# 查看响应状态码,200代表请求成功
print(f"响应状态码:{response.status_code}")
# 查看网页HTML源码
print(response.text)
POST请求:模拟表单提交

很多网站的搜索、登录功能,都需要通过POST请求提交表单数据。requests处理POST请求同样简单,比如提交一个简单的用户信息表单:

import requests

# 要提交的表单数据
params = {'firstname': 'Python', 'lastname': 'Scraping'}
# 发起POST请求
r = requests.post("http://pythonscraping.com/files/processing.php", data=params)
# 打印服务器返回的结果
print(r.text)

在这里插入图片描述

图注:requests库发起请求到获取响应的完整流程

2.2 网页解析:从HTML中精准提取数据

拿到HTML源码后,下一步就是从杂乱的标签中提取目标数据,最常用的工具就是BeautifulSoup,它可以把HTML文本解析成树形结构,通过标签名、属性、层级关系快速定位元素。

安装BeautifulSoup
pip install beautifulsoup4
pip install lxml  # 解析器依赖
基础解析示例:提取维基百科的词条链接
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re

# 请求页面并创建BeautifulSoup对象
html = urlopen("http://en.wikipedia.org/wiki/Python_(programming_language)")
bsObj = BeautifulSoup(html, "lxml")

# 提取页面正文内容
content = bsObj.find("div", {"id":"mw-content-text"}).get_text()
# 提取页面中所有指向维基词条的链接
links = bsObj.find("div", {"id":"bodyContent"}).findAll("a", href=re.compile("^(/wiki/)((?!:).)*$"))

print(f"页面正文前200字:{content[:200]}")
print(f"找到的词条链接数量:{len(links)}")
print(f"第一个链接:{links[0]['href']}")

BeautifulSoup的核心定位方法:

  • find():返回匹配到的第一个元素
  • findAll():返回匹配到的所有元素列表
  • 定位条件支持标签名、idclass、自定义属性,还能结合正则表达式做模糊匹配,应对复杂的页面结构绰绰有余。

2.3 数据存储:把抓取的数据持久化

抓下来的数据不能只打印在控制台,需要持久化存储起来,方便后续分析和使用。常用的存储方式有三种:本地文件(txt/csv)、媒体文件、MySQL数据库

1. 存储为CSV文件

CSV是表格类数据最常用的存储格式,能直接用Excel打开,Python内置的csv库就能轻松处理:

import csv
from urllib.request import urlopen
from bs4 import BeautifulSoup

# 抓取维基百科的文本编辑器对比表格
html = urlopen("http://en.wikipedia.org/wiki/Comparison_of_text_editors")
bsObj = BeautifulSoup(html, "lxml")
# 定位主表格
table = bsObj.findAll("table",{"class":"wikitable"})[0]
rows = table.findAll("tr")

# 写入CSV文件
with open("editors.csv", 'wt', newline='', encoding='utf-8') as csvFile:
    writer = csv.writer(csvFile)
    for row in rows:
        csvRow = []
        for cell in row.findAll(['td', 'th']):
            csvRow.append(cell.get_text().strip())
        writer.writerow(csvRow)
print("CSV文件写入完成!")
2. 下载并存储媒体文件

爬虫不仅能抓文本,还能下载图片、文档、视频等文件,urlliburlretrieve可以直接实现文件下载:

from urllib.request import urlopen, urlretrieve
from bs4 import BeautifulSoup

# 抓取页面中的logo图片
html = urlopen("http://www.pythonscraping.com")
bsObj = BeautifulSoup(html, "lxml")
# 定位图片地址
imageLocation = bsObj.find("a", {"id": "logo"}).find("img")["src"]
# 下载图片并保存到本地
urlretrieve(imageLocation, "logo.jpg")
print("图片下载完成!")
3. 存储到MySQL数据库

对于大量、结构化的数据,存入数据库是最佳选择,方便后续的增删改查和数据分析。Python中通过pymysql库操作MySQL:

import pymysql
from urllib.request import urlopen
from bs4 import BeautifulSoup

# 连接MySQL数据库
conn = pymysql.connect(
    host='127.0.0.1',
    user='root',
    passwd='你的数据库密码',
    db='scraping',
    charset='utf8mb4'
)
cur = conn.cursor()

# 抓取维基百科词条标题和正文
html = urlopen("http://en.wikipedia.org/wiki/Python_(programming_language)")
bsObj = BeautifulSoup(html, "lxml")
title = bsObj.find("h1").get_text().strip()
content = bsObj.find("div", {"id":"mw-content-text"}).find("p").get_text().strip()

# 写入数据库
try:
    cur.execute("INSERT INTO pages (title, content) VALUES (%s, %s)", (title, content))
    conn.commit()
    print("数据写入数据库成功!")
except Exception as e:
    print(f"写入失败:{e}")
    conn.rollback()
finally:
    cur.close()
    conn.close()

三、更高效的数据获取:直接调用API

很多时候,我们不需要费力解析HTML页面,网站官方提供的API接口是更稳定、更高效的数据来源。API(应用程序编程接口)会直接返回结构化的JSON数据,省去了HTML解析的环节,大幅降低爬虫开发成本。

3.1 API调用的核心特点

  • 数据格式规范:几乎都是JSON格式,Python内置的json库就能直接解析
  • 稳定性高:网站的HTML结构经常改版,但API接口通常会保持兼容
  • 权限可控:大多需要api_key鉴权,有明确的请求频率限制

3.2 API调用实战示例

以一个通用的RESTful API为例,用requests调用接口并解析数据:

import requests
import json

# 接口地址与请求参数
api_url = "https://api.example.com/v1/data"
params = {
    "api_key": "你的api_key",
    "keyword": "python",
    "page": 1,
    "page_size": 10
}

# 发起GET请求调用API
response = requests.get(api_url, params=params)
# 解析JSON响应数据
if response.status_code == 200:
    data = json.loads(response.text)
    print(f"接口返回总条数:{data.get('total')}")
    print(f"数据列表:{data.get('list')}")
else:
    print(f"接口请求失败,状态码:{response.status_code}")

书中也举了很多经典API的例子,比如The Echo Nest音乐API、Twitter API、Google地图API,核心逻辑都是一致的:通过API地址传递鉴权信息和查询参数,解析返回的JSON数据

四、进阶爬虫:搞定JavaScript动态渲染页面

基础爬虫能应对静态页面,但现在越来越多的网站用JavaScript动态渲染页面,比如:

  • 页面内容通过Ajax异步加载,初始HTML里没有目标数据
  • 单页应用(SPA),整个页面靠JavaScript渲染生成
  • 数据需要触发点击、滚动等事件后才会加载

这时候,传统的requests+BeautifulSoup就失效了,因为它们只能拿到初始的HTML源码,无法执行JavaScript代码。解决这个问题的神器,就是Selenium

4.1 Selenium是什么

Selenium原本是网站自动化测试工具,它可以驱动浏览器(包括无头浏览器PhantomJS/Chrome Headless),完全模拟人的操作:执行JavaScript、点击按钮、填写表单、滚动页面,最终拿到浏览器渲染完成的完整页面内容,完美应对动态页面。

安装与准备
pip install selenium

同时需要下载对应浏览器的驱动:Chrome需要下载ChromeDriver,Firefox需要GeckoDriver,注意驱动版本要和浏览器版本匹配。

4.2 实战:抓取Ajax动态加载的内容

以一个Ajax加载的示例页面为例,用Selenium获取渲染后的内容:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

# 初始化浏览器驱动(这里用Chrome无头模式)
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")  # 无头模式,不打开浏览器窗口
options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36")
driver = webdriver.Chrome(options=options)

# 访问目标页面
driver.get("http://pythonscraping.com/pages/javascript/ajaxDemo.html")

# 方式1:强制等待3秒,等待Ajax加载完成
# time.sleep(3)

# 方式2:智能等待,直到目标元素加载出来(推荐,更高效)
try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "loadedButton"))
    )
finally:
    # 提取渲染后的内容
    content = driver.find_element(By.ID, "content").text
    print(f"动态加载的内容:{content}")
    # 关闭驱动
    driver.quit()

Selenium不仅能获取内容,还能模拟各种用户操作:

# 填写表单
input_box = driver.find_element(By.NAME, "username")
input_box.send_keys("你的用户名")

# 点击按钮
submit_btn = driver.find_element(By.ID, "submit")
submit_btn.click()

# 滚动页面到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

# 拖拽元素
from selenium.webdriver import ActionChains
source = driver.find_element(By.ID, "draggable")
target = driver.find_element(By.ID, "div2")
actions = ActionChains(driver)
actions.drag_and_drop(source, target).perform()

五、图像识别与OCR:让爬虫看懂图片里的文字

爬虫的另一个常见难题,就是数据藏在图片里,最典型的就是验证码,还有一些网站把关键信息做成图片防止抓取。这时候就需要OCR(光学文字识别)技术,把图片里的文字提取出来。

5.1 核心工具:Pillow+Tesseract

  • Pillow:Python最常用的图像处理库,负责图片的预处理、裁剪、阈值过滤,提升识别准确率
  • Tesseract:谷歌维护的开源OCR引擎,识别精度高,支持多语言,配合Python封装库pytesseract使用
环境准备
  1. 安装Tesseract引擎:从Tesseract官网下载安装,Windows需要配置环境变量
  2. 安装Python库:
pip install pillow pytesseract

5.2 实战:图片文字识别

先看一个基础的图片识别示例:

import pytesseract
from PIL import Image

# 打开图片
image = Image.open("text_image.png")
# 识别图片中的文字
text = pytesseract.image_to_string(image, lang='eng')
print(f"识别结果:\n{text}")

对于有背景干扰、模糊的图片,直接识别准确率很低,需要先用Pillow做预处理,比如阈值过滤、去噪:

from PIL import Image
import pytesseract

def clean_image(image_path, output_path, threshold=143):
    """图片预处理:灰度+二值化,提升识别准确率"""
    image = Image.open(image_path)
    # 转为灰度图
    image = image.convert('L')
    # 二值化阈值过滤,去掉背景干扰
    image = image.point(lambda x: 0 if x < threshold else 255)
    image.save(output_path)
    return image

# 预处理图片
cleaned_img = clean_image("blurry_text.jpg", "cleaned_text.png")
# 识别预处理后的图片
text = pytesseract.image_to_string(cleaned_img, lang='eng')
print(f"预处理后的识别结果:\n{text}")

5.3 验证码识别

对于简单的字符验证码,通过“图片预处理+Tesseract识别+模型训练”,就能实现自动破解。书中也详细介绍了Tesseract的训练方法,针对特定的验证码字体做定制化训练后,识别准确率能大幅提升。

六、反爬规避:让爬虫更像“人”,不被封IP

随着网站反爬技术的升级,爬虫开发的很大一部分工作,都在和反爬策略对抗。网站会从多个维度判断访问者是真人还是爬虫,我们需要针对性地做伪装,核心原则就是:让爬虫的行为尽可能贴近正常的人类浏览器

6.1 最基础的伪装:修改请求头

网站反爬的第一步,就是检查请求头里的User-Agent,Python内置的urllib默认UA是Python-urllib/3.x,一眼就会被识别为爬虫。

requests修改请求头,模拟浏览器:

import requests

# 模拟Chrome浏览器的请求头
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0",
    "Accept": "application/x-clarity-gzip",
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
    "Referer": "https://blog.csdn.net/weixin_56097064/article/details/144262022?",
    "Accept-Encoding": "gzip, deflate, br, zstd"
}

response = requests.get("https://so.csdn.net/", headers=headers)

在这里插入图片描述
图注:完整的浏览器请求头,能大幅降低被识别的概率

6.2 处理Cookie与会话保持

网站会通过Cookie跟踪用户的会话状态,比如登录后的权限、访问轨迹。如果请求不带Cookie,或者Cookie异常,很容易被拦截。

requestsSession对象可以自动保持Cookie,模拟浏览器的会话机制:

import requests

# 创建会话对象,自动维护Cookie
session = requests.Session()
# 设置会话级别的请求头,所有请求都会带上
session.headers.update(headers)

# 第一步:提交登录表单,会话会自动保存登录后的Cookie
login_params = {"username": "xxx", "password": "xxx"}
session.post("https://www.example.com/login", data=login_params)

# 第二步:访问需要登录才能看的页面,自动带上登录Cookie
response = session.get("https://www.example.com/user/profile")
print(response.text)

6.3 控制访问频率,避免给服务器造成压力

短时间内大量的高频请求,是最容易被网站封禁IP的行为。哪怕伪装的再好,一秒钟请求几十次,也会被网站的风控系统直接拦截。

解决方法很简单:在每次请求之间增加随机延时,模拟真人的浏览节奏。

import time
import random

# 每次请求后,随机暂停1-3秒
time.sleep(random.uniform(1, 3))

# 采集大量页面时,避免集中请求,错峰运行
# 比如夜间网站流量低的时候运行,降低对服务器的影响

6.4 代理IP:突破IP封禁

如果网站对单IP的请求频率做了严格限制,或者已经封禁了你的IP,就需要用代理IP来切换出口IP,让网站认为是不同的用户在访问。

requests使用代理IP非常简单:

import requests

# 代理IP格式:协议://IP:端口
proxies = {
    "http": "http://127.0.0.1:7890",
    "https": "http://127.0.0.1:7890"
}

# 发起请求时带上代理
response = requests.get("https://icanhazip.com", proxies=proxies)
print(f"当前出口IP:{response.text}")

对于高匿名度的需求,还可以使用Tor洋葱路由网络,实现IP的动态匿名化,书中也详细介绍了Tor代理的配置和使用方法。

6.5 避开蜜罐陷阱

很多网站会设置“蜜罐”来抓爬虫:比如在HTML里放一个对用户隐藏的链接,真人在浏览器里看不到也不会点击,而爬虫如果不加判断地抓取页面里所有链接并访问,就会直接触发网站的反爬规则,被封禁IP。

应对方法:

  • 用Selenium时,通过is_displayed()判断元素是否对用户可见,避开隐藏元素
  • 解析链接时,过滤掉隐藏的、无意义的链接,遵守网站的robots.txt规则

七、爬虫的工程化与高级玩法

掌握了以上内容,你已经能应对绝大多数爬虫场景了。在此基础上,还可以拓展更多工程化的能力,让爬虫更稳定、更强大。

7.1 用爬虫做网站自动化测试

Selenium不仅能做爬虫,还是网站自动化测试的神器。我们可以用Python+Selenium写单元测试,自动验证网站的功能是否正常:

  • 表单提交是否正常
  • 页面元素是否正确渲染
  • 链接跳转是否符合预期
  • 拖拽、点击等交互功能是否生效

示例:网站表单功能的自动化测试

import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By

class TestWebsiteForm(unittest.TestCase):
    def setUp(self):
        # 每个测试用例执行前,初始化浏览器
        options = webdriver.ChromeOptions()
        options.add_argument("--headless=new")
        self.driver = webdriver.Chrome(options=options)
        self.driver.get("http://pythonscraping.com/pages/files/form.html")

    def tearDown(self):
        # 每个测试用例执行后,关闭浏览器
        self.driver.quit()

    def test_form_submit(self):
        # 填写表单
        self.driver.find_element(By.NAME, "firstname").send_keys("Test")
        self.driver.find_element(By.NAME, "lastname").send_keys("User")
        self.driver.find_element(By.ID, "submit").click()
        # 验证提交后的结果
        result = self.driver.find_element(By.TAG_NAME, "body").text
        self.assertEqual("Hello there, Test User!", result.strip())

if __name__ == '__main__':
    unittest.main()

7.2 远程部署与分布式采集

本地运行爬虫有很多限制:电脑关机就停止、本地IP容易被封、带宽和性能有限。企业级的爬虫,都会部署在远程服务器上,甚至用分布式架构实现多机并发采集。

  • 单机远程部署:把爬虫脚本放到阿里云、腾讯云、AWS的云服务器上,7×24小时运行,用Linux的crontab实现定时执行。
  • 分布式采集:对于超大规模的抓取任务,用多台服务器/多个实例分布式运行,通过任务队列分配抓取任务,比如Scrapy-Redis框架。

八、红线不能碰:爬虫的法律与道德规范

技术是工具,合规是底线。爬虫开发中,法律和道德问题是必须放在第一位的,稍有不慎就可能触犯法律,承担民事甚至刑事责任。

8.1 必须遵守的核心规则

  1. 遵守robots.txt协议
    网站根目录下的robots.txt文件,会明确声明哪些页面允许抓取、哪些禁止抓取。比如维基百科的robots.txt,明确禁止爬虫采集搜索页面、随机页面等,只允许采集词条正文。
    即使robots.txt没有法律效力,但它是网站管理者的明确意愿,遵守它能避免绝大多数纠纷。

  2. 版权问题
    文字、图片、视频等内容都受《著作权法》保护,未经授权抓取并商用,会构成版权侵权。即使是非商用,也要注意合理使用的边界,不能完整复制、传播他人的原创内容。
    事实性数据(比如价格、名称、公开的统计数据)不受版权保护,但也要注意数据来源的合规性。

  3. 不要给服务器造成负担
    高频请求拖垮对方服务器,会构成“侵犯动产”,哪怕是小网站,也可能因为你的爬虫导致服务不可用,面临法律追责。一定要控制抓取频率,优先选择夜间低峰期运行。

  4. 敏感信息绝对不能碰
    个人隐私信息、用户账号信息、未公开的商业机密、政府非公开信息,哪怕网站存在漏洞能轻易获取,也绝对不能抓取和传播,否则会触犯《个人信息保护法》《网络安全法》,甚至《刑法》。

8.2 经典法律案例警示

  • eBay诉Bidder’s Edge案:高频抓取eBay的拍卖数据,被法院认定为侵犯动产,最终败诉。
  • 美国政府诉Auernheimer案:利用网站漏洞抓取用户隐私信息并曝光,即便后续法院改判无罪,也经历了搜查、诉讼、牢狱之灾。
  • 爬虫抓取公开信息,但用于非法用途(比如电信诈骗、恶意竞争),同样会触犯法律。

在这里插入图片描述

图注:网站robots.txt文件示例,明确了爬虫的抓取规则

robots.txt规则:

  • User-agent:指定规则适用的爬虫名称。*表示适用于所有爬虫。
  • Disallow:指定不希望被爬取的路径。一条Disallow:后为空,表示允许所有。
  • Allow(非所有爬虫都支持):用于在Disallow的父目录下,特别允许某个子路径。优先级通常高于 Disallow。
  • Crawl-delay:建议爬虫两次请求之间至少间隔的秒数。
  • Sitemap:提示网站地图的位置,方便爬虫发现内容。

九、总结

Python网络爬虫是一门实践性极强的技术,从基础的请求-解析-存储,到动态页面渲染、OCR识别、反爬对抗,再到工程化部署和合规性把控,需要我们不断学习和实践。

学习爬虫的核心路径,总结下来就是三步:

  1. 打牢基础:吃透HTTP协议、HTML结构、Python基础库的使用,写出能稳定运行的基础爬虫。
  2. 解决问题:针对动态页面、验证码、反爬策略等具体问题,逐个学习对应的解决方案,积累实战经验。
  3. 守住底线:永远把合规性放在第一位,明确爬虫的使用目的,不越界、不滥用技术。

最后,技术的价值在于解决问题,而不是制造问题。希望这篇文章能帮你系统掌握Python爬虫技术,在合规的前提下,用技术提升效率、创造价值。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐