本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本项目是一个基于Python3的网络爬虫脚本,旨在抓取百度等网站的相关关键词数据,并成功绕过常见的安全验证机制。该脚本使用了如代理IP、动态User-Agent、HTML解析、延时策略等技术,具备良好的反爬应对能力。适用于学习网络请求、网页解析、数据提取等爬虫核心技术,并可作为实践案例应用于搜索引擎数据分析领域。
Pyhon3相关词爬虫脚本下载 绕过安全验证,亲测,有效

1. Python3爬虫基础原理与环境搭建

1.1 爬虫基本概念与工作原理

网络爬虫(Web Crawler)是一种自动抓取互联网信息的程序,其核心原理是模拟浏览器行为,向目标网站发送HTTP请求,接收响应内容,并从中提取所需数据。一个典型的爬虫工作流程包括:

  1. 发起请求 :爬虫程序向目标服务器发送HTTP请求;
  2. 接收响应 :服务器返回HTML、JSON或XML格式的数据;
  3. 解析内容 :使用解析库(如BeautifulSoup、lxml、XPath)提取关键信息;
  4. 存储数据 :将提取的数据保存到数据库或文件中;
  5. 遵循规则 :遵守网站的robots.txt协议,避免频繁请求造成服务器压力。

爬虫可以分为 通用爬虫 (如搜索引擎使用的爬虫)和 聚焦爬虫 (针对特定网站或数据结构进行定制)。本章将围绕聚焦爬虫展开实践。

2. Requests库网络请求实现与实践

作为Python网络爬虫的核心组件之一, Requests 库因其简洁的API和强大的功能,被广泛应用于HTTP请求的发送与处理。本章将围绕 Requests 库的核心功能展开,详细介绍其在网络请求中的应用,包括GET和POST请求的发送、请求参数的设置、异常处理机制、性能优化策略等内容。通过本章的学习,读者将能够熟练掌握 Requests 库的使用方法,并能够根据实际场景进行灵活配置与优化。

2.1 Requests库的基本使用

Requests 是Python中用于发送HTTP请求的第三方库,其设计理念是“人性化”,使得开发者可以以极少的代码完成复杂的网络请求任务。在本节中,我们将介绍 Requests 库的两个基础功能:发送GET和POST请求,以及响应内容的获取与处理。

2.1.1 发送GET和POST请求

HTTP协议中最常见的两种请求方式是GET和POST。GET请求通常用于从服务器获取数据,而POST请求则用于向服务器提交数据。

GET请求示例
import requests

response = requests.get('https://httpbin.org/get')
print(response.text)

代码逻辑分析:

  1. requests.get() :发送GET请求到指定的URL地址(此处为测试用的 httpbin.org )。
  2. response.text :获取响应内容,并以字符串形式输出。
POST请求示例
import requests

data = {'username': 'test', 'password': '123456'}
response = requests.post('https://httpbin.org/post', data=data)
print(response.json())

代码逻辑分析:

  1. data = {'username': 'test', 'password': '123456'} :定义要提交的数据,以字典形式传递。
  2. requests.post() :发送POST请求,并通过 data 参数将数据提交到服务器。
  3. response.json() :将响应内容解析为JSON格式并输出。
GET与POST对比表
特性 GET请求 POST请求
数据可见性 URL中可见 数据在请求体中不可见
安全性 不适合传输敏感数据 更适合传输敏感数据
数据长度限制 有URL长度限制 无限制
缓存与书签支持 支持 不支持
用途 获取数据 提交数据

2.1.2 响应内容的获取与处理

在发送HTTP请求后,服务器会返回响应数据。 Requests 库提供了多种方式来处理这些响应内容,包括文本、JSON、状态码、响应头等。

响应处理示例
import requests

response = requests.get('https://httpbin.org/get')

print("状态码:", response.status_code)
print("响应头:", response.headers)
print("文本内容:", response.text)
print("JSON内容:", response.json())

代码逻辑分析:

  1. response.status_code :获取HTTP响应的状态码(如200表示成功)。
  2. response.headers :获取响应头信息,通常包含服务器类型、内容类型等。
  3. response.text :获取响应的文本内容。
  4. response.json() :将响应内容解析为JSON格式(如果响应是JSON类型)。
常见HTTP状态码含义
状态码 含义
200 请求成功
301 永久重定向
400 请求错误(客户端错误)
404 资源未找到
500 服务器内部错误
流程图:HTTP请求响应过程
graph TD
    A[客户端] --> B[发送HTTP请求]
    B --> C[服务器接收请求]
    C --> D[处理请求]
    D --> E[生成响应数据]
    E --> F[返回响应]
    F --> G[客户端接收响应]

2.2 请求参数与会话管理

在实际开发中,往往需要向服务器传递参数,或者维护会话状态。本节将介绍如何在 Requests 中设置URL参数、请求头,以及使用 Session 对象管理Cookie。

2.2.1 URL参数与请求头设置

URL参数设置示例
import requests

params = {'key1': 'value1', 'key2': 'value2'}
response = requests.get('https://httpbin.org/get', params=params)
print(response.url)

代码逻辑分析:

  1. params :定义URL参数,以字典形式传递。
  2. requests.get() :通过 params 参数将键值对自动拼接到URL后面。
  3. response.url :打印拼接后的完整URL。
请求头设置示例
import requests

headers = {
    'User-Agent': 'Mozilla/5.0',
    'Accept-Encoding': 'gzip, deflate',
    'Connection': 'keep-alive'
}
response = requests.get('https://httpbin.org/get', headers=headers)
print(response.request.headers)

代码逻辑分析:

  1. headers :定义自定义请求头字段,如 User-Agent
  2. requests.get() :通过 headers 参数传递请求头。
  3. response.request.headers :打印发送的请求头信息。

2.2.2 Session对象与Cookie管理

Session 对象可以跨请求保持某些参数,例如Cookie,非常适合模拟登录等场景。

Session示例:登录并保持会话
import requests

session = requests.Session()

# 登录请求
login_data = {'username': 'user', 'password': 'pass'}
session.post('https://httpbin.org/post', data=login_data)

# 后续请求将携带登录后的Cookie
response = session.get('https://httpbin.org/get')
print(response.cookies.get_dict())

代码逻辑分析:

  1. requests.Session() :创建一个 Session 对象。
  2. session.post() :发送POST请求进行登录。
  3. session.get() :后续GET请求会自动携带登录时获取的Cookie。
  4. response.cookies.get_dict() :获取Cookie信息。
Cookie管理流程图
graph TD
    A[创建Session对象] --> B[发送登录请求]
    B --> C[服务器返回Cookie]
    C --> D[Session自动保存Cookie]
    D --> E[后续请求携带Cookie]

2.3 异常处理与高级功能

在网络请求过程中,可能会遇到超时、连接失败、服务器错误等问题。本节将介绍如何使用 Requests 库的异常处理机制,以及如何实现文件的上传与下载。

2.3.1 超时设置与重试机制

超时设置示例
import requests

try:
    response = requests.get('https://httpbin.org/get', timeout=3)
    print(response.status_code)
except requests.Timeout:
    print("请求超时")

代码逻辑分析:

  1. timeout=3 :设置请求超时时间为3秒。
  2. try...except :捕获 requests.Timeout 异常,处理超时情况。
使用 retry 实现重试机制(需配合 urllib3
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
import requests

s = requests.Session()
retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504])
s.mount('https://', HTTPAdapter(max_retries=retries))

response = s.get('https://httpbin.org/get')
print(response.status_code)

代码逻辑分析:

  1. Retry() :定义重试策略,最大重试次数为5次。
  2. HTTPAdapter() :将重试策略绑定到Session对象。
  3. s.get() :发送请求,自动根据策略重试。

2.3.2 文件上传与下载实践

文件上传示例
import requests

url = 'https://httpbin.org/post'
file_path = 'example.txt'

with open(file_path, 'rb') as f:
    files = {'file': f}
    response = requests.post(url, files=files)

print(response.text)

代码逻辑分析:

  1. with open() :以二进制模式打开文件。
  2. files={'file': f} :定义上传的文件字段。
  3. requests.post() :发送POST请求上传文件。
文件下载示例
import requests

url = 'https://httpbin.org/image/jpeg'
response = requests.get(url)

with open('downloaded.jpg', 'wb') as f:
    f.write(response.content)

print("文件下载完成")

代码逻辑分析:

  1. response.content :获取响应的二进制内容。
  2. with open(..., 'wb') :以二进制写入模式保存文件。

2.4 Requests库的性能优化

在网络爬虫开发中,性能优化是提升效率的重要手段。本节将介绍如何通过 Session 对象和并发处理来提升 Requests 库的性能。

2.4.1 使用Session提升效率

如前所述, Session 对象可以复用底层的TCP连接,从而减少重复建立连接的开销。

import requests

session = requests.Session()
urls = ['https://httpbin.org/get'] * 10

for url in urls:
    response = session.get(url)
    print(response.status_code)

性能提升分析:

  • 复用连接避免了频繁的TCP握手和TLS协商。
  • 减少了DNS解析时间。
  • 提高了整体请求效率。

2.4.2 并发请求与异步处理初步探讨

虽然 Requests 库本身是同步的,但可以通过多线程或异步库(如 grequests aiohttp )实现并发请求。

使用 concurrent.futures 实现多线程请求
import requests
from concurrent.futures import ThreadPoolExecutor

def fetch(url):
    response = requests.get(url)
    return response.status_code

urls = ['https://httpbin.org/get'] * 10

with ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(fetch, urls))

print(results)

代码逻辑分析:

  1. ThreadPoolExecutor :创建线程池,设置最大并发数为5。
  2. executor.map() :并发执行 fetch 函数。
  3. 返回每个请求的状态码列表。
并发请求性能对比表
方法 并发能力 适用场景 性能表现
单线程顺序请求 小规模数据抓取
多线程+Session 中等规模数据抓取 中等
异步+aiohttp 大规模高并发数据抓取

总结:
本章深入讲解了 Requests 库在网络请求中的核心功能,包括GET与POST请求的发送、响应处理、参数设置、会话管理、异常处理、文件上传下载以及性能优化策略。通过代码示例和流程图的结合,读者能够清晰理解每个功能的实现原理,并掌握在实际项目中如何灵活运用这些技巧。下一章我们将进入网页解析技术,介绍 BeautifulSoup 库的使用方法。

3. BeautifulSoup与网页解析技术

在网页数据抓取的过程中,获取网页内容只是第一步,真正的挑战在于如何从这些 HTML 或 XML 文档中提取出有用的数据。 BeautifulSoup 是 Python 中一个非常强大的网页解析库,它能够将复杂的 HTML 文档解析成一个易于操作的对象树,使得开发者可以快速定位和提取所需的信息。本章将深入探讨 BeautifulSoup 的核心功能、数据提取方法、高级解析技巧以及实际应用案例,帮助读者掌握如何高效地进行网页内容解析。

3.1 BeautifulSoup库的核心功能

BeautifulSoup 提供了多种方式来解析 HTML 或 XML 文档,其核心在于将文档结构转化为一个可遍历的树形结构,便于提取和操作节点数据。

3.1.1 解析HTML文档结构

在使用 BeautifulSoup 之前,我们需要先导入该库,并传入 HTML 内容。以下是一个基本的 HTML 解析示例:

from bs4 import BeautifulSoup

html_doc = """
<html>
<head><title>示例页面</title></head>
<body>
<h1>欢迎访问我的网站</h1>
<p class="content">这是一个段落。</p>
<p class="footer">这是页脚信息。</p>
</body>
</html>

soup = BeautifulSoup(html_doc, 'html.parser')
print(soup.prettify())
代码逻辑分析与参数说明:
  • BeautifulSoup() :构造函数接收两个参数:
  • 第一个参数是待解析的 HTML 文档内容(字符串形式)。
  • 第二个参数是解析器类型,常用的有 'html.parser' (Python 内置解析器)、 'lxml' (基于 C 的高性能解析器)等。
  • soup.prettify() :该方法返回格式化后的 HTML 字符串,便于查看结构。
输出示例:
<html>
 <head>
  <title>
   示例页面
  </title>
 </head>
 <body>
  <h1>
   欢迎访问我的网站
  </h1>
  <p class="content">
   这是一个段落。
  </p>
  <p class="footer">
   这是页脚信息。
  </p>
 </body>
</html>

通过上述代码,我们成功将 HTML 文档解析为结构化的对象树,接下来可以进行标签查找、内容提取等操作。

3.1.2 Tag对象与NavigableString对象

BeautifulSoup 中的每个 HTML 标签都被解析为一个 Tag 对象,而标签内的文本则被解析为 NavigableString 对象。

示例代码:
# 获取第一个 <p> 标签
p_tag = soup.p
print("Tag对象:", p_tag)

# 获取第一个 <p> 标签的文本内容
p_text = soup.p.string
print("NavigableString对象:", p_text)

# 获取标签名
print("标签名:", p_tag.name)

# 获取标签属性
print("属性:", p_tag.attrs)
输出结果:
Tag对象: <p class="content">这是一个段落。</p>
NavigableString对象: 这是一个段落。
标签名: p
属性: {'class': ['content']}
说明:
  • Tag 对象可以嵌套访问,例如通过 soup.body.h1 可以访问 <body> 下的 <h1> 标签。
  • NavigableString 是标签内部的文本内容,可以直接访问。
  • .name 属性用于获取标签名称。
  • .attrs 返回标签的属性字典,注意其值是一个列表(如 class 属性可能包含多个类名)。

3.2 数据提取方法详解

BeautifulSoup 提供了多种方法来提取数据,包括按标签名查找、按属性过滤、按文本内容匹配等。

3.2.1 标签查找与属性过滤

1. 查找单个标签: find()
# 查找第一个 <h1> 标签
h1_tag = soup.find('h1')
print(h1_tag.text)
2. 查找所有匹配标签: find_all()
# 查找所有 <p> 标签
p_tags = soup.find_all('p')
for p in p_tags:
    print(p.text)
3. 按属性查找
# 查找 class 为 "footer" 的 <p> 标签
footer_p = soup.find('p', class_='footer')
print(footer_p.text)

注意:由于 class 是 Python 关键字,因此在查找时应使用 class_ 参数。

3.2.2 文本匹配与多条件查询

1. 按文本内容匹配
# 查找文本内容为 "这是一个段落" 的 <p> 标签
text_p = soup.find('p', text='这是一个段落。')
print(text_p)
2. 多条件组合查询
# 查找同时满足 class="content" 且文本内容为 "这是一个段落。" 的 <p> 标签
multi_p = soup.find('p', {'class': 'content'}, text='这是一个段落。')
print(multi_p)

3.3 网页解析的高级技巧

3.3.1 处理嵌套结构与多层遍历

HTML 文档通常具有多层嵌套结构,BeautifulSoup 提供了多种方法来处理这种结构:

示例 HTML:
<div class="container">
    <ul>
        <li>项目1</li>
        <li>项目2</li>
        <li>项目3</li>
    </ul>
</div>
遍历子节点:
# 获取 <div> 标签下的所有子节点
div = soup.find('div', class_='container')
for child in div.children:
    print(child.name)  # 输出 ul
遍历所有后代节点:
for desc in div.descendants:
    print(desc.name)  # 输出 ul, li, li, li
父节点访问:
# 获取某个 <li> 的父节点
li = soup.find('li')
print(li.parent.name)  # 输出 ul

3.3.2 结合正则表达式进行灵活匹配

当标签名或属性值具有某种模式时,可以结合 re 模块进行灵活匹配。

示例:
import re

# 匹配所有 class 属性包含 "item" 的标签
items = soup.find_all(class_=re.compile("item"))
for item in items:
    print(item.text)
流程图:正则表达式匹配流程
graph TD
    A[HTML文档] --> B{是否有匹配正则的class属性?}
    B -->|是| C[提取该标签]
    B -->|否| D[跳过该标签]

3.4 实战:解析目标网页结构

在实际项目中,我们通常需要解析真实的网页结构并提取特定信息。以下是一个解析示例网页的实战过程。

3.4.1 分析网页DOM结构

假设我们想从 https://example.com 页面中提取所有文章标题。

步骤如下:
  1. 使用 Requests 获取网页内容。
  2. 使用 BeautifulSoup 解析 HTML。
  3. 定位文章标题所在的标签结构。
示例代码:
import requests
from bs4 import BeautifulSoup

url = 'https://example.com'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')

# 假设文章标题在 <h2 class="post-title"> 中
titles = soup.find_all('h2', class_='post-title')
for title in titles:
    print(title.text.strip())

3.4.2 提取指定节点内容

1. 提取链接(a标签的href属性)
links = soup.find_all('a')
for link in links:
    print(link.get('href'))
2. 提取图片链接(img标签的src属性)
images = soup.find_all('img')
for img in images:
    print(img.get('src'))
3. 提取表格数据(table标签)
table = soup.find('table')
rows = table.find_all('tr')
for row in rows:
    cols = row.find_all('td')
    for col in cols:
        print(col.text.strip(), end=' | ')
    print()

小结与延伸讨论

通过本章内容的学习,我们掌握了 BeautifulSoup 的核心功能、数据提取方法以及高级解析技巧,并通过实战案例了解了如何解析真实网页内容。在实际开发中,网页结构往往更加复杂,结合 XPath、正则表达式、以及 CSS 选择器(将在下一章介绍)可以实现更灵活的数据提取。

下一章我们将深入探讨 XPath 与 CSS 选择器 ,它们与 BeautifulSoup 配合使用,可以极大提升数据提取的效率与灵活性。

4. XPath与CSS选择器数据提取

在爬虫开发中,提取网页中所需的数据是关键步骤之一。为了实现高效、准确的数据提取,XPath 和 CSS 选择器是两种广泛使用的定位技术。XPath 是一种在 XML 和 HTML 文档中进行导航的语言,能够通过路径表达式来选取节点或节点集;而 CSS 选择器则源于网页样式表语言,通过标签、类名、ID 等属性来匹配 HTML 元素。本章将深入讲解这两种技术的使用方法、语法结构以及实战技巧,并通过实际案例演示如何结合 Requests 库构建完整的数据抓取流程。

4.1 XPath表达式入门

XPath 是一种用于在 XML 或 HTML 文档中导航并选择节点的语言。它通过路径表达式来定位文档中的特定元素,是网页解析中非常强大且灵活的工具。掌握 XPath 的基本语法是进行高效数据提取的前提。

4.1.1 节点选择与路径匹配

XPath 通过路径表达式来定位 HTML 或 XML 节点。基本语法如下:

  • / :从根节点开始选取;
  • // :从任意位置开始选取节点;
  • . :当前节点;
  • .. :父节点;
  • @ :选取属性。

例如,假设我们有如下 HTML 片段:

<div class="content">
    <ul>
        <li class="item">苹果</li>
        <li class="item">香蕉</li>
        <li class="item">橙子</li>
    </ul>
</div>

我们可以使用以下 XPath 表达式来提取数据:

表达式 说明
/div/ul/li 选取根节点下的 div 中 ul 下的所有 li 元素
//ul/li 选取文档中所有 ul 下的 li 元素
//li[@class='item'] 选取 class 为 item 的 li 元素
//li[@class='item']/text() 选取 li 元素的文本内容

4.1.2 使用XPath提取数据

在 Python 中,XPath 通常与 lxml scrapy 框架配合使用。下面是一个使用 lxml 和 XPath 提取数据的示例:

from lxml import html

# 示例HTML内容
html_content = '''
<div class="content">
    <ul>
        <li class="item">苹果</li>
        <li class="item">香蕉</li>
        <li class="item">橙子</li>
    </ul>
</div>

# 解析HTML
tree = html.fromstring(html_content)

# 使用XPath提取数据
items = tree.xpath('//li[@class="item"]/text()')

# 输出结果
print(items)  # ['苹果', '香蕉', '橙子']

代码解析:

  • html.fromstring() :将字符串格式的 HTML 内容转换为可操作的树形结构。
  • tree.xpath() :执行 XPath 表达式,返回匹配的结果列表。
  • //li[@class="item"]/text() :选取 class 为 item 的 li 元素的文本内容。
  • print(items) :输出提取到的数据列表。

此代码展示了如何使用 XPath 提取网页中的文本信息,适用于静态网页数据抓取。

4.2 CSS选择器的使用技巧

与 XPath 不同,CSS 选择器是一种用于样式匹配的语言,但在网页解析中也常被用于数据提取。CSS 选择器语法简洁,适合快速匹配 HTML 元素。

4.2.1 CSS语法基础

CSS 选择器的基本语法包括:

  • tag :匹配所有指定标签;
  • .class :匹配 class 为指定值的元素;
  • #id :匹配 id 为指定值的元素;
  • tag[attr=value] :匹配具有特定属性的元素;
  • tag1 > tag2 :匹配直接子元素;
  • tag1 tag2 :匹配所有后代元素。

例如,以下是一些 CSS 选择器的使用示例:

选择器 说明
li 所有 li 元素
.item class 为 item 的元素
#menu id 为 menu 的元素
li.item 同时具有 li 标签和 item 类的元素
ul > li ul 的直接子元素 li
div ul li div 下所有 ul 中的 li 元素
a[href="https://example.com"] 匹配 href 为指定链接的 a 标签

4.2.2 提取嵌套元素与属性值

在实际应用中,我们常常需要提取嵌套结构中的元素内容,或者获取特定属性的值。CSS 选择器提供了简洁的方式来实现这些需求。

from bs4 import BeautifulSoup

# 示例HTML内容
html_content = '''
<div class="content">
    <ul>
        <li class="item"><a href="/apple">苹果</a></li>
        <li class="item"><a href="/banana">香蕉</a></li>
        <li class="item"><a href="/orange">橙子</a></li>
    </ul>
</div>

# 解析HTML
soup = BeautifulSoup(html_content, 'html.parser')

# 提取所有a标签的文本和链接
links = soup.select('li.item a')

# 输出结果
for link in links:
    print(f"文本: {link.text}, 链接: {link['href']}")

代码解析:

  • soup.select('li.item a') :使用 CSS 选择器选取 class 为 item 的 li 元素下的 a 标签。
  • link.text :获取 a 标签的文本内容。
  • link['href'] :获取 a 标签的 href 属性值。
  • for link in links: :遍历所有匹配的 a 标签并输出其文本和链接。

该示例演示了如何使用 CSS 选择器提取嵌套结构中的文本和属性值,适用于 BeautifulSoup 等解析库。

4.3 XPath与CSS选择器对比

XPath 和 CSS 选择器各有优劣,适用于不同场景。理解它们的差异有助于我们在实际项目中做出合理选择。

4.3.1 适用场景与性能差异

特性 XPath CSS 选择器
表达能力 支持复杂路径和逻辑判断(如条件筛选、函数) 简洁易用,适合快速匹配
性能 通常较慢 通常更快
可读性 表达较长,可读性一般 表达简洁,可读性强
文档结构依赖性 强,依赖完整的文档结构 较弱,更灵活
支持函数与运算 支持如 contains() starts-with() 等函数 不支持函数,但可通过 Python 进行处理

适用场景建议:

  • XPath 更适合
  • 需要复杂逻辑判断(如包含、匹配、索引等);
  • 需要获取节点路径、父节点、兄弟节点等;
  • 在 Scrapy 框架中,XPath 是首选。

  • CSS 选择器 更适合

  • 快速提取简单的标签、类名、属性;
  • 代码简洁,易于维护;
  • 在 BeautifulSoup 中,CSS 选择器更常用。

4.3.2 综合使用策略

在实际项目中,XPath 和 CSS 选择器可以结合使用,发挥各自优势。例如,在解析结构复杂的网页时,先使用 XPath 定位到目标节点,再使用 CSS 选择器提取其中的子元素内容。

from lxml import html
from bs4 import BeautifulSoup

# 假设我们已获取HTML内容
html_content = '''
<div class="content">
    <ul>
        <li class="item"><a href="/apple">苹果</a></li>
        <li class="item"><a href="/banana">香蕉</a></li>
        <li class="item"><a href="/orange">橙子</a></li>
    </ul>
</div>

# 使用XPath定位到ul节点
tree = html.fromstring(html_content)
ul_node = tree.xpath('//div[@class="content"]/ul')[0]

# 将ul节点转换为字符串,再用BeautifulSoup解析
ul_html = html.tostring(ul_node, encoding='utf-8').decode('utf-8')
soup = BeautifulSoup(ul_html, 'html.parser')

# 使用CSS选择器提取每个li中的a标签
links = soup.select('li.item a')

for link in links:
    print(f"水果: {link.text}, 链接: {link['href']}")

代码解析:

  • tree.xpath('//div[@class="content"]/ul')[0] :使用 XPath 提取 div 下的 ul 节点;
  • html.tostring(ul_node) :将 XPath 提取的节点转换为 HTML 字符串;
  • BeautifulSoup(ul_html, 'html.parser') :将 HTML 字符串转换为 BeautifulSoup 对象;
  • soup.select('li.item a') :使用 CSS 选择器提取 a 标签内容;
  • link.text link['href'] :提取文本和链接。

这种混合使用方式结合了 XPath 的强大定位能力和 CSS 选择器的简洁表达,适用于结构复杂但需快速提取的场景。

4.4 实战:结合Requests与选择器提取数据

在实际开发中,我们需要将网页请求与数据提取结合起来。下面以抓取一个示例网站的标题和链接为例,展示如何使用 Requests 结合 XPath 或 CSS 选择器提取数据。

4.4.1 完整数据抓取流程设计

完整的数据抓取流程通常包括以下步骤:

  1. 发送 HTTP 请求获取网页内容
  2. 解析 HTML 文档
  3. 使用 XPath 或 CSS 选择器提取所需数据
  4. 数据清洗与结构化输出

以下是一个完整的流程示意图:

graph TD
    A[开始] --> B[发送HTTP请求]
    B --> C[获取HTML响应]
    C --> D{解析HTML}
    D --> E[使用XPath提取数据]
    D --> F[使用CSS选择器提取数据]
    E --> G[清洗与结构化输出]
    F --> G
    G --> H[结束]

4.4.2 数据清洗与结构化输出

我们以爬取一个假想的水果网站为例,演示如何提取标题和链接:

import requests
from lxml import html

# 发送请求
url = "https://example.com/fruits"
response = requests.get(url)

# 解析HTML
tree = html.fromstring(response.content)

# 使用XPath提取数据
titles = tree.xpath('//div[@class="fruit-list"]/ul/li/a/text()')
links = tree.xpath('//div[@class="fruit-list"]/ul/li/a/@href')

# 数据清洗与结构化输出
fruits = [{"name": title, "url": link} for title, link in zip(titles, links)]

# 打印结果
for fruit in fruits:
    print(fruit)

代码解析:

  • requests.get(url) :发送 HTTP 请求获取网页内容;
  • html.fromstring(response.content) :将响应内容解析为 HTML 树;
  • tree.xpath('//div[@class="fruit-list"]/ul/li/a/text()') :提取水果名称;
  • tree.xpath('//div[@class="fruit-list"]/ul/li/a/@href') :提取水果链接;
  • zip(titles, links) :将标题和链接一一对应;
  • [{"name": title, "url": link} for ...] :构造结构化数据;
  • print(fruit) :输出结构化数据。

该代码展示了从请求、解析到提取、清洗的完整数据抓取流程,适用于大多数静态网页的爬取任务。

本章深入讲解了 XPath 与 CSS 选择器的基本语法、使用技巧与实战应用,并通过代码示例展示了如何结合 Requests 库实现完整的网页数据提取流程。这两种技术各有优势,合理选择将极大提升爬虫开发的效率与准确性。

5. User-Agent伪装与代理IP配置

在现代网络爬虫实践中,反爬虫机制的复杂性和多样性使得爬虫程序必须具备伪装身份和动态切换IP地址的能力。User-Agent 和代理IP是两个关键的伪装工具。User-Agent用于模拟浏览器标识,而代理IP则用于绕过IP封禁和访问限制。本章将深入探讨这两者的原理、配置方式以及如何结合使用,构建一个稳定且抗封的请求环境。

5.1 爬虫身份识别机制

现代网站通常会通过多种手段来识别和限制爬虫行为,其中最常见的方式就是通过 User-Agent 和 IP 地址进行判断。

5.1.1 User-Agent的作用与常见格式

User-Agent(UA) 是 HTTP 请求头的一部分,用于告知服务器当前请求的客户端类型、操作系统、浏览器版本等信息。服务器通过解析 User-Agent 来判断是否是浏览器发出的请求,从而识别是否为爬虫。

常见浏览器 User-Agent 示例:
浏览器 User-Agent 字符串
Chrome 114(Windows) Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Firefox 110(Mac) Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:110.0) Gecko/20100101 Firefox/110.0
Safari 16(iOS) Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1
默认的 Requests User-Agent:

Requests 库默认使用的 User-Agent 是:

python-requests/2.28.1

这个 UA 很容易被识别为爬虫,因此需要手动设置一个更像浏览器的 UA。

5.1.2 IP封禁机制与反爬策略

除了 User-Agent,服务器还会通过 IP 地址来识别爬虫行为。常见的反爬策略包括:

  • 访问频率限制 :单位时间内请求次数超过阈值则封禁。
  • IP黑名单 :已知的爬虫 IP 地址被直接封禁。
  • 行为分析 :通过分析访问路径、点击行为等判断是否为真人。
  • 验证码挑战 :触发频率限制后弹出验证码验证。
  • JavaScript渲染检测 :检查是否能执行 JS,从而判断是否为真实浏览器。

因此,使用代理 IP 成为绕过这些限制的关键策略。

5.2 User-Agent伪装技巧

为了模拟浏览器访问,爬虫程序需要动态切换 User-Agent。

5.2.1 构造随机User-Agent

可以通过维护一个 User-Agent 列表,每次请求时随机选取一个。

import random
import requests

user_agents = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:110.0) Gecko/20100101 Firefox/110.0",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1"
]

headers = {
    "User-Agent": random.choice(user_agents)
}

response = requests.get("https://httpbin.org/get", headers=headers)
print(response.text)
代码逻辑分析:
  1. 定义一个包含多个浏览器 UA 的列表 user_agents
  2. 使用 random.choice() 随机选择一个 UA。
  3. 构建请求头 headers ,将 UA 放入其中。
  4. 使用 requests.get() 发送请求,并传入自定义请求头。
参数说明:
  • random.choice() :从列表中随机选取一个元素。
  • headers :HTTP 请求头,可设置多个字段如 Accept、Referer 等。

5.2.2 使用fake-useragent库自动切换

为了更方便地获取真实 UA,可以使用 fake-useragent 库,它会自动从网上抓取最新的 User-Agent 数据。

pip install fake-useragent
from fake_useragent import UserAgent
import requests

ua = UserAgent()
headers = {
    "User-Agent": ua.random
}

response = requests.get("https://httpbin.org/get", headers=headers)
print(response.text)
代码逻辑分析:
  1. 创建 UserAgent() 实例。
  2. 调用 ua.random 获取一个随机的 User-Agent。
  3. 构建请求头并发送请求。
参数说明:
  • ua.random :返回一个随机的 User-Agent。
  • ua.chrome ua.firefox 等:可以指定特定浏览器的 UA。

5.3 代理IP的应用与配置

5.3.1 代理IP类型与获取方式

代理 IP 可分为以下几类:

类型 描述 匿名性 速度
高匿名代理 完全隐藏原始 IP,不传递任何代理信息 一般
匿名代理 伪装 IP,但可能暴露代理特征 中等 中等
透明代理 仅转发请求,不隐藏原始 IP
HTTPS 代理 支持加密通信
获取方式:
  • 免费代理网站 :如快代理、西刺代理等,但质量不稳定。
  • 付费代理服务 :如芝麻代理、讯代理、快代理高级版等,稳定性高。
  • 自建代理池 :使用爬虫定期抓取免费代理并测试可用性。

5.3.2 在Requests中配置代理

Requests 支持通过 proxies 参数配置代理服务器。

import requests

proxies = {
    "http": "http://138.68.60.8:8080",
    "https": "http://138.68.60.8:8080"
}

response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.text)
代码逻辑分析:
  1. 定义 proxies 字典,分别指定 HTTP 和 HTTPS 使用的代理地址。
  2. 使用 requests.get() 并传入 proxies 参数。
  3. 请求 httpbin.org/ip 以查看当前请求使用的 IP。
参数说明:
  • proxies :字典格式,键为协议类型,值为代理地址。
  • 如果代理需要认证,格式为: "http://username:password@ip:port"

5.4 实战:构建稳定的请求环境

在实际项目中,单一的 User-Agent 和固定代理 IP 很容易被封禁。构建一个稳定的请求环境,需要实现多 User-Agent 切换、代理 IP 池管理以及异常重试机制。

5.4.1 多User-Agent与代理池设计

我们可以将 User-Agent 和代理 IP 分别维护为列表,并在每次请求时随机选择。

import requests
import random
from fake_useragent import UserAgent

# User-Agent 列表
def get_random_ua():
    ua = UserAgent()
    return {"User-Agent": ua.random}

# 代理 IP 列表(可定期更新)
proxies_list = [
    {"http": "http://138.68.60.8:8080"},
    {"http": "http://45.77.136.205:3128"},
    {"http": "http://173.212.235.75:80"}
]

def fetch(url):
    headers = get_random_ua()
    proxy = random.choice(proxies_list)
    try:
        response = requests.get(url, headers=headers, proxies=proxy, timeout=10)
        if response.status_code == 200:
            return response.text
        else:
            print(f"Status Code: {response.status_code}, Retrying...")
            return fetch(url)
    except Exception as e:
        print(f"Error: {e}, Switching Proxy and UA...")
        return fetch(url)
代码逻辑分析:
  1. get_random_ua() :使用 fake-useragent 获取随机 User-Agent。
  2. proxies_list :维护多个代理 IP。
  3. fetch(url) :封装请求函数,实现 UA 与代理的随机切换。
  4. 异常处理中递归调用自身,实现自动重试。

5.4.2 动态更换机制与异常处理

为了增强程序的健壮性,可以引入以下机制:

  • 失败重试机制 :当请求失败时尝试切换代理和 UA。
  • 代理可用性检测 :每次使用前先测试代理是否有效。
  • 自动更新代理池 :定时抓取新的代理地址。
graph TD
    A[开始请求] --> B{代理是否可用?}
    B -->|是| C[发送请求]
    B -->|否| D[更换代理]
    C --> E{响应是否成功?}
    E -->|是| F[返回数据]
    E -->|否| G[切换 UA 和代理]
    G --> C
表格:异常处理策略
异常类型 处理方式
TimeoutError 增加超时时间或更换代理
ConnectionError 更换代理或重试
ProxyError 移除该代理并换新
HTTPError (4xx/5xx) 修改请求参数或重试

通过本章的学习,我们掌握了 User-Agent 的伪装技巧、代理 IP 的配置方法,并结合实际案例设计了一个具备自动切换 UA 和代理的爬虫请求模块。这些技能对于构建一个稳定、高效、抗封的爬虫系统至关重要,为后续的高阶爬虫开发打下坚实基础。

6. 搜索引擎关键词数据采集实战与道德规范

6.1 百度相关词抓取目标分析

百度作为中国最大的搜索引擎,其“相关搜索词”功能在SEO优化和关键词挖掘中具有重要价值。本节将通过分析百度搜索页面的结构与请求参数,掌握如何构建一个高效的关键词采集系统。

6.1.1 页面结构与请求参数解析

百度搜索页面的URL结构如下:

https://www.baidu.com/s?wd=关键词

其中 wd 参数表示搜索关键词。我们可以通过抓包工具(如 Chrome DevTools)观察请求头与返回数据,发现相关词信息通常嵌入在 HTML 页面的特定 DOM 节点中。

例如,相关词部分的 HTML 片段可能如下所示:

<div id="rs">
  <table>
    <tr><td><a href="/s?wd=关键词1">关键词1</a></td></tr>
    <tr><td><a href="/s?wd=关键词2">关键词2</a></td></tr>
    ...
  </table>
</div>

6.1.2 数据接口识别与模拟请求

虽然百度没有公开的 API 接口提供相关词数据,但我们可以通过模拟请求 + 页面解析的方式获取。使用 Requests + BeautifulSoup 是较为常见的方式。

示例代码如下:

import requests
from bs4 import BeautifulSoup

def fetch_related_keywords(keyword):
    url = "https://www.baidu.com/s"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36'
    }
    params = {
        'wd': keyword
    }
    response = requests.get(url, headers=headers, params=params)
    if response.status_code == 200:
        soup = BeautifulSoup(response.text, 'html.parser')
        related_div = soup.find('div', {'id': 'rs'})
        if related_div:
            links = related_div.find_all('a')
            related_keywords = [link.text for link in links]
            return related_keywords
    return []

# 示例调用
keywords = fetch_related_keywords("人工智能")
print(keywords)

代码说明:

  • 使用 requests.get() 发起带参数的 GET 请求。
  • 设置 User-Agent 模拟浏览器访问,避免被识别为爬虫。
  • 使用 BeautifulSoup 解析 HTML,定位相关词区域并提取链接文本。

6.2 完整采集流程设计

6.2.1 抓取策略与数据存储设计

为了高效采集大量关键词的相关词数据,需设计合理的抓取策略和存储机制:

  1. 关键词队列管理: 使用队列结构(如 Python 的 queue.Queue )管理待采集关键词,支持多线程消费。
  2. 数据去重机制: 使用集合( set )记录已采集过的关键词,避免重复抓取。
  3. 持久化存储: 采集到的数据可保存为 CSV、JSON 或数据库(如 SQLite、MongoDB)中。

6.2.2 多线程与异步请求实现

为提高采集效率,可以使用 Python 的 concurrent.futures.ThreadPoolExecutor 实现多线程采集:

from concurrent.futures import ThreadPoolExecutor
import csv

keyword_list = ["人工智能", "大数据", "区块链", "机器学习", "云计算"]
collected_data = []

def worker(keyword):
    related = fetch_related_keywords(keyword)
    collected_data.extend([(keyword, k) for k in related])

with ThreadPoolExecutor(max_workers=5) as executor:
    executor.map(worker, keyword_list)

# 保存为CSV
with open('baidu_related_keywords.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['source_keyword', 'related_keyword'])
    writer.writerows(collected_data)

代码说明:

  • 使用线程池并发执行多个关键词采集任务。
  • 采集结果统一写入 CSV 文件,便于后续分析。

6.3 反爬虫策略应对方案

6.3.1 验证码识别与模拟登录

某些搜索引擎在频繁访问时会触发验证码(如 Google、百度部分接口),可使用以下策略应对:

  • OCR识别: 使用第三方 OCR 服务(如云智搜、打码平台)识别验证码。
  • 模拟登录: 若需登录才能访问目标数据,可使用 Selenium 模拟浏览器登录过程。

6.3.2 Selenium 与 Headless 浏览器使用

Selenium 可以操控真实浏览器(或无头模式),适用于复杂页面或需要 JavaScript 渲染的场景。

示例代码(使用无头模式打开百度搜索页):

from selenium import webdriver
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/s?wd=人工智能")

# 获取相关词区域
related_div = driver.find_element_by_id("rs")
related_links = related_div.find_elements_by_tag_name("a")
related_keywords = [link.text for link in related_links]

print(related_keywords)
driver.quit()

代码说明:

  • 使用 Chrome 无头模式,避免弹出浏览器窗口。
  • 通过 Selenium 定位 DOM 元素,获取相关词。

6.4 爬虫道德与法律规范

6.4.1 robots.txt 协议解读

robots.txt 是网站根目录下的一个文件,用于告知爬虫哪些页面可以抓取,哪些禁止访问。例如百度的 robots.txt 文件:

User-agent: *
Disallow: /baidu
Disallow: /index.php
Disallow: /shifen/
  • User-agent: * 表示对所有爬虫生效。
  • Disallow 指定禁止抓取的路径。

注意: 遵守 robots.txt 是爬虫的基本道德规范,但不具有法律强制力。

6.4.2 合理采集与数据使用边界

  • 频率控制: 避免高频访问,设置合理的请求间隔(如每秒 1~2 次)。
  • 数据用途: 不得用于非法用途,如侵犯用户隐私、商业恶意竞争等。
  • 法律责任: 根据《中华人民共和国网络安全法》和《数据安全法》,非法获取、使用他人数据将承担法律责任。

提示: 建议在采集前与目标网站进行沟通,获取合法授权,或使用其开放 API 接口。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本项目是一个基于Python3的网络爬虫脚本,旨在抓取百度等网站的相关关键词数据,并成功绕过常见的安全验证机制。该脚本使用了如代理IP、动态User-Agent、HTML解析、延时策略等技术,具备良好的反爬应对能力。适用于学习网络请求、网页解析、数据提取等爬虫核心技术,并可作为实践案例应用于搜索引擎数据分析领域。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐