杰瑞科技汇

Python爬遇URL中文乱码,怎么解决?

下面我将详细解释原因,并提供几种常见的解决方案和最佳实践。

Python爬遇URL中文乱码,怎么解决?-图1
(图片来源网络,侵删)

问题根源:URL 的编码与解码

为了在互联网上安全地传输,URL 中的非 ASCII 字符(如中文)需要进行编码,URL 编码使用 加上两位十六进制数来表示一个特殊字符或一个非 ASCII 字符。

  • 你好世界 会被编码为 %E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
  • 空格 会被编码为 %20

乱码通常发生在以下两种情况:

  1. 二次编码(Over-encoding):你拿到一个已经编码的 URL(%E4%BD%A0%E5%A5%BD),但你误以为它是未编码的字符串,然后又对它进行了一次 URL 编码,这会导致它变成 %25E4%25BD%25A0...,解码时会得到乱码。
  2. 未解码:你拿到一个已经编码的 URL,但直接使用了它,当你用这个 URL 发起请求时,requestsurllib 库有时会自动处理,但有时不会,导致服务器无法正确识别路径,返回 404 或错误页面。

解决方案

我们将从最推荐、最稳健的方法开始,逐步介绍。

使用 requests 库(推荐)

requests 库非常智能,它能很好地处理 URL,最佳实践是将完整的、包含中文的 URL 作为一个字符串直接传递给 requests.get()

Python爬遇URL中文乱码,怎么解决?-图2
(图片来源网络,侵删)

关键点:

  • 不要手动对 URL 进行编码requests 会自动处理。
  • URL 来自网页源码,需要先清理(比如去掉周围的引号)。
  • URL 包含查询参数,最好使用 params 参数,让 requests 自动为你处理好编码。

示例 1:直接使用完整 URL

假设你从一个地方获取到了这个 URL 字符串: url_str = "https://www.example.com/search?q=你好世界"

直接使用它即可,requests 会自动编码查询参数。

Python爬遇URL中文乱码,怎么解决?-图3
(图片来源网络,侵删)
import requests
# 这个字符串是已经编码过的,或者本身就是包含中文的
url_str = "https://www.example.com/search?q=你好世界"
# 直接传递给 requests,它会自动处理编码
try:
    response = requests.get(url_str)
    response.raise_for_status()  # 如果请求失败 (状态码不是 200), 则抛出异常
    print("请求成功!")
    print("URL 实际发送的:", response.url) # 你会看到这里的 q 参数已经被编码了
    # print(response.text)
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

输出:

请求成功!
URL 实际发送的: https://www.example.com/search?q=%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C

可以看到,requests 自动将 你好世界 编码了,response.url 属性显示了最终发送到服务器的正确 URL。

示例 2:使用 params 参数(更佳实践)

当 URL 由基础 URL 和查询参数组成时,使用 params 是最清晰、最不容易出错的方式。

import requests
base_url = "https://www.example.com/search"
params = {
    'q': '你好世界',
    'page': 1
}
# requests 会自动对字典中的键值对进行 URL 编码,并拼接成正确的 URL
try:
    response = requests.get(base_url, params=params)
    response.raise_for_status()
    print("请求成功!")
    print("URL 实际发送的:", response.url)
    # print(response.text)
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

这种方法的好处是代码更易读,且能避免手动拼接字符串时可能出现的错误。


使用 urllib.parse 库(标准库)

如果你不想用 requests,或者需要更底层的 URL 处理,Python 标准库 urllib.parse 是你的不二之选。

核心函数:

  • urllib.parse.quote(string, safe='/'):将字符串中的特殊字符进行 URL 编码。safe 参数指定哪些字符不需要被编码(比如路径中的 )。
  • urllib.parse.unquote(string):将 URL 编码的字符串解码回原始字符串。

示例:手动编码(不推荐,但有助于理解)

假设你有一个未编码的字符串,需要手动构建 URL。

from urllib.parse import quote
query = "你好世界"
# 对查询参数进行编码,空格等字符会被处理
encoded_query = quote(query)
print(f"编码后的查询字符串: {encoded_query}") # 输出: %E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
url = f"https://www.example.com/search?q={encoded_query}"
print(f"URL: {url}")
# 如果有一个已经编码的 URL,想解码它
encoded_url = "https://www.example.com/search?q=%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C"
decoded_url = unquote(encoded_url)
print(f"解码后的 URL: {decoded_url}")

什么时候需要手动编码? 当你需要拼接的 URL 结构非常复杂,或者 requestsparams 参数不适用时,但即便如此,也推荐优先使用 requests


处理从 HTML/JS 中提取的 URL

这是最常见的问题场景,你用 BeautifulSoup 或正则表达式从 HTML 的 hrefsrc 属性中提取出一个 URL,但它可能被引号包围,或者已经是编码过的。

示例:从 HTML 属性中提取 URL

<!-- 假设这是网页源码中的一行 -->
<a href="https://www.example.com/搜索?q=你好世界">点击这里</a>

用 BeautifulSoup 提取后,你需要先处理这个字符串。

from bs4 import BeautifulSoup
import requests
html = """
<a href="https://www.example.com/搜索?q=你好世界">点击这里</a>
<a href='https://www.example.com/路径/测试'>另一个链接</a>
"""
soup = BeautifulSoup(html, 'html.parser')
# 找到所有 a 标签
for a_tag in soup.find_all('a'):
    raw_url = a_tag['href']
    print(f"从 HTML 提取的原始 URL: '{raw_url}'")
    # 直接使用 requests,它会处理编码
    try:
        response = requests.get(raw_url)
        response.raise_for_status()
        print(f"  -> 请求成功! URL: {response.url}\n")
    except requests.exceptions.RequestException as e:
        print(f"  -> 请求失败: {e}\n")

在这个例子中,即使 href 属性里的值是中文,requests.get() 也能正确处理,但为了代码的健壮性,最好养成一个习惯:对提取出的 URL 进行一次 unquote,然后再判断是否需要重新编码

更稳健的处理方式:

from urllib.parse import unquote, quote
import requests
raw_url = "https://www.example.com/搜索?q=你好世界"
# 1. 先解码,确保我们拿到的是原始字符串
#    这一步可以防止二次编码问题,如果原始URL已经是未编码的,unquote不会改变它。
decoded_url = unquote(raw_url)
print(f"解码后的 URL: {decoded_url}")
# 2. 将解码后的 URL 作为最终 URL 发起请求
#    requests 会自动处理其中可能存在的特殊字符
try:
    response = requests.get(decoded_url)
    response.raise_for_status()
    print(f"请求成功! URL: {response.url}")
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

总结与最佳实践

  1. 首选 requests:它极大地简化了 URL 处理,是爬虫开发的利器。
  2. 善用 requests.get(url, params=your_dict):这是传递查询参数最安全、最清晰的方式,能自动处理所有编码问题。
  3. 不要手动编码:除非你有特殊需求,否则不要轻易使用 urllib.parse.quote,让 requestsurllib 的标准函数来处理。
  4. 处理外部输入时保持警惕:当你从 HTML、API 或其他来源获取 URL 时,先用 urllib.parse.unquote() 解码一次,可以有效地防止“二次编码”导致的乱码问题。
  5. 检查最终 URL:在发送请求后,打印 response.url 来查看最终发送给服务器的 URL,这能帮你快速定位是不是 URL 编码的问题。

遵循这些原则,你就能轻松解决 99% 的 Python 爬虫 URL 中文乱码问题。

分享:
扫描分享到社交APP
上一篇
下一篇