--- title: urllib URL 处理模块 description: urllib URL 处理模块 keywords: - python - urllib tags: - FormalSciences/ComputerScience - ProgrammingLanguage/Python - Python/Libraires author: 7Wate date: 2023-08-29 --- ## 概述 `urllib` 是 Python 标准库中用于处理 URL(统一资源定位符)相关操作的模块,它提供了多个子模块,用于执行网络请求、解析 URL、处理错误以及解析 robots.txt 文件等。以下是 `urllib` 的子模块: ### 子模块 - **`urllib.request`**:提供打开和读取 URL 的功能。支持多种网络协议,如 HTTP、FTP 等。 - **`urllib.error`**:包含与网络请求相关的异常类,用于处理错误和异常情况。 - **`urllib.parse`**:用于解析和构建 URL,提供各种操作,如分割、组合、编码和解码。 - **`urllib.robotparser`**:用于解析网站的 `robots.txt` 文件,确定哪些页面可以被爬取。 ### 优点 - **内置模块**:作为 Python 标准库的一部分,无需单独安装。 - **全面功能**:支持多种网络协议和操作,适用于多种网络操作需求。 - **高度可定制**:用于处理 URL 的多个方面,如打开、读取、解析等。 ### 缺点 - **较低层次的 API**:与一些第三方库相比(如 `requests`),`urllib` 的 API 较为底层,可能需要编写更多的代码。 - **繁琐的错误处理**:错误处理需要额外的代码,相比使用像 `requests` 这样的库可能更复杂。 ### 同类产品对比 | 产品 | 优点 | 缺点 | 适用背景 | 社区支持 | | -------- | ------------ | ------------ | ------------------ | ----------- | | urllib | 标准库,全面 | API 较底层 | 网络请求,URL 操作 | Python 社区 | | requests | API 简单 | 需要单独安装 | HTTP 请求 | Python 社区 | | httplib2 | 功能丰富 | 使用复杂 | HTTP 请求 | Python 社区 | ## `urllib.request` | 方法 | 功能描述 | 示例 | | ------------------------ | ------------------------------------ | ------------------------------------------------------------ | | `urlopen()` | 打开并读取一个 URL 的内容 | `urllib.request.urlopen(url)` | | `urlretrieve()` | 将 URL 指向的文件下载到本地 | `urllib.request.urlretrieve(url, filename)` | | `build_opener()` | 构建一个可自定义的 `Opener` 对象 | `opener = urllib.request.build_opener()` | | `install_opener()` | 安装全局的 `Opener` | `urllib.request.install_opener(opener)` | | `HTTPBasicAuthHandler()` | HTTP 基础认证处理程序 | `handler = urllib.request.HTTPBasicAuthHandler()` | | `HTTPCookieProcessor()` | 用于处理 HTTP cookies | `handler = urllib.request.HTTPCookieProcessor()` | | `ProxyHandler()` | 设置代理 | `proxy = urllib.request.ProxyHandler({'http': 'http://www.example.com:8080'})` | | `Request()` | 创建一个请求对象,用于定制 HTTP 头等 | `req = urllib.request.Request(url, headers={...})` | ### `urlopen()` 打开 URL ```python import urllib.request # 打开一个网页 response = urllib.request.urlopen('http://www.example.com') # 读取网页内容 data = response.read() # 输出网页内容 print(data) ``` ### `urlretrieve()` 下载文件 ```python import urllib.request # 从指定 URL 下载文件,并保存到本地 urllib.request.urlretrieve('http://www.example.com/file.txt', 'local_file.txt') ``` ### `build_opener()` 和 `install_opener()` `build_opener()` 传递一系列处理程序(handlers),这些处理程序用于定义如何处理各种 HTTP 功能,比如重定向、基础认证、cookies 等。一旦你使用 `build_opener()` 创建了一个 `Opener` 对象,你可以使用 `install_opener()` 来设置它作为默认的 `Opener`。 ```python import urllib.request # 创建基础认证处理程序 auth_handler = urllib.request.HTTPBasicAuthHandler() auth_handler.add_password('realm', 'host', 'username', 'password') # 创建代理处理程序 proxy_handler = urllib.request.ProxyHandler({'http': 'http://www.proxy.com:8080'}) # 创建 Opener opener = urllib.request.build_opener(auth_handler, proxy_handler) # 安装 Opener urllib.request.install_opener(opener) # 使用 urlopen() 方法,这样会应用我们之前设置的所有处理程序 response = urllib.request.urlopen('http://www.example.com') ``` ### HTTP 基础认证 (`HTTPBasicAuthHandler`) ```python import urllib.request # 创建一个 HTTPBasicAuthHandler 对象 auth_handler = urllib.request.HTTPBasicAuthHandler() # 添加认证信息 auth_handler.add_password('realm', 'host', 'username', 'password') # 创建并安装 opener opener = urllib.request.build_opener(auth_handler) urllib.request.install_opener(opener) ``` ### `HTTPCookieProcessor` 处理 Cookies ```python import urllib.request import http.cookiejar # 创建一个 CookieJar 对象 cookie_jar = http.cookiejar.CookieJar() # 创建一个 HTTPCookieProcessor 对象 cookie_handler = urllib.request.HTTPCookieProcessor(cookie_jar) # 构建和安装 opener opener = urllib.request.build_opener(cookie_handler) ``` ### `ProxyHandler` 设置代理 ```python import urllib.request # 创建一个 ProxyHandler 对象 proxy_handler = urllib.request.ProxyHandler({'http': 'http://www.proxy.com:8080'}) # 构建并安装 opener opener = urllib.request.build_opener(proxy_handler) ``` ### `Request()` 自定义请求 ```python import urllib.request # 创建一个 Request 对象 req = urllib.request.Request(url='http://www.example.com', headers={'User-Agent': 'MyApp/1.0'}) # 使用 urlopen 打开自定义的请求 response = urllib.request.urlopen(req) ``` ## `urllib.error` | 方法 | 功能描述 | | ---------------------- | ------------------------------------- | | `URLError` | 所有 `urllib` 产生的异常的基类 | | `HTTPError` | 处理 HTTP 错误状态,继承自 `URLError` | | `ContentTooShortError` | 在下载过程中,数据不足时抛出的异常 | ### `URLError` 当使用 `urllib.request` 打开一个 URL 失败时,通常会抛出 `URLError` 异常。 ```python import urllib.request import urllib.error try: response = urllib.request.urlopen('http://www.nonexistentwebsite.com') except urllib.error.URLError as e: print(e.reason) ``` ### `HTTPError` 当服务器返回 HTTP 错误状态码(如 404、500 等)时,会抛出 `HTTPError`。 ```python import urllib.request import urllib.error try: response = urllib.request.urlopen('http://www.example.com/404') except urllib.error.HTTPError as e: print(f'HTTP Error Code: {e.code}') print(f'Reason: {e.reason}') ``` ### `ContentTooShortError` 如果使用 `urlretrieve()` 函数,但获取的数据长度与 `Content-Length` 头中声明的长度不匹配时,会抛出 `ContentTooShortError`。 ```python import urllib.request import urllib.error try: urllib.request.urlretrieve('http://www.example.com/file', 'local_file.txt') except urllib.error.ContentTooShortError as e: print('The downloaded data is less than expected.') ``` ## `urllib.parse` | 方法 | 功能描述 | 示例 | | -------------- | ------------------------------------ | --------------------------------------- | | `urlparse()` | 解析 URL,返回一个 ParseResult 对象 | `urllib.parse.urlparse(url)` | | `urlunparse()` | 将 ParseResult 对象转回 URL | `urllib.parse.urlunparse(parse_result)` | | `urlsplit()` | 类似于 `urlparse()`,但不分割 params | `urllib.parse.urlsplit(url)` | | `urlunsplit()` | 将由 `urlsplit()` 返回的对象转回 URL | `urllib.parse.urlunsplit(split_result)` | | `urljoin()` | 合并两个 URL | `urllib.parse.urljoin(base, url)` | | `urlencode()` | 将字典或序列转换为 URL 查询字符串 | `urllib.parse.urlencode(query_dict)` | | `quote()` | 将字符串进行 URL 编码 | `urllib.parse.quote(string)` | | `unquote()` | 对 URL 编码的字符串进行解码 | `urllib.parse.unquote(encoded_string)` | ### 解析和构建 URL ```python from urllib.parse import urlparse, urlunparse, urlsplit, urlunsplit, urljoin # 解析URL并返回ParseResult对象 parsed_url = urlparse('http://www.example.com/path?query=arg') # 将ParseResult对象转换回URL new_url = urlunparse(parsed_url) # 类似于urlparse(),但不分割params split_result = urlsplit('http://www.example.com/path?query=arg') # 将由urlsplit()返回的对象转换回URL original_url = urlunsplit(split_result) # 合并两个URL new_url = urljoin('http://www.example.com/path/', '/anotherpath.html') ``` ### 转换查询字符串 ```python from urllib.parse import urlencode # 将字典或序列转换为URL查询字符串 query_dict = {'key1': 'value1', 'key2': 'value2'} query_string = urlencode(query_dict) ``` ### URL 编码和解码 ```python from urllib.parse import quote, unquote # 将字符串进行URL编码 encoded = quote('a string with / and ?') # 对URL编码的字符串进行解码 decoded = unquote(encoded) ``` ## `urllib.robotparser` 通过使用 `urllib.robotparser`,你可以确保你的网络爬虫**尊重网站的抓取策略,这是一种负责任的爬虫行为。** | 方法 | 功能描述 | 示例 | | ------------------- | ------------------------------------------------------ | -------------------------------------------------- | | `RobotFileParser()` | 创建一个 `RobotFileParser` 对象 | `rp = urllib.robotparser.RobotFileParser()` | | `set_url()` | 设置 `robots.txt` 文件的 URL | `rp.set_url('http://www.example.com/robots.txt')` | | `read()` | 从设置的 URL 读取 `robots.txt` 文件 | `rp.read()` | | `parse()` | 用于手动解析 `robots.txt` 文件的行 | `rp.parse(robots_txt_body.split("\n"))` | | `can_fetch()` | 检查指定的 User-Agent 是否可以访问某个路径 | `rp.can_fetch('*', 'http://www.example.com/page')` | | `mtime()` | 获取最后一次获取 `robots.txt` 文件的时间(Unix 时间戳) | `rp.mtime()` | | `modified()` | 设置最后一次获取 `robots.txt` 文件的时间 | `rp.modified()` | ### 创建和设置 RobotFileParser 首先,你需要创建一个 `RobotFileParser` 对象,并设置要解析的 `robots.txt` 文件的 URL。 ```python import urllib.robotparser # 创建 RobotFileParser 对象 rp = urllib.robotparser.RobotFileParser() # 设置 robots.txt 文件的 URL rp.set_url('http://www.example.com/robots.txt') # 从 URL 读取 robots.txt 文件 rp.read() ``` ### 检查爬虫是否可以访问特定页面 使用 `can_fetch()` 方法,您可以检查指定的 User-Agent 是否被允许抓取特定的网页路径。 ```python # 检查 '*'(所有 User-Agents)是否允许访问 '/page' allowed = rp.can_fetch('*', 'http://www.example.com/page') if allowed: print("I can crawl this page.") else: print("I cannot crawl this page.") ``` ### 手动解析 robots.txt 如果你需要手动解析 `robots.txt` 文件的内容,可以使用 `parse()` 方法。 ```python # 假设 robots_txt_body 包含了 robots.txt 的文本内容 robots_txt_body = ''' User-agent: * Disallow: /private/ ''' # 手动解析这些规则 rp.parse(robots_txt_body.split("\n")) ```