☰
【Python】基础语法入门(二十五)—— 网络编程入门:用 socket 与 requests 联网
2026/10/29 10:47:10 网站建设 项目流程

一、为什么要学网络编程

回想一下你日常用电脑和手机做的事情:

  • 打开天气 App,看今天多少度;
  • 在浏览器里下载一个安装包;
  • 用 Python 调用 ChatGPT 的 API 自动生成文案;
  • 写个爬虫把豆瓣电影 Top250 抓下来存到数据库。

这些操作有一个共同点:数据不在你本地,而在远端的服务器上。程序要拿到这些数据,就必须通过网络和服务器"对话"。网络编程,就是教你的程序如何:

  1. 发起请求:告诉服务器"我要什么";
  2. 接收响应:把服务器返回的数据读进来;
  3. 解析数据:把字节流变成我们能用的字符串、JSON、图片等。

可以说,现代应用几乎没有不联网的。学会网络编程,你才能写出真正"有用"的程序,而不是只会在本地算算1+1。再举几个更贴近开发的例子:你写个脚本每天定时从公司内网 API 拉取销售数据生成报表;你做个钉钉机器人,出 bug 时自动发消息到群里;你训练机器学习模型时从对象存储下载几十 G 的训练数据。这些场景背后全是网络请求。理解了网络通信的原理,你才能在请求失败时知道是 DNS 解析问题、是连接被拒、还是超时,而不是对着一个报错两眼一抹黑。

Python 在网络编程方面非常强大:标准库自带socket、urllib、http等模块,第三方库requests、aiohttp更是把 HTTP 请求简化到一两行代码。本篇我们就从底层到高层,把它们逐一讲清楚。


二、网络通信基础概念

在写代码之前,先搞懂几个最基础的概念,否则后面会一头雾水。

2.1 IP 地址

IP 地址是网络上每台设备的"门牌号"。比如192.168.1.100是你家里路由器分配给电脑的局域网 IP,142.250.190.46是 Google 服务器的公网 IP。程序要和某台机器通信,首先得知道它的 IP。

小知识:127.0.0.1是本机回环地址,也叫localhost,永远指向自己。调试时经常用到。

2.2 端口(Port)

一台机器上可能同时跑很多服务:Web 服务、SSH 服务、数据库服务……光有 IP 还不够,还要知道是哪个"门"进去,这就是端口。端口是一个 0~65535 的数字,常见端口:

  • 80:HTTP
  • 443:HTTPS
  • 22:SSH
  • 3306:MySQL
  • 8080:常用开发端口

所以一个网络通信地址通常写成IP:端口,例如127.0.0.1:8080。

2.3 TCP 与 UDP

这是两个最底层的传输协议:

协议特点适用场景
TCP面向连接、可靠、有序、慢一点网页、文件传输、数据库
UDP无连接、不可靠、可能丢包、快视频直播、DNS 查询、游戏

通俗讲:TCP 像打电话,先拨通建立连接,然后一句句说,对方没听懂还会重说;UDP 像寄明信片,写好地址扔出去就不管了,可能丢、可能乱序,但快。

2.4 HTTP 协议简介

HTTP(超文本传输协议)是建立在 TCP 之上的应用层协议,也是我们日常联网最常打交道的协议。一次 HTTP 通信大致是:

  1. 客户端(你的程序)通过 TCP 连到服务器IP:端口(默认 80);
  2. 发送一个"请求",包含方法(GET/POST)、路径、头信息、正文;
  3. 服务器返回一个"响应",包含状态码(200/404/500)、头信息、正文;
  4. 连接关闭(或保持)。

常见 HTTP 方法:

  • GET:请求数据(最常用);
  • POST:提交数据(表单、JSON);
  • PUT/DELETE:更新/删除资源。

常见状态码:

  • 200成功;301/302跳转;404找不到;403没权限;500服务器内部错误。

理解了这些,下面就能看懂代码了。再补充一个容易混淆的点:我们常说的"客户端/服务端"是相对的角色。你的爬虫对网站来说是客户端,但如果你写了一个本地 Web 服务接收别人的请求,那它就是服务端。一次通信中,主动发起连接的一方是客户端,被动等待并响应的一方是服务端。socket 编程里,服务端要bind+listen+accept,客户端只要connect,就是这个道理。


三、socket:最底层的网络通信

socket(套接字)是操作系统提供的网络通信接口,Python 标准库socket模块对它做了封装。它非常底层,你要自己处理连接、收发字节流,但也最灵活。

3.1 TCP 服务端与客户端完整示例

先写一个 TCP 服务端,它监听本机 9999 端口,收到客户端发来的消息后回一句"已收到"。

服务端server.py:

import socket # 1. 创建一个 IPv4(TCP) 的 socket 对象 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET 表示 IPv4,SOCK_STREAM 表示 TCP # 2. 绑定 IP 和端口('' 表示本机所有网卡) server.bind(('0.0.0.0', 9999)) # 3. 开始监听,参数 5 表示允许最多 5 个连接在队列里等待 server.listen(5) print("服务端启动,等待连接...") while True: # 4. 接受一个连接,返回 (连接对象, 客户端地址) conn, addr = server.accept() print(f"客户端 {addr} 连上来了") while True: # 5. 接收数据,每次最多读 1024 字节 data = conn.recv(1024) if not data: # 收到空数据说明客户端断开了 print(f"客户端 {addr} 断开") break print(f"收到: {data.decode('utf-8')}") # 6. 回复客户端 conn.sendall("已收到你的消息\n".encode('utf-8')) conn.close() # 关闭这个连接

客户端client.py:

import socket # 1. 创建 socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 连接服务端 client.connect(('127.0.0.1', 9999)) # 3. 发送数据(必须是字节,所以要 encode) client.sendall("你好,我是客户端".encode('utf-8')) # 4. 接收回复 response = client.recv(1024) print(f"服务端回复: {response.decode('utf-8')}") # 5. 关闭 client.close()

运行说明:

  1. 先在一个终端运行python server.py,看到"服务端启动,等待连接...";
  2. 再开一个终端运行python client.py;
  3. 客户端输出:服务端回复: 已收到你的消息;
  4. 服务端输出:客户端 ('127.0.0.1', xxxxx) 连上来了和收到: 你好,我是客户端。

注意:send/recv收发的都是字节(bytes),所以字符串要用.encode()编码成字节再发,收到后用.decode()解码成字符串。这是新手最容易忘的一点。

3.2 UDP 示例

UDP 不需要建立连接,直接发就行。

UDP 服务端:

import socket server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # SOCK_DGRAM 表示 UDP server.bind(('0.0.0.0', 9998)) print("UDP 服务端启动...") while True: data, addr = server.recvfrom(1024) # 接收数据,同时拿到对方地址 print(f"来自 {addr}: {data.decode('utf-8')}") server.sendto("UDP回复".encode('utf-8'), addr)

UDP 客户端:

import socket client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client.sendto("Hello UDP".encode('utf-8'), ('127.0.0.1', 9998)) data, _ = client.recvfrom(1024) print(f"收到: {data.decode('utf-8')}")

UDP 没有连接、没有重传,发出去就不管了,所以代码更短,但不可靠。

3.3 socket 常用方法表

方法说明
socket()创建套接字
bind(addr)绑定地址(服务端用)
listen(n)开始监听
accept()接受连接,返回 (conn, addr)
connect(addr)连接服务器(客户端用)
send(data)/sendall(data)发送数据
recv(bufsize)接收数据
sendto(data, addr)UDP 发送
recvfrom(bufsize)UDP 接收
close()关闭
settimeout(t)设置超时时间

3.4 注意事项

  • 收发都是字节,记得encode/decode;
  • recv(1024)一次最多读 1024 字节,大数据要循环读;
  • 服务端要bind端口,客户端一般不用(系统自动分配);
  • 端口被占用会报OSError: [Errno 48] Address already in use,换个端口或加socket.SO_REUSEADDR;
  • 长时间不收发,连接可能被防火墙断掉,记得加settimeout。

socket 太底层,日常开发很少直接用。下面看看标准库提供的更高层工具。


四、urllib:标准库的 HTTP 工具

Python 标准库自带urllib模块,不用安装就能发 HTTP 请求,适合简单场景或没有第三方库的环境。

from urllib import request, parse # GET 请求 url = "http://httpbin.org/get" with request.urlopen(url) as resp: print(resp.status) # 200 print(resp.read().decode('utf-8')) # 响应正文 # POST 请求(带表单数据) data = parse.urlencode({'name': '陈序员', 'age': 18}).encode('utf-8') with request.urlopen("http://httpbin.org/post", data=data) as resp: print(resp.read().decode('utf-8'))

urllib能用,但 API 比较啰嗦,要手动urlencode、手动decode、加 header 要构造Request对象……所以实际开发中,大家几乎都用第三方库requests。urllib这里只作为过渡了解。


五、requests:日常联网首选

requests是 Python 世界最流行的 HTTP 库,号称"为人类设计的 HTTP 库"。它的 API 简洁优雅,几行代码就能完成复杂请求。

5.1 安装

pip install requests

5.2 GET 请求

import requests # 最简单的 GET resp = requests.get("https://httpbin.org/get") print(resp.status_code) # 200 print(resp.text) # 响应文本 # 带 query 参数(自动拼到 URL 后面) params = {'name': 'chen', 'age': 18} resp = requests.get("https://httpbin.org/get", params=params) print(resp.url) # https://httpbin.org/get?name=chen&age=18 # 带请求头 headers = {'User-Agent': 'MyPythonBot/1.0'} resp = requests.get("https://httpbin.org/get", headers=headers) # 设置超时(秒),避免一直卡住 resp = requests.get("https://httpbin.org/get", timeout=5)

运行结果:resp.text会返回一段 JSON 字符串,里面能看到args字段里带着你传的参数。

5.3 POST 请求

import requests # 1. 提交表单数据(application/x-www-form-urlencoded) resp = requests.post("https://httpbin.org/post", data={'username': 'admin', 'pwd': '123'}) print(resp.text) # 2. 提交 JSON(application/json),用 json= 参数 resp = requests.post("https://httpbin.org/post", json={'title': 'hello', 'body': 'world'}) print(resp.text) # 3. 上传文件 files = {'uploadfile': open('test.txt', 'rb')} resp = requests.post("https://httpbin.org/post", files=files) print(resp.status_code)

区分data=和json=:data发的是表单格式,json发的是 JSON 格式。调 API 时大多用json=。

5.4 会话 Session

如果要在多次请求之间保持 Cookie、复用 TCP 连接(更快),就用Session:

import requests s = requests.Session() s.headers.update({'User-Agent': 'MyBot/1.0'}) # 给所有请求统一加 header # 第一次请求登录,Cookie 会自动保存 s.post("https://httpbin.org/cookies/set", data={'token': 'abc123'}) # 后续请求自动带上 Cookie resp = s.get("https://httpbin.org/cookies") print(resp.text) # 能看到 cookies 里有 token=abc123 s.close() # 用完关闭

5.5 响应对象详解

requests.get()返回一个Response对象,常用属性和方法:

属性/方法说明
status_code状态码,如 200、404
text响应正文(字符串,自动猜测编码)
content响应正文(字节,适合下载二进制)
json()把正文当 JSON 解析成字典
headers响应头(字典)
cookies响应 Cookie
url最终请求的 URL(可能经过跳转)
encoding字符编码
raise_for_status()状态码不是 200 就抛异常

示例:

import requests resp = requests.get("https://httpbin.org/json") print(resp.status_code) # 200 print(resp.headers['Content-Type']) # application/json data = resp.json() # 直接拿到字典 print(data['slideshow']['title'])

5.6 下载文件(流式下载大文件)

下载小文件直接resp.content写到文件即可,但下载几个 G 的电影就不能一次性读进内存,要用流式下载:

import requests url = "https://example.com/bigfile.zip" resp = requests.get(url, stream=True) # stream=True 不立即下载正文 with open("bigfile.zip", "wb") as f: for chunk in resp.iter_content(chunk_size=8192): # 每次读 8KB if chunk: f.write(chunk) print("下载完成")

还可以加进度条(配合tqdm):

from tqdm import tqdm total = int(resp.headers.get('content-length', 0)) with open("bigfile.zip", "wb") as f, tqdm(total=total, unit='B', unit_scale=True) as bar: for chunk in resp.iter_content(8192): f.write(chunk) bar.update(len(chunk))

5.7 代理设置

爬虫经常要挂代理:

import requests proxies = { 'http': 'http://127.0.0.1:7890', 'https': 'http://127.0.0.1:7890', } resp = requests.get("https://www.google.com", proxies=proxies, timeout=10) print(resp.status_code)

六、异常处理

网络不稳定,请求随时可能失败。requests提供了清晰的异常体系:

import requests from requests.exceptions import Timeout, ConnectionError, RequestException try: resp = requests.get("https://httpbin.org/get", timeout=3) resp.raise_for_status() # 4xx/5xx 会抛 HTTPError except Timeout: print("请求超时,请检查网络") except ConnectionError: print("连接失败,可能是网址不对或断网") except RequestException as e: print(f"其他请求异常: {e}") else: print("成功:", resp.text[:100])

常用异常:

  • Timeout:超时;
  • ConnectionError:连接失败(DNS 解析失败、拒绝连接等);
  • HTTPError:状态码 4xx/5xx(需raise_for_status()触发);
  • TooManyRedirects:重定向过多;
  • RequestException:所有 requests 异常的基类。

实际开发中,建议总是加 try-except,否则一个网络抖动就让程序崩溃。


七、实战案例:简单的天气查询工具

我们用免费的wttr.in服务(无需注册、无需 key)写一个命令行天气查询工具:

import requests def get_weather(city: str) -> str: """查询指定城市的天气,返回格式化字符串""" url = f"https://wttr.in/{city}" params = {'format': 'j1'} # 返回 JSON try: resp = requests.get(url, params=params, timeout=10) resp.raise_for_status() data = resp.json() except requests.RequestException as e: return f"查询失败: {e}" current = data['current_condition'][0] temp = current['temp_C'] desc = current['weatherDesc'][0]['value'] humidity = current['humidity'] wind = current['windspeedKmph'] return (f"【{city} 天气】\n" f"温度: {temp}°C\n" f"天气: {desc}\n" f"湿度: {humidity}%\n" f"风速: {wind} km/h") if __name__ == '__main__': while True: city = input("请输入城市名(英文,如 Beijing/Shanghai,输入 q 退出): ").strip() if city.lower() == 'q': break print(get_weather(city)) print("-" * 30)

运行示例:

请输入城市名(英文,如 Beijing/Shanghai,输入 q 退出): Beijing 【Beijing 天气】 温度: 18°C 天气: Partly cloudy 湿度: 45% 风速: 12 km/h ------------------------------

这个例子综合用到了 GET 请求、params、timeout、异常处理、JSON 解析,是一个完整的小工具。你可以把它扩展成定时查询、保存历史、发邮件提醒等。比如结合schedule库每天早上 8 点查询一次,把结果写入 SQLite 历史表,温度低于 5 度就调用邮件接口提醒你加衣服——这就从一个脚本变成了一个真正有用的小服务。再进一步,用 FastAPI 把它包成 Web 接口,前端做个页面展示,你就拥有了自己的天气应用。所有这些扩展,底层都离不开本篇学的requests这几行代码。


八、最佳实践

  1. 永远设置 timeout:不设超时的请求可能永远卡住,拖垮整个程序。
  2. 用 Session 复用连接:多次请求同一站点时,Session 能复用 TCP 连接,速度快很多。
  3. 总是处理异常:网络不可靠,try-except 是标配。
  4. 大文件用 stream 流式下载:避免内存爆炸。
  5. 加 User-Agent:很多网站会拒绝默认 UA,伪装成浏览器更稳。
  6. 遵守 robots.txt 和频率限制:爬虫别太猛,加time.sleep,做个有礼貌的爬虫。
  7. JSON 用resp.json(),别手动json.loads(resp.text):前者更安全,编码处理更好。
  8. 敏感信息(token、密码)放 header 或环境变量,别写在 URL 里(URL 会被日志记录)。
  9. HTTPS 优先:HTTP 明文传输,不安全。
  10. 调试时打开日志:import logging; logging.basicConfig(level=logging.DEBUG),能看到请求详情。

九、小结

本篇我们从最底层的socket一路讲到日常最常用的requests:

  • socket是操作系统网络接口的封装,能做 TCP/UDP 通信,但要自己处理字节、连接、收发循环;
  • urllib是标准库的 HTTP 工具,能用但啰嗦;
  • requests是日常首选,GET/POST/Session/文件上传/流式下载/代理一应俱全;
  • 网络请求一定要处理异常、设超时;
  • 实战写了一个天气查询小工具,把所学串了起来。

下一篇我们将进入并发编程的世界——当你要同时下载 100 个网页时,串行一个个下太慢了,得让程序"同时干多件事",敬请期待。


十、练习题 / 思考题

  1. 用socket写一个简单的"聊天室"服务端,支持多个客户端同时连接并互相转发消息(提示:结合下一篇的多线程)。
  2. 用requests调用https://api.github.com/users/你的用户名,打印你的 GitHub 公开信息。
  3. 写一个函数download(url, path),支持断点续传(提示:用Range请求头)。
  4. 用Session模拟登录某个需要 Cookie 的网站(注意遵守对方条款)。
  5. 思考:为什么requests比urllib用起来舒服这么多?它帮你封装了哪些东西?
  6. 挑战:用socket手写一个最简单的 HTTP 客户端,向httpbin.org发 GET 请求并解析响应(体会一下requests帮你省了多少事)。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询