Python入门学习三

一 异步编程

1. 协程

协程不是计算机提供的,是人员创造的;也可以称为微线程,是一种用户态上下文切换技术。简而言之,就是通过一个县程序实现代码相互切换执行。人为控制在一个函数之间来回切换

python 复制代码
def func1():
    print(1)
    ...
    print(2)

def func2():
    print(3)
    ...
    pirnt(4)
   

func1()
func2()

实现协程有以下几种方式

  • greenlet,早期模块
  • yield关键字
  • asyncio装饰器(py3.4)
  • async, await关键字(py3.5)

2.协程的实现方式

2.1 greenlet

python 复制代码
pip/pip3 install greenlet

实现

python 复制代码
form greenlet import greenlet

def func1():
    print(1)
    gr2.switch()  #切换到func2函数
    print(2)
    gr2.switch()  #切换到func2函数,从上一次执行位置继续向后执行

def func2():
    print(3)
    gr1.switch() #切换到func1函数,从上一次执行位置继续向后执行
    print(4)


gr1 = greenlet(func1)
gr2 = greenlet(func2)

gr1.switch()  #执行func1函数

#输出顺序 -》 1,3,2,4

2.2 yield 关键字

python 复制代码
def func1():
    yield 1
    yield from func2()
    yield 2

def func2():
    yield 3
    yield 4

f1 = func1()

for item in f1:
    print(item)

2.3 asyncio

python3.4 之后

python 复制代码
@asyncio.coroutine
def func1():
    print(1)
    yield from asyncio.sleep(2) #遇到IO耗时操作,自动化切换到tasks的其他任务
    print(2)

@asyncio.coroutine
def func2():
    print(3)
    yield from asyncio.sleep(2) #遇到IO耗时操作,自动化切换到tasks的其他任务
    print(4)

tasks = [
    asyncio.ensure_future(func1()),
    asyncio.ensure_future(func2()),
]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

#打印 出来也是1324

和前端js的宏任务,微任务一样,有点放在队列的意思

2.4 async await

python 3.5 及以后版本

python 复制代码
async def func1():
    print(1)
    await asyncio.sleep(2)  # IO耗时操作时,自动切换到其他任务
    print(2)


async def func2():
    print(3)
    await asyncio.sleep(2)  # IO耗时操作时,自动切换到其他任务
    print(4)


# 主协程:所有异步逻辑的入口(必须用 async def 定义)
async def main():
    # 1. 用 create_task() 替代 ensure_future()(3.13 推荐创建任务的方式)
    task1 = asyncio.create_task(func1())
    task2 = asyncio.create_task(func2())

    # 2. 等待所有任务完成(两种方式任选,效果一致)
    # 方式一:逐个 await(适合任务数量少的情况)
    await task1
    await task2

    # 方式二:用 gather() 批量等待(适合任务多的情况,更简洁)
    # await asyncio.gather(task1, task2)


if __name__ == "__main__":
    # 3. 用 asyncio.run() 启动主协程(3.13 唯一推荐的启动方式)
    # 自动创建/管理/关闭事件循环,无需手动操作 loop
    asyncio.run(main())

3. 协程的意义

在一个线程中,如果遇到了io等待的时间,线程不会等待,会利用空闲的时候再去干点其他的事情

案例: 下载图片

3.1 传统方式(同步)

python 复制代码
pip/pip3 install requests
python 复制代码
import requests
def dowload_images(url):
    print("开始下载", url)
    response = requests.get(url)
    print("下载完成")
    print(response)
    file_name = url.split("/")[-1]

    # 以2进制的方式写入文件,直接下载到当前文件夹
    with open(file_name, mode="wb") as file_object:
        file_object.write(response.content)

url_list = [
    'https://car2.autoimg.cn/cardfs/series/g31/M07/74/F8/400x300_autohomecar__ChxoHWYU1RCAPDx5AAjYvgS1ZC8852.png.png',
    'https://car3.autoimg.cn/cardfs/series/g33/M06/AD/95/400x300_autohomecar__ChxpVWkRnliACHALAC3azVUgZSs382.png.png'
]

for item in url_list:
    dowload_images(item)

# 按照写法,假设一张图片需要下载2分钟,那么2张图就是4分钟

按照协程的写法(异步)

python 复制代码
#aiohttp -Python 异步 HTTP 请求库,专门用于在异步程序中发送 HTTP/HTTPS 请求(对应同步的 requests 库);
# 核心用途:支持异步请求,不会因为等待一个请求的响应而阻塞其他任务(这是并发下载的关键
import aiohttp

#导入 Python 内置的异步编程核心库 ------ 负责管理事件循环、创建任务、调度协程等,是所有 Python 异步
#程序的 "发动机"。
import asyncio

# 返回一个协程对象
async def fetch(session, url):
        print("发送请求", url)
        async with session.get(url, verify_ssl=False) as response:
            print("请求成功")
            content = await response.content.read()
            file_name = url.split('_')[-1]
            with open(file_name, 'wb') as f:
                print("写入开始")
                f.write(content)
                print("写入成功")

async def main():
    async  with aiohttp.ClientSession() as session:
        url_list = [
            'https://car2.autoimg.cn/cardfs/series/g31/M07/74/F8/400x300_autohomecar__ChxoHWYU1RCAPDx5AAjYvgS1ZC8852.png.png',
            'https://car3.autoimg.cn/cardfs/series/g33/M06/AD/95/400x300_autohomecar__ChxpVWkRnliACHALAC3azVUgZSs382.png.png'
        ]
        tasks = [ asyncio.create_task(fetch(session, url)) for url in url_list]
        await  asyncio.wait(tasks)

if __name__ == '__main__':
    asyncio.run(main())

#可以看到打印,请求是一起发送的

4. 异步编程

4.1 事件循环

检测并执行某些代码

4.2 快速上手

协程函数: 定义函数的时候 async def 函数名

协程对象:执行协程函数得到的对象

python 复制代码
async def func():
    pass

result = func()

注意:创建协程函数创建协程对象,函数内部不会执行

python 复制代码
improt asyncio

async def func():
    pirnt("开始执行了")

result = func()

#获取事件循环,执行事件循环
# loop = asyncio.get_event_loop()
# loop.run_until_complete(result )

asyncio.run( result ) #pyhon3.7 后写法

4.3 await

await + 可等待对象(协程对象,Future, Task对象 -> IO等待)

python 复制代码
import asyncio 

async def func():
    print("执行1")
    respones = await asyncio.sleep(2)
    print("结束")

asyncio.run( func() )
python 复制代码
import asyncio

async def others():
    print("others1")
    await asyncio.sleep(2)
    print("others2")
    return "返回值"

async def func():
    print("我是func1-1")
    r = await others()
    print(r)

    r1 = await others()
    print(r1)
    print("我是func1-2")

asyncio.run(func())

4.4 Task对象

帮助在事件循环中添加多个任务

python 复制代码
import asyncio

async def func():
    print(1)
    await asyncio.sleep(1)
    print(2)
    return "返回值"

async def main():
    print("main开始")

    #创建task对象,将当前执行函数添加到事件循环中
    # task1 = asyncio.create_task(func())
    # task2 = asyncio.create_task(func())

    # ret1 = await task1
    # ret2 = await task2

    # name,加入队列的名称
    task_list = [
        asyncio.create_task(func(), name='task1'),
        asyncio.create_task(func(), name='task2')
    ]

    #timeout 是否延迟执行, 返回两种状态,done中是执行完成后的结果列表
    done ,pendding= await asyncio.wait(task_list, timeout=None)
    print(done)


# 创建事件循环队列,并将main添加在里面
asyncio.run(main())

4.5 asyncio.Future 对象

_state 维护了一个状态

Task继承了Future,Task内部await结果的处理基于Future对象来的

python 复制代码
import asyncio
async def main():
    # 获取到当前事件循环对象
    loop = asyncio.get_event_loop()
    # 创建一个任务(Future对象) 这个任务什么都不干
    fut = loop.create_future()

    await fut
asyncio.run(main())
python 复制代码
import asyncio

async def func(fut):
    print(1)
    await asyncio.sleep(1)
    fut.set_result(66)

async def main():
    # 获取到当前事件循环对象
    loop = asyncio.get_event_loop()
    # 创建一个任务(Future对象) 这个任务什么都不干
    fut = loop.create_future()
    #创建一个任务(task对象),绑定了func函数在1s后,会给fut赋值
    #即手动设置了future任务的最终结果
    await loop.create_task(func(fut))

    r = await fut
    print(r)

asyncio.run(main())

4.6 concurrent的Future对象

使用线程池,进程池实现异步操作时用到的对象

4.7 异步可迭代对象

可在async for 语句中被使用的对象。

创建一个异步迭代对象,循环生成 1~99 的数字,直到生成第 100 次时停止迭代

python 复制代码
import asyncio

class Reader(object):

    def __init__(self):
        self.count = 0
    
    async def readline(self):  
        self.count += 1
        if self.count == 100:
            return None
        return self.count

    def __aiter__(self):
        return self
    
    async def __anext__(self):
        val = await self.readline() 
        if val is None:  
            raise StopAsyncIteration 
        return val

async def func():
    obj = Reader()  
    async for item in obj:  
        print(item)

asyncio.run(func())  

5. 详细讲解 async await

5.1. 定义协程函数:async def

要创建一个 "能异步执行的函数",必须用 async def 开头(不能用普通 def):

python 复制代码
# 这是一个协程函数(注意 async def)
async def my_coroutine():
    print("协程开始执行")
    return "协程执行完成"

注意:协程函数不能直接调用!

如果像普通函数一样调用 my_coroutine(),不会执行函数内部代码,只会返回一个 "协程对象":

python 复制代码
result = my_coroutine()  # 不会打印任何内容
print(type(result))       # 输出:<class 'coroutine'>(是协程对象,不是返回值)

5.2. 运行协程:asyncio.run()

要真正执行协程函数,必须通过 asyncio.run()(Python 3.7+ 内置,最简单的启动方式):

python 复制代码
import asyncio  # 必须导入异步库

async def my_coroutine():
    print("协程开始执行")
    return "协程执行完成"

# 用 asyncio.run() 启动协程(这是异步程序的入口)
final_result = asyncio.run(my_coroutine())
print(final_result)  # 输出:协程执行完成

执行流程:

  1. asyncio.run() 会创建一个 "事件循环"(可以理解为 "任务调度中心",负责安排协程执行);
  2. 事件循环运行 my_coroutine 协程,执行函数内部代码;
  3. 协程执行完后,asyncio.run() 会返回协程的返回值;
  4. 最后关闭事件循环。

5.3. 暂停协程:await

await 是 "等待" 的意思,只能用在 协程函数内部,作用是:

  • 暂停当前协程的执行,去执行其他协程或异步操作;
  • 等被等待的操作完成后,再恢复当前协程的执行。

语法格式:

python 复制代码
async def 协程A():
    # 等待 协程B 执行完成,拿到返回值后再继续
    result = await 协程B()
    print(result)

示例:用 await 等待另一个协程

python 复制代码
import asyncio

# 定义一个需要"耗时"的协程(模拟 IO 操作,比如网络请求)
async def fake_io_operation():
    print("开始执行耗时操作(比如下载文件)")
    await asyncio.sleep(2)  # 模拟 2 秒耗时(注意:必须用 asyncio.sleep,不能用 time.sleep!)
    return "耗时操作完成"

# 主协程
async def main():
    print("主协程开始")
    # 等待 fake_io_operation 完成,拿到返回值
    result = await fake_io_operation()
    print("主协程继续执行,拿到结果:", result)

# 启动主协程
asyncio.run(main())

执行结果(会等待 2 秒):

plaintext

python 复制代码
主协程开始
开始执行耗时操作(比如下载文件)
# 此处暂停 2 秒(期间事件循环可以安排其他任务,但这个例子只有一个任务)
主协程继续执行,拿到结果: 耗时操作完成

关键提醒:

  • await 后面必须跟 "可等待对象" (常见的是:协程对象、asyncio.Taskasyncio.Future 等);
  • 绝对不能在普通 def 函数里用 await
  • 不能用 time.sleep(2) 模拟耗时!time.sleep 是 "同步阻塞",会让整个事件循环卡住;asyncio.sleep 是 "异步非阻塞",会让当前协程暂停,事件循环可以去做其他事。

5.4 案例

5.4.1 为什么要用异步

"为什么要用异步?同步不是挺好吗?" 看这个对比案例就懂了!

场景:模拟 3 个耗时任务(比如下载 3 个文件,每个任务耗时 2 秒)

1. 同步版本(普通 def + time.sleep
python 复制代码
import time

# 同步函数:每个任务耗时 2 秒
def sync_task(name):
    print(f"同步任务 {name} 开始")
    time.sleep(2)  # 同步阻塞,整个程序都要等
    print(f"同步任务 {name} 结束")

# 同步执行3个任务
def sync_main():
    start_time = time.time()
    sync_task("A")
    sync_task("B")
    sync_task("C")
    end_time = time.time()
    print(f"同步总耗时:{end_time - start_time:.2f} 秒")

sync_main()

执行结果(总耗时~6 秒):

复制代码
同步任务 A 开始
同步任务 A 结束
同步任务 B 开始
同步任务 B 结束
同步任务 C 开始
同步任务 C 结束
同步总耗时:6.01 秒

→ 问题:任务 A、B、C 按顺序执行,每个都要等前一个完成,总耗时是 2+2+2=6 秒。

2. 异步版本(async def + await + asyncio.sleep
python 复制代码
import asyncio
import time

# 异步任务:每个任务"耗时"2秒(非阻塞)
async def async_task(name):
    print(f"异步任务 {name} 开始")
    await asyncio.sleep(2)  # 异步暂停,事件循环可以去执行其他任务
    print(f"异步任务 {name} 结束")

# 异步执行3个任务
async def async_main():
    start_time = time.time()
    # 同时启动3个异步任务(关键:不是顺序执行,是并发执行)
    await asyncio.gather(
        async_task("A"),
        async_task("B"),
        async_task("C")
    )
    end_time = time.time()
    print(f"异步总耗时:{end_time - start_time:.2f} 秒")

# 启动异步主协程
asyncio.run(async_main())

执行结果(总耗时~2 秒):

复制代码
异步任务 A 开始
异步任务 B 开始
异步任务 C 开始
异步任务 A 结束
异步任务 B 结束
异步任务 C 结束
异步总耗时:2.01 秒

→ 奇迹:3 个任务 "同时执行",总耗时只有 2 秒!

关键解释:

  • asyncio.gather(task1, task2, task3):让多个协程 "并发执行"(同时启动,不用等前一个开始);
  • async_task(A) 执行到 await asyncio.sleep(2) 时,会暂停自己,事件循环立刻去执行 async_task(B)
  • async_task(B) 暂停后,再去执行 async_task(C)
  • 2 秒后,3 个任务的 sleep 都完成,各自恢复执行,打印 "结束"。

这就是异步的核心价值:在等待某个耗时操作时,不闲着,去处理其他任务,大幅节省总时间

5.4.2 异步获取网页

实际开发中,async/await 常用在 "网络请求""数据库操作" 等耗时 IO 场景。下面用 aiohttp(异步 HTTP 库)模拟批量获取网页内容:

步骤 1:安装依赖

复制代码
pip install aiohttp  # 异步 HTTP 库,支持 await

步骤 2:异步批量请求网页

python 复制代码
import asyncio
import aiohttp  # 异步 HTTP 客户端

# 要请求的网页地址列表
URLS = [
    "https://www.baidu.com",
    "https://www.taobao.com",
    "https://www.jd.com",
    "https://www.zhihu.com"
]

# 异步请求单个网页
async def fetch_url(session, url):
    async with session.get(url) as response:  # 异步发送 GET 请求
        return f"{url}:状态码 {response.status}"  # 返回响应状态码

# 主协程:批量请求所有网页
async def main():
    # 创建一个异步 HTTP 会话(类似 requests.Session,但支持异步)
    async with aiohttp.ClientSession() as session:
        # 创建所有要执行的异步任务
        tasks = [fetch_url(session, url) for url in URLS]
        # 并发执行所有任务,等待全部完成后返回结果列表
        results = await asyncio.gather(*tasks)  # * 是解包列表
    
    # 打印所有结果
    for result in results:
        print(result)

# 启动程序
asyncio.run(main())
执行效果:

4 个网页请求 "同时进行",总耗时≈单个网页的请求时间(比如 1~2 秒);如果用同步的 requests 库,总耗时≈4 个网页请求时间之和(比如 4~8 秒)。

常见误区

  1. 误区 1:用 time.sleep 模拟异步耗时 → 错!time.sleep 是同步阻塞,会让整个事件循环卡住,异步变同步。→ 正确:用 asyncio.sleep()

  2. 误区 2:在普通 def 函数里用 await → 错!await 只能在 async def 定义的协程函数内部使用。→ 正确:如果需要用 await,必须把函数改成 async def

  3. 误区 3:直接调用协程函数就能执行 → 错!async def 函数调用后返回协程对象,不会执行内部代码。→ 正确:用 asyncio.run() 或事件循环(loop.run_until_complete())启动。

  4. 误区 4:异步一定比同步快→ 错!只有在 "有耗时 IO 操作"(网络、文件、数据库)时,异步才快;如果是 CPU 密集型任务(比如计算),异步反而可能更慢(因为事件循环有调度开销)。

总结(核心知识点浓缩)

  1. async def:定义协程函数,返回协程对象(不能直接执行);
  2. asyncio.run(coro):启动协程的入口,自动管理事件循环;
  3. await:暂停当前协程,等待其他异步操作完成后恢复,只能用在协程函数内;
  4. 核心价值:IO 密集型场景下,通过 "并发执行" 减少等待时间,提高程序效率;
  5. 常用工具asyncio.gather()(并发执行多个协程)、aiohttp(异步网络请求)、asyncpg(异步操作 PostgreSQL)等。

二 python三大http请求库

1. requests

1. 基本介绍

requests的官方文档:https://requests.readthedocs.io/en/latest/user/quickstart/#make-a-request

w3c 学习文档:https://www.w3cschool.cn/requests2/requests2-fu433fja.html

Requests 是 Python 最流行的 HTTP 请求库,功能强大且用法简洁,能轻松实现网页爬取、接口调用、数据提交等需求。本教程从环境搭建到实战案例,循序渐进带你掌握核心用法,学完即可上手开发。

2. 安装

python 复制代码
pip|pip3 install requests

3. 核心基础:Requests 核心方法与语法

Requests 最常用的 5 个方法,对应 HTTP 协议的核心请求类型:

方法 作用 场景示例
requests.get() 发送 GET 请求(获取数据) 爬取网页、获取接口数据
requests.post() 发送 POST 请求(提交数据) 登录、表单提交、上传文件
requests.put() 发送 PUT 请求(全量更新数据) 接口更新资源(如修改用户信息)
requests.delete() 发送 DELETE 请求(删除数据) 接口删除资源(如删除订单)
requests.session() 创建会话(保持登录状态) 需登录后才能访问的页面 / 接口
3.1 GET 请求(获取数据)
3.1.1 简单请求
python 复制代码
import requests  # 导入 requests 库

# 1. 发送 GET 请求到目标 URL
response = requests.get("https://www.baidu.com")  # response 是响应对象,包含服务器返回的所有信息

# 2. 查看响应结果
print("响应状态码:", response.status_code)  # 状态码:200 表示成功,4xx 客户端错误,5xx 服务器错误
print("响应内容(前100字符):", response.text[:100])  # response.text:以字符串形式返回响应内容(网页源码)
3.1.2 核心响应对象属性
属性 / 方法 说明 示例输出
response.status_code 响应状态码(判断请求是否成功) 200(成功)、404(未找到)
response.text 以字符串返回响应内容(自动解码) 网页 HTML 源码、接口 JSON 字符串
response.content 以字节流返回响应内容(二进制数据) 图片、文件、未解码的文本
response.json() 自动解析 JSON 格式的响应内容(返回字典) {"name": "张三", "age": 20}
response.headers 响应头(服务器返回的 metadata) {'Content-Type': 'text/html'}
response.url 实际请求的 URL(处理重定向后) https://www.baidu.com/
3.1.3 GET 请求带参数

很多时候需要在 URL 后拼接参数(如 https://search.baidu.com/s?wd=pythonwd=python 是搜索参数),用 params 参数传递更简洁(无需手动拼接 URL):

python 复制代码
import requests

# 定义要传递的参数(字典格式)
params = {
    "wd": "Python Requests 教程",  # 搜索关键词
    "pn": 0  # 分页参数(第 0 页,即第一页)
}

# 发送带参数的 GET 请求(params 参数自动拼接 URL)
response = requests.get(
    url="https://search.baidu.com/s",  # 基础 URL(不含参数)
    params=params  # 传递参数
)

print("实际请求的 URL:", response.url)  # 输出拼接后的完整 URL
print("响应状态码:", response.status_code)
3.2 POST 请求

POST 请求用于向服务器提交数据(如登录、注册、提交表单),数据放在请求体中(而非 URL 上),更安全。

3.2.1 提交表单数据(data 参数)

适用于 Content-Type: application/x-www-form-urlencoded 格式(最常见的表单提交格式):

python 复制代码
import requests

# 1. 定义要提交的表单数据(字典格式)
form_data = {
    "username": "test_user",
    "password": "123456"
}

# 2. 发送 POST 请求(data 参数传递表单数据)
response = requests.post(
    url="https://example.com/login",  # 目标接口 URL(示例,需替换为实际接口)
    data=form_data  # 提交表单数据
)

# 3. 解析响应(假设接口返回 JSON 格式)
if response.status_code == 200:
    result = response.json()  # 自动解析 JSON
    print("登录结果:", result)
else:
    print("请求失败,状态码:", response.status_code)
3.2.2 提交 JSON 数据(json 参数)

适用于 Content-Type: application/json 格式(现代接口常用,如 RESTful API):

python 复制代码
import requests

# 1. 定义 JSON 数据(字典格式,requests 会自动序列化为 JSON 字符串)
json_data = {
    "name": "张三",
    "age": 25,
    "hobbies": ["编程", "阅读"]
}

# 2. 发送 POST 请求(json 参数传递 JSON 数据)
response = requests.post(
    url="https://example.com/api/user",
    json=json_data  # 自动设置 Content-Type: application/json
)

# 3. 处理响应
print("响应结果:", response.json())
3.3 配置:请求头、超时、代理
3.3.1 设置请求头(headers

模拟浏览器请求(避免被服务器识别为爬虫),最常用的是 User-Agent(标识客户端类型):

python 复制代码
import requests

# 定义请求头(模拟 Chrome 浏览器)
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}

# 发送带请求头的 GET 请求
response = requests.get(
    url="https://www.baidu.com",
    headers=headers
)

print("响应状态码:", response.status_code)
3.3.2 设置超时(timeout

避免请求因网络问题一直阻塞,timeout 表示最大等待时间(单位:秒):

python 复制代码
import requests

try:
    response = requests.get(
        url="https://www.baidu.com",
        timeout=5  # 5 秒内未响应则抛出超时异常
    )
except requests.exceptions.Timeout:
    print("请求超时,请检查网络!")
3.3.3 处理 SSL 证书验证(verify

访问 HTTPS 网站时,requests 会自动验证 SSL 证书,若证书无效(如自签名证书),可关闭验证:

python 复制代码
import requests

# verify=False 关闭 SSL 证书验证(仅用于测试/内部网站)
response = requests.get(
    url="https://xxx.com",  # 示例:证书无效的 HTTPS 网站
    verify=False
)
3.4 会话保持(requests.Session()

用于需要维持登录状态的场景(如登录后访问个人中心),会话会自动保存 cookies:

python 复制代码
import requests

# 1. 创建会话对象(自动保存 cookies)
session = requests.Session()

# 2. 第一步:发送登录请求(会话会保存登录后的 cookies)
login_data = {"username": "test", "password": "123"}
session.post("https://example.com/login", data=login_data)

# 3. 第二步:访问需要登录的页面(会话携带 cookies,无需再次登录)
response = session.get("https://example.com/user/center")

print("个人中心内容:", response.text[:200])  # 成功获取登录后的内容

4. 实战案例:

4.1:爬取网页内容(百度首页标题)

场景:获取百度首页的 HTML 源码,并提取页面标题(基础爬虫入门)。

python 复制代码
import requests
from bs4 import BeautifulSoup  # 用于解析 HTML(需安装:pip install beautifulsoup4)

def crawl_baidu_title():
    # 1. 目标 URL(百度首页)
    url = "https://www.baidu.com"
    
    # 2. 设置请求头(模拟浏览器,避免被反爬)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
    }
    
    try:
        # 3. 发送 GET 请求(设置超时 5 秒)
        response = requests.get(
            url=url,
            headers=headers,
            timeout=5
        )
        
        # 4. 检查请求是否成功(200 表示成功)
        if response.status_code == 200:
            # 5. 解析 HTML 源码(用 BeautifulSoup)
            soup = BeautifulSoup(response.text, "html.parser")  # html.parser 是 Python 内置解析器
            title = soup.find("title").text  # 查找 <title> 标签并获取文本内容
            
            print(f"百度首页标题:{title}")
            print(f"页面源码长度:{len(response.text)} 字符")
        else:
            print(f"请求失败,状态码:{response.status_code}")
    
    # 捕获常见异常
    except requests.exceptions.Timeout:
        print("请求超时,请检查网络连接!")
    except requests.exceptions.ConnectionError:
        print("连接错误,请检查 URL 是否正确!")
    except Exception as e:
        print(f"未知错误:{str(e)}")

# 执行函数
if __name__ == "__main__":
    crawl_baidu_title()
语法讲解:
  • BeautifulSoup(response.text, "html.parser"):将 HTML 字符串转为可操作的对象,html.parser 无需额外安装;
  • soup.find("title"):查找 HTML 中第一个 <title> 标签,text 属性获取标签内的文本;
  • 异常捕获:Timeout(超时)、ConnectionError(连接错误)是网络请求最常见的异常,避免程序崩溃。
4.2:调用公开 API(获取天气数据)

场景:调用第三方公开 API(聚合数据天气接口),获取指定城市的天气(接口调用入门)。

python 复制代码
import requests

def get_weather(city="北京"):
    # 1. 接口信息(聚合数据天气 API,需替换为自己的 key,免费申请地址:https://www.juhe.cn/docs/api/id/73)
    api_url = "http://v.juhe.cn/tianqi/index"
    api_key = "你的接口 key"  # 替换为自己申请的 key(免费)
    
    # 2. 接口参数(根据 API 文档传递)
    params = {
        "cityname": city,  # 城市名称
        "key": api_key,    # 接口密钥
        "format": 2        # 返回格式:2 表示 JSON(1 表示 XML)
    }
    
    try:
        # 3. 发送 GET 请求(带参数)
        response = requests.get(
            url=api_url,
            params=params,
            timeout=10
        )
        
        if response.status_code == 200:
            # 4. 解析 JSON 响应(接口返回 JSON 格式)
            result = response.json()
            
            # 5. 根据 API 返回的状态判断是否成功(不同 API 的状态字段可能不同)
            if result["error_code"] == 0:  # error_code=0 表示接口调用成功
                # 提取关键天气数据
                realtime_weather = result["result"]["realtime"]  # 实时天气
                today_weather = result["result"]["future"][0]    # 今日天气
                
                print(f"===== {city} 天气信息 =====")
                print(f"当前温度:{realtime_weather['temperature']}℃")
                print(f"天气状况:{realtime_weather['info']}")
                print(f"风力:{realtime_weather['direct']}{realtime_weather['power']}级")
                print(f"今日最低温:{today_weather['temperature'].split('-')[0]}℃")
                print(f"今日最高温:{today_weather['temperature'].split('-')[1]}℃")
                print(f"温馨提示:{result['result']['life']['info']['chuanyi'][0]}")
            else:
                print(f"接口调用失败:{result['reason']}(错误码:{result['error_code']})")
        else:
            print(f"请求失败,状态码:{response.status_code}")
    
    except requests.exceptions.Timeout:
        print("请求超时,请稍后重试!")
    except Exception as e:
        print(f"错误:{str(e)}")

# 执行函数(可传入其他城市,如 get_weather("上海"))
if __name__ == "__main__":
    get_weather("广州")
语法讲解:
  • 接口参数:params 传递 API 要求的参数(城市名、密钥),API 文档会明确说明必填参数;
  • response.json():自动将 JSON 格式的响应转为 Python 字典,方便提取数据;
  • 嵌套字典取值:result["result"]["realtime"] 表示从响应结果中逐层提取实时天气数据,需根据 API 返回结构调整。
4.3:文件下载(下载图片到本地)

场景:从网络下载图片(如产品详情图)并保存到本地(文件操作 + 请求结合)。

python 复制代码
import requests
import os

def download_image(image_url, save_dir="downloads"):
    # 1. 验证 URL 是否有效(简单判断是否为图片 URL)
    if not image_url.endswith((".jpg", ".png", ".jpeg", ".gif")):
        print("无效的图片 URL(需以 .jpg/.png 等结尾)")
        return
    
    # 2. 创建保存目录(若不存在则创建)
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)  # 递归创建目录
    
    # 3. 提取文件名(从 URL 中截取,如 "https://example.com/img.jpg" → "img.jpg")
    file_name = image_url.split("/")[-1]
    save_path = os.path.join(save_dir, file_name)  # 完整保存路径(目录+文件名)
    
    try:
        # 4. 发送 GET 请求(stream=True 表示流式下载,适合大文件)
        response = requests.get(
            url=image_url,
            stream=True,  # 流式下载,避免一次性加载大文件到内存
            timeout=10
        )
        
        if response.status_code == 200:
            # 5. 以二进制模式写入文件(图片是二进制数据,用 "wb" 模式)
            with open(save_path, "wb") as f:
                # 分块写入(每次写 1024 字节,适合大文件)
                for chunk in response.iter_content(chunk_size=1024):
                    if chunk:  # 过滤空块
                        f.write(chunk)
            
            print(f"图片下载成功!保存路径:{save_path}")
        else:
            print(f"下载失败,状态码:{response.status_code}")
    
    except requests.exceptions.Timeout:
        print("下载超时!")
    except requests.exceptions.ConnectionError:
        print("连接错误,图片 URL 不可访问!")
    except Exception as e:
        print(f"下载错误:{str(e)}")

# 执行函数(可替换为任意图片 URL)
if __name__ == "__main__":
    image_url = "https://picsum.photos/800/600"  # 测试用图片 URL(随机生成 800x600 图片)
    download_image(image_url)
语法讲解:
  • stream=True:流式下载,适用于图片、视频等大文件,避免占用过多内存;
  • response.iter_content(chunk_size=1024):分块读取响应内容,chunk_size 表示每次读取的字节数;
  • 文件操作:with open(save_path, "wb") 以二进制写入模式打开文件,with 语句会自动关闭文件,无需手动调用 f.close()
  • 目录创建:os.makedirs(save_dir) 递归创建目录(若父目录不存在也会创建)。

5. 常见问题与避坑指南

  1. 请求被拒绝(403 Forbidden)

    • 原因:服务器识别为爬虫,拒绝访问;
    • 解决:设置 User-Agent 模拟浏览器,必要时添加 Referer(请求来源)。
  2. JSON 解析失败(json.decoder.JSONDecodeError)

    • 原因:响应内容不是 JSON 格式(如 HTML 错误页面);
    • 解决:先打印 response.text 查看实际响应内容,确认接口返回格式是否正确。
  3. 大文件下载内存溢出

    • 原因:未使用流式下载,一次性将大文件加载到内存;
    • 解决:添加 stream=True,分块写入文件(如案例 3)。
  4. 接口调用失败(错误码非 0)

    • 原因:参数错误、密钥无效、接口限流;
    • 解决:查看 API 文档,检查参数是否完整,密钥是否有效,是否需要处理限流(如添加请求间隔)。

6. 总结(核心知识点浓缩)

  1. 核心方法get()(获取)、post()(提交)、session()(会话保持);
  2. 关键参数params(GET 参数)、data/json(POST 数据)、headers(请求头)、timeout(超时);
  3. 响应处理status_code(判断成功)、text(字符串)、json()(JSON 解析)、content(二进制);
  4. 实战场景:网页爬取、接口调用、文件下载,掌握这 3 类场景即可应对 80% 的需求;
  5. 避坑关键:设置请求头、处理异常、流式下载大文件、保持会话状态。

2. httpx

1. 基本介绍

httpx 是 requests 的「增强版」HTTP 请求库,核心优势是 兼容 requests 语法 + 支持同步 / 异步双模式 + 更强功能扩展(HTTP/2、自动重试等)。

2. 安装

复制代码
# 基础安装(仅支持同步+HTTP/1.1)
pip|pip3 install httpx

# 完整安装(推荐,支持异步+HTTP/2+代理等扩展功能)
pip|pip3 install httpx[http2]

验证安装成功

复制代码
python -c "import httpx; print(httpx.__version__)"
# 示例输出:0.25.2

3. 核心基础:httpx 同步模式(完全兼容 requests)

httpx 同步模式的 API 与 requests 几乎完全一致,相当于「无缝替换」,但增加了更多实用功能。如果你的项目正在用 requests,直接全局替换 requestshttpx 即可运行。

3.1 入门第一步:GET 请求(获取数据)
3.1.1 简单入门
python 复制代码
import httpx

# 1. 发送 GET 请求(和 requests.get() 语法完全一致)
response = httpx.get("https://www.baidu.com")

# 2. 查看响应核心属性(和 requests 一致)
print("响应状态码:", response.status_code)  # 200 表示成功
print("响应内容(前 100 字符):", response.text[:100])  # 字符串形式响应(自动解码)
print("响应内容(字节流):", response.content[:50])  # 二进制形式(用于图片/文件)
print("响应头:", response.headers)  # 服务器返回的元数据
print("实际请求 URL:", response.url)  # 处理重定向后的最终 URL
3.1.2 GET 请求带参数
python 复制代码
import httpx

def get_with_params():
    # 1. 定义参数(字典格式,键值对对应 URL 中的参数)
    params = {
        "wd": "httpx 教程",  # 搜索关键词
        "pn": 0,  # 分页参数(第 0 页=第一页)
        "rn": 10  # 每页显示 10 条结果
    }

    # 2. 发送带参数的 GET 请求
    response = httpx.get(
        url="https://search.baidu.com/s",  # 基础 URL(不含参数)
        params=params,  # 传递参数(自动拼接为完整 URL)
        timeout=5  # 超时时间(5 秒内未响应则抛出异常,和 requests 一致)
    )

    # 3. 验证结果
    print("拼接后的完整 URL:", response.url)
    # 输出示例:https://search.baidu.com/s?wd=httpx+%E6%95%99%E7%A8%8B&pn=0&rn=10

if __name__ == "__main__":
    get_with_params()
参数解析:
  • url:目标请求地址(基础 URL,不含参数);
  • params:字典类型,存储 URL 拼接参数,httpx 会自动编码特殊字符(如中文、空格);
  • timeout:整数 / 浮点数,单位秒,防止请求因网络问题无限阻塞。
3.2 POST 请求

POST 用于向服务器提交数据(登录、表单提交、接口调用),httpx 支持两种主流格式:表单数据(application/x-www-form-urlencoded)和 JSON 数据(application/json)。

3.2.1 提交表单数据(data 参数)
python 复制代码
import httpx

def post_form_data():
    # 1. 定义表单数据(字典格式)
    form_data = {
        "username": "test_user",
        "password": "123456"
    }

    # 2. 发送 POST 请求(用 data 参数传递表单数据)
    response = httpx.post(
        url="https://example.com/login",  # 模拟登录接口(实际需替换为真实接口)
        data=form_data,  # 表单数据(自动设置 Content-Type: application/x-www-form-urlencoded)
        timeout=5
    )

    # 3. 处理响应(假设接口返回 JSON)
    if response.status_code == 200:
        result = response.json()  # 自动解析 JSON(和 requests 一致)
        print("登录结果:", result)
    else:
        print("请求失败,状态码:", response.status_code)

if __name__ == "__main__":
    post_form_data()
3.2.2 提交 JSON 数据(json 参数)

适用于现代 API(如 RESTful API),httpx 会自动将字典序列化为 JSON 字符串,并设置请求头 Content-Type: application/json

python 复制代码
import httpx

def post_json_data():
    # 1. 定义 JSON 数据(字典格式,支持嵌套)
    json_data = {
        "name": "张三",
        "age": 25,
        "hobbies": ["编程", "阅读"],
        "address": {"city": "北京", "district": "朝阳区"}
    }

    # 2. 发送 POST 请求(用 json 参数传递 JSON 数据)
    response = httpx.post(
        url="https://example.com/api/user",  # 模拟用户注册接口
        json=json_data,  # 自动序列化为 JSON,无需手动 json.dumps()
        timeout=5
    )

    print("响应结果:", response.json())

if __name__ == "__main__":
    post_json_data()
3.3 进阶配置:请求头、代理、超时
3.3.1 设置请求头(模拟浏览器,避免反爬)

服务器可能通过 User-Agent 识别爬虫,需设置请求头模拟浏览器:

python 复制代码
import httpx

def get_with_headers():
    # 1. 定义请求头(字典格式,key 为请求头名称,value 为对应值)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Referer": "https://www.baidu.com"  # 模拟请求来源(部分网站会校验)
    }

    # 2. 发送带请求头的 GET 请求
    response = httpx.get(
        url="https://www.baidu.com",
        headers=headers,
        timeout=5
    )

    print("响应状态码:", response.status_code)  # 200 表示成功(未被反爬拦截)

if __name__ == "__main__":
    get_with_headers()

参数解析:

  • headers:字典类型,常用键包括 User-Agent(客户端标识)、Referer(请求来源)、Authorization(身份认证 token)等。
3.3.2 设置代理(突破 IP 限制)

爬虫或接口测试时,需用代理隐藏真实 IP,httpx 支持 HTTP/HTTPS/SOCKS 代理:

python 复制代码
import httpx

def get_with_proxy():
    # 1. 定义代理配置(字典格式,key 为协议,value 为代理地址)
    proxies = {
        "http://": "http://127.0.0.1:7890",  # HTTP 代理
        "https://": "http://127.0.0.1:7890",  # HTTPS 代理
        # "https://": "socks5://127.0.0.1:7890"  # SOCKS 代理(需完整安装 httpx[http2])
    }

    # 2. 发送带代理的请求
    response = httpx.get(
        url="https://httpbin.org/ip",  # 用于查看当前 IP 的测试接口
        proxies=proxies,
        timeout=10,
        verify=False  # 关闭 SSL 证书验证(部分代理可能需要)
    )

    print("当前 IP 信息:", response.json())

if __name__ == "__main__":
    get_with_proxy()
3.3.3 会话保持(httpx.Client

用于需要维持登录状态的场景(如登录后访问个人中心),会话会自动保存 cookies,类似 requests.Session:

python 复制代码
import httpx

def session_demo():
    # 1. 创建会话对象(自动保存 cookies,复用连接)
    with httpx.Client() as client:
        # 2. 第一步:发送登录请求(会话保存登录 cookies)
        login_data = {"username": "test", "password": "123456"}
        client.post("https://example.com/login", data=login_data)

        # 3. 第二步:访问需要登录的页面(会话携带 cookies,无需再次登录)
        response = client.get("https://example.com/user/center")

        print("个人中心内容(前 200 字符):", response.text[:200])

if __name__ == "__main__":
    session_demo()
核心优势:
  • 复用 TCP 连接,比每次单独请求更快;
  • 自动保存 cookies、请求头,无需手动传递。

4. httpx 异步模式(高并发必备)

httpx 支持异步请求(需完整安装 httpx[http2]),语法简单,并发性能远超同步模式,适合批量接口调用、爬取等场景。

4.1 异步基础语法(AsyncClient

异步模式的核心是 httpx.AsyncClient,配合 async/await 语法使用,步骤如下:

  1. 函数定义前加 async 关键字;
  2. async with httpx.AsyncClient() 管理异步客户端;
  3. 请求方法(get/post)前加 await 等待响应;
  4. asyncio.run() 启动异步事件循环。
4.1.1 异步 GET 请求示例
python 复制代码
import httpx
import asyncio  # Python 内置异步库,用于启动事件循环

# 1. 定义异步函数(加 async 关键字)
async def async_get_demo():
    # 2. 创建异步客户端(async with 自动管理连接)
    async with httpx.AsyncClient() as client:
        # 3. 异步请求(加 await 等待响应,非阻塞)
        response = await client.get(
            url="https://www.baidu.com",
            timeout=5
        )
        print("异步 GET 响应状态码:", response.status_code)
        return response.text[:100]

# 4. 启动异步事件循环(执行异步函数)
if __name__ == "__main__":
    result = asyncio.run(async_get_demo())
    print("异步 GET 响应内容(前 100 字符):", result)
4.1.2 异步并发执行多个任务(核心场景)

asyncio.gather() 同时启动多个异步任务,总耗时 = 最长单个任务耗时,效率大幅提升:

python 复制代码
import httpx
import asyncio
import time

# 异步任务:模拟耗时接口请求(如获取城市天气)
async def fetch_weather(client, city):
    """
    异步获取城市天气
    :param client: 异步客户端对象(复用,提升性能)
    :param city: 城市名称(字符串)
    :return: 天气信息字符串
    """
    params = {
        "cityname": city,
        "key": "你的接口 key",  # 替换为聚合数据天气 API 的 key(免费申请:https://www.juhe.cn/docs/api/id/73)
        "format": 2  # 返回 JSON 格式
    }
    # 异步发送 GET 请求
    response = await client.get(
        url="http://v.juhe.cn/tianqi/index",
        params=params,
        timeout=10
    )
    # 解析响应(JSON 格式)
    result = response.json()
    if result["error_code"] == 0:
        realtime = result["result"]["realtime"]
        return f"【{city}】天气:{realtime['info']},温度:{realtime['temperature']}℃"
    else:
        return f"【{city}】获取失败:{result['reason']}"

# 主异步函数:批量执行任务
async def main():
    # 定义需要查询的城市列表(10 个城市,并发执行)
    cities = ["北京", "上海", "广州", "深圳", "杭州", "成都", "武汉", "西安", "南京", "重庆"]
    
    # 创建异步客户端(复用连接,关键优化)
    async with httpx.AsyncClient() as client:
        # 生成所有异步任务(不立即执行,仅创建任务对象)
        tasks = [fetch_weather(client, city) for city in cities]
        
        # 并发执行所有任务,等待全部完成(总耗时≈最长单个任务耗时)
        results = await asyncio.gather(*tasks)  # * 用于解包任务列表
    
    # 打印所有结果
    for res in results:
        print(res)

if __name__ == "__main__":
    # 记录开始时间
    start_time = time.time()
    # 启动异步事件循环
    asyncio.run(main())
    # 计算总耗时
    end_time = time.time()
    print(f"\n总耗时:{end_time - start_time:.2f} 秒")

代码解析:

  1. 参数说明

    • client:异步客户端对象,必须复用(不能在每个任务中创建新客户端,否则会频繁建立连接,性能骤降);
    • city:字符串类型,传入需要查询的城市名称;
    • tasks:列表推导式生成所有异步任务,每个任务对应一个城市的天气查询。
  2. 执行效果:10 个城市的天气查询并发执行,总耗时≈1-2 秒(同步执行需 10-20 秒),体现异步高并发优势。

  3. 核心优化

    • 复用 AsyncClient:一个客户端管理所有请求,复用 TCP 连接;
    • asyncio.gather(*tasks):批量并发执行任务,自动调度,无需手动管理。

5.httpx 增强功能(requests 没有的实用特性)

httpx 相比 requests 增加了多个关键功能,无需额外依赖即可实现。

5.1 自动重试(避免网络波动导致失败)

网络不稳定时,自动重试失败的请求(如超时、连接错误),无需手动写重试逻辑:

python 复制代码
import httpx

def retry_demo():
    # 创建客户端时配置重试策略
    with httpx.Client(
        retries=3,  # 最大重试次数(3 次)
        retry_conditions=httpx.HTTPStatusRetryCondition(
            status_codes=[500, 502, 503, 504]  # 仅对 5xx 服务器错误重试
        )
    ) as client:
        response = client.get("https://example.com/api/data", timeout=5)
        print("响应状态码:", response.status_code)

if __name__ == "__main__":
    retry_demo()
5.2 支持 HTTP/2 协议

现代 API 越来越多支持 HTTP/2,httpx 完整安装后可自动适配,无需额外配置:

python 复制代码
import httpx

def http2_demo():
    # 创建支持 HTTP/2 的客户端
    with httpx.Client(http2=True) as client:
        response = client.get("https://http2.pro/api/v1", timeout=5)
        print("HTTP 协议版本:", response.http_version)  # 输出:HTTP/2
        print("响应结果:", response.json())

if __name__ == "__main__":
    http2_demo()
5.3 连接限制(防止并发过高)

批量请求时,限制最大连接数,避免给服务器造成过大压力:

python 复制代码
import httpx

def limit_connections_demo():
    # 配置最大连接数(全局最大 10 个,单主机最大 5 个)
    limits = httpx.Limits(
        max_connections=10,
        max_keepalive_connections=5
    )
    with httpx.Client(limits=limits) as client:
        for i in range(10):
            response = client.get("https://www.baidu.com", timeout=5)
            print(f"第 {i+1} 次请求状态码:", response.status_code)

if __name__ == "__main__":
    limit_connections_demo()

6. 实战案例:httpx 异步批量爬取网页

6.1 案例场景

批量爬取 5 个主流网站的标题,用异步模式实现高并发,对比同步模式的效率差异。

python 复制代码
import httpx
import asyncio
import time
from bs4 import BeautifulSoup  # 用于解析 HTML(需安装:pip install beautifulsoup4)

async def crawl_page(client, url, site_name):
    """
    异步爬取单个网页的标题
    :param client: 异步客户端对象(复用连接)
    :param url: 目标网页 URL(字符串)
    :param site_name: 网站名称(用于日志输出,字符串)
    :return: 爬取结果(成功返回标题,失败返回错误信息)
    """
    try:
        # 1. 异步发送 GET 请求(加 await,设置超时 10 秒)
        response = await client.get(
            url=url,
            headers={
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
            },
            timeout=httpx.Timeout(10.0)  # 显式配置超时(更灵活)
        )
        
        # 2. 检查请求是否成功(200 表示成功)
        response.raise_for_status()  # 若状态码为 4xx/5xx,会抛出异常
        
        # 3. 解析 HTML,提取标题(用 BeautifulSoup)
        soup = BeautifulSoup(response.text, "html.parser")  # html.parser 是 Python 内置解析器
        title = soup.find("title").text.strip()  # 查找 <title> 标签并获取文本
        
        return f"✅ 【{site_name}】爬取成功:标题={title},页面大小={len(response.text)} 字符"
    
    # 捕获常见异常
    except httpx.TimeoutException:
        return f"❌ 【{site_name}】爬取失败:请求超时"
    except httpx.HTTPStatusError as e:
        return f"❌ 【{site_name}】爬取失败:状态码={e.response.status_code}"
    except Exception as e:
        return f"❌ 【{site_name}】爬取失败:未知错误={str(e)}"

async def main():
    """主异步函数:批量启动爬取任务"""
    # 1. 定义要爬取的网站列表(URL + 网站名称)
    sites = [
        ("https://www.baidu.com", "百度"),
        ("https://www.taobao.com", "淘宝"),
        ("https://www.jd.com", "京东"),
        ("https://www.zhihu.com", "知乎"),
        ("https://www.bilibili.com", "B站")
    ]
    
    # 2. 创建异步客户端(复用连接,配置超时和连接限制)
    async with httpx.AsyncClient(
        timeout=httpx.Timeout(10.0),  # 全局超时配置
        limits=httpx.Limits(max_connections=10)  # 最大连接数限制
    ) as client:
        # 3. 生成所有异步任务(列表推导式,每个任务对应一个网站)
        tasks = [crawl_page(client, url, name) for url, name in sites]
        
        # 4. 并发执行所有任务,等待全部完成(返回结果列表)
        results = await asyncio.gather(*tasks)
    
    # 5. 打印所有爬取结果
    print("=" * 50)
    print("爬取结果汇总:")
    print("=" * 50)
    for result in results:
        print(result)

if __name__ == "__main__":
    # 记录开始时间
    start_time = time.time()
    
    # 启动异步事件循环(执行主函数)
    asyncio.run(main())
    
    # 计算总耗时
    end_time = time.time()
    print(f"\n总耗时:{end_time - start_time:.2f} 秒")

运行效果

复制代码
==================================================
爬取结果汇总:
==================================================
✅ 【百度】爬取成功:标题=百度一下,你就知道,页面大小=22732 字符
✅ 【京东】爬取成功:标题=京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!,页面大小=138567 字符
✅ 【淘宝】爬取成功:标题=淘宝网 - 淘!我喜欢,页面大小=104892 字符
✅ 【知乎】爬取成功:标题=知乎 - 有问题,就会有答案,页面大小=89765 字符
✅ 【B站】爬取成功:标题=哔哩哔哩 (゜-゜)つロ 干杯~-bilibili,页面大小=98743 字符

总耗时:1.86 秒

核心亮点解析

  1. 高并发:5 个网站同时爬取,总耗时≈1-2 秒(同步模式需 5-10 秒);
  2. 异常处理:捕获超时、状态码错误等常见问题,避免单个任务失败导致整体崩溃;
  3. 连接复用 :复用 AsyncClient,减少 TCP 连接建立 / 关闭的开销;
  4. 灵活配置:支持全局超时、连接限制,适配不同场景需求。

7. 常见问题与避坑指南

7.1. 异步模式报错「No module named 'httpx._async'」
  • 原因:未安装完整版本的 httpx,缺少异步依赖;
  • 解决:执行 pip install httpx[http2] 重新安装。
7.2. 响应解析 JSON 报错「JSONDecodeError」
  • 原因:响应内容不是 JSON 格式(如 HTML 错误页面);
  • 解决:先打印 response.text 查看实际响应内容,确认接口返回格式是否正确。
7.3. 异步任务中创建多个 AsyncClient 导致性能差
  • 错误:在循环 / 子函数中创建 async with httpx.AsyncClient()
  • 正确:复用一个 AsyncClient,传入所有子任务(如实战案例中那样)。
7.4. 代理配置后无法访问
  • 原因:代理地址错误、端口未开放,或 HTTPS 代理需要关闭 SSL 验证;
  • 解决:检查代理是否可用,添加 verify=False 参数(仅测试环境使用)。
7.5. 超时参数配置错误
  • 同步模式:timeout=5(直接传数字,单位秒);
  • 异步模式:支持两种写法:timeout=5timeout=httpx.Timeout(5)(更灵活)。

8. 总结

  1. 同步模式:完全兼容 requests,直接替换即可使用,新增自动重试、HTTP/2 等功能;
  2. 异步模式 :用 AsyncClient+async/await,高并发场景必备,语法简单;
  3. 核心 API
    • 同步:httpx.get()/httpx.post()/httpx.Client()
    • 异步:httpx.AsyncClient()+await client.get()
  4. 适用场景
    • 简单脚本、少量请求:用同步模式;
    • 批量接口调用、爬取、高并发:用异步模式;
    • 需要 HTTP/2、自动重试、代理池:优先选 httpx。
相关推荐
深蓝电商API1 天前
httpx 异步客户端处理 WebSocket 数据
websocket·网络协议·httpx
SunnyRivers2 天前
HTTPX vs Requests vs AIOHTTP特征和性能对比指南
httpx·requests·aiohttp
SunnyRivers2 天前
Python 的下一代 HTTP 客户端 HTTPX 特性详解
python·httpx
深蓝电商API4 天前
httpx库异步爬虫实战对比aiohttp
爬虫·httpx
qq_381454997 天前
Python httpx:现代HTTP客户端全解析
httpx
曲幽8 天前
FastAPI异步多线程:从踩坑到精通,解锁高性能API的正确姿势
python·flask·fastapi·web·thread·async·httpx·asyncio
SunnyRivers9 天前
Python 中的 HTTP 客户端:Requests、HTTPX 与 AIOHTTP 对比
python·httpx·requests·aiohttp·区别
FITA阿泽要努力17 天前
动手体验:5min实现第一个智能体——1
json·api·agent·requests·查询天气·pprint·f-string
百锦再18 天前
python之路并不一马平川:带你踩坑Pandas
开发语言·python·pandas·pip·requests·tools·mircro
数据知道18 天前
如何使用 httpx + SQLAlchemy 异步高效写入上亿级图片链接与MD5到 PostgreSQL
数据库·postgresql·httpx