news 2026/10/16 23:03:14

Python爬虫入门:urllib请求传参、UA伪装与IP代理配置详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python爬虫入门:urllib请求传参、UA伪装与IP代理配置详解

爬虫学习笔记这个系列,第一篇我主要写的是urllib的安装、urlopen基本用法和read数据的姿势,当时还留了个尾巴,说请求传参、UA伪装、代理这些内容单独开一篇。现在这篇笔记就是把这三个躲不开的问题一次性讲透:请求传参到底应该怎么传、User-Agent为什么一定要设置、IP代理又是怎么挂上去的。对于正在用Python写爬虫、又不太想一上来就上requests库的朋友,这篇应该能帮你把底层逻辑理清楚,看完之后你再回过头看requests,会发现它内部玩的花样,其实全是urllib这套东西。

1. 笔记背景与调试环境准备

1.1 为什么先学urllib

网上大多数爬虫教程一上来就推荐requests,说它比urllib好用不知道多少倍。这话没错,但我个人的学习路径是先把urllib啃下来。原因很简单:requests内部就是对urllib的封装,你把urllib这套Handler、Request、Response的机制搞明白了,requests里那些让人摸不着头脑的session、proxies、headers参数,本质上都是在对相同的底层组件做批量设置。

用urllib写爬虫,代码会稍微啰嗦一点,但每一步都有迹可循。比如你设置一个代理,urllib要求你显式创建ProxyHandler,再通过build_opener构造一个opener,最后用这个opener去open。整个过程非常直白地告诉你:HTTP请求在发出之前,会经过一层层Handler的处理。这种“暴露过程”的学习方式,对建立脑子里的请求模型特别有帮助。

另外一点比较现实:在服务器端看到urllib的默认User-Agent时,几乎所有反爬策略都会直接拉黑。学着设置UA,其实就是在学着理解HTTP请求头的作用,这是后面做任何伪装、破解反爬、模拟浏览器操作的起点。所以这篇笔记写的都是最基础但最根上的东西。

1.2 环境与调试小工具

先说环境。我用的是Python 3.8,urllib是标准库,不需要额外安装,直接import就能用。建议你也在虚拟环境里操作,别把依赖搞乱了。命令行建环境这步略过,不是这篇的重点。

调试工具方面,我强烈建议你准备两个:

  • 一个能显示HTTP请求和响应详情的命令行工具,比如curl,方便做对照实验,看看直接发请求和用Python发请求的差异在哪儿。
  • 一个能回显请求信息的公共HTTP测试服务。这类服务收到你的请求后,会把请求头、请求参数、来源IP原封不动地返回给你,是验证传参、UA、代理是否生效的神器。

我在整篇笔记里都会用这类回显服务作为观察窗口,因为爬虫最怕的就是“代码没报错,但抓回来的数据不对”。有了回显服务,你能直接看到服务器视角里的你长什么样,非常直观。

1.3 贯穿全文的模拟场景

为了让内容不那么枯燥,我虚构了一个场景来串联三种技术点:假设有一个提供天气查询的开放接口(模拟项目X),它接受城市名和日期两个参数,返回当日气温。再假设有一个需要提交表单才能登录的留言板(模拟项目Y),用来演示POST传参。

整个笔记就是围绕这两个非常简单的模拟目标展开的。你先学会怎么把参数正确地交给服务器,然后学会怎么让服务器觉得你是个正常访客,最后学会怎么让请求从代理IP发出去。三个环节走下来,一个勉强能用的基础爬虫就出来了。

2. 请求传参的完整实现

2.1 GET传参:urlencode的编码原理

GET传参的本质,就是把参数拼在URL的问号后面。第一个念头当然是字符串拼接:

city = "北京" url = f"http://example.com/weather?city={city}&date=20200503"

如果你真这么干,大概率会得到一个乱七八糟的URL,或者直接被服务器拒绝。我当时第一次跑就懵了,URL看起来没什么问题,为什么请求失败?后来用回显服务一看,才发现“北京”这两个字在URL里被原样塞进去了,而URL规范里只允许ASCII字符,中文必须先做百分号编码。

手动百分号编码太蠢,urllib里现成的工具就是urlencode。它接受一个字典,自动把键值对拼接好,同时把所有需要编码的字符做转换:

from urllib.parse import urlencode params = { "city": "北京", "date": "20200503" } query_string = urlencode(params) print(query_string) # 输出:city=%E5%8C%97%E4%BA%AC&date=20200503

city参数变成了一串%E5%8C%97%E4%BA%AC,这就是“北京”两个字的UTF-8编码结果。服务器拿到后会按照同样的规范把它解码回中文。这个过程不用背,知道原理就行,反正urlencode会帮你处理好。

完整请求代码如下:

import urllib.request from urllib.parse import urlencode base_url = "http://example.com/weather?" params = {"city": "北京", "date": "20200503"} url = base_url + urlencode(params) req = urllib.request.Request(url) resp = urllib.request.urlopen(req, timeout=5) print(resp.read().decode("utf-8"))

注意我构造的是Request对象,再交给urlopen。你也可以直接把url字符串传给urlopen,两者都行,但前者是后面设置UA、Cookie的基础,所以建议你从现在就习惯用Request。

2.2 POST传参:bytes与请求头

GET传参适合查询类操作,但登录、提交留言这种改变服务器状态的请求,必须用POST。POST的参数不是放在URL里,而是放在请求体(body)里。

urllib的Request构造方法里有一个data参数,只要给它传了值,请求就会自动变成POST。但这个data必须是bytes类型,不能直接传字符串或字典。所以参数要经历两步转换:先urlencode变成标准查询字符串,再encode成UTF-8编码的bytes。

import urllib.request from urllib.parse import urlencode post_data = urlencode({ "username": "test_user", "message": "hello world" }).encode("utf-8") req = urllib.request.Request( "http://example.com/message", data=post_data ) resp = urllib.request.urlopen(req, timeout=5) print(resp.read().decode("utf-8"))

我为什么一开始总在这步翻车?因为urlencode返回的是str,直接塞给data参数会立刻报TypeError。后来养成了习惯:任何发给data的参数,先检查是不是str,是str就先encode。这个习惯帮我省了很多排查时间。

POST请求还有一个容易被忽略的地方:Content-Type请求头。默认情况下,如果不手动指定,urllib不会帮你声明Content-Type。大部分表单处理程序都默认按application/x-www-form-urlencoded来解析请求体,不声明有时候也能工作,但某些严格的框架会认为请求体格式不合法,直接返回400。所以我一般在构造Request时顺便把这块也补上:

req = urllib.request.Request( "http://example.com/message", data=post_data, headers={ "Content-Type": "application/x-www-form-urlencoded" } )

2.3 传参中的常见坑

这部分是血泪经验,按出现频率排的。

第一个坑:使用urlencode之后,参数的顺序会按照字典的键名排序。如果你对顺序有严格要求,那就别用字典,直接用列表嵌套元组传进去。绝大多数接口对参数顺序不敏感,所以这个坑踩到的人才少。

第二个坑:重复参数的情况。有些接口允许同一个参数名传递多次,比如tags=a&tags=b。字典结构天然不支持这种场景,list of tuples也支持。遇到这种需求,用列表套元组的写法准没错。

第三个坑:请求体编码不一致。前端页面是GBK编码,你强行用UTF-8的encode去传POST,中文可能变成乱码,服务器拿到后一脸懵。判断编码的方式是看目标页面HTML的charset声明,页面上写GBK就用gb2312或gbk编码去encode,最稳妥的办法是直接用cp936这个别名。

这里多说一句:GET和POST不是互相替代的关系,它们有各自的语义。爬虫里你只需要记住:查询类、无副作用请求用GET;提交类、改变数据状态的请求用POST。这也是HTTP协议本身的设计意图,按着这个规矩来,能避开很多服务器端的校验。

3. 请求头伪装:User-Agent的细节

3.1 服务器怎么判断你是“人”还是“程序”

服务器识别爬虫的方法有很多,但第一个也是最廉价的一个就是看User-Agent。这个请求头用来标识客户端的类型,浏览器会带上完整的浏览器版本、操作系统、内核信息,而Python的urllib默认会发送这样一串:

Python-urllib/3.8

任何一个正常的反爬系统,看到这串都会直接拒绝请求,或者返回一个验证页面让你去点选图片。服务器端的逻辑很朴素:真实用户不可能用“Python-urllib”这串字符串去浏览网页,所以见到它就直接当成爬虫处理。

想验证这个说法,用我前面说的回显服务测试一下就知道了。不设置任何请求头直接请求,回显服务显示你User-Agent是Python-urllib;设置了Chrome的UA之后,回显服务看到的就完全是浏览器访客的样子。

UA为什么在一个小地方包含了这么多信息?因为设备型号、操作系统版本、内核版本、浏览器版本都在这一串字符串里编码了。服务器可以用这串信息去适配不同的页面渲染方案,也可以用来做最基本的客户端识别。所以做爬虫的时候,UA是不是真实存在,有没有明显的拼接痕迹,非常容易被识别出来。

3.2 UA的构造与轮换策略

最简单的破解方法,就是从浏览器里复制一份完整的UA字符串,然后用headers参数传进Request对象:

import urllib.request headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36" } req = urllib.request.Request( "http://example.com/weather?city=北京", headers=headers ) resp = urllib.request.urlopen(req, timeout=5) print(resp.read().decode("utf-8"))

这里我直接用字符串拼接URL,没走urlencode,是因为中文在代码里手动拼进去也可以,只是不够严谨,正式应用请回到第二部分的正确姿势。

UA不能一直固定用同一个。当你用同一个UA、同一个IP,高频率去抓数据,服务器很快会分析出“这个UA的访问行为不像人类”。人类的特征是:浏览几个页面停留一阵,再看别的;爬虫的特征是:短时间内获取几十上百个页面,间隔固定甚至没有间隔。

轮换UA的标准做法是准备一个UA列表,每次请求随机选一个:

import random UA_LIST = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/75.0" ] ua = random.choice(UA_LIST)

千万别用随机乱拼的字符串,比如“HackBrowser 9.9.9”这种编造的东西。某些WAF会维护已知爬虫工具的UA名单,也会把明显不存在的UA当成恶意特征。用真实的浏览器UA字符串,才是最好的伪装。

3.3 不只是UA:一份完整的请求头清单

设置完UA,你以为能高枕无忧了,但服务器还可以看其他请求头来猜你。最典型的是Accept-Language。一个正常的浏览器访客,大概率会带简体中文和英语的语言偏好设置;一个Python脚本,默认可能什么都不带,或者用开发者的系统语言。

我在调研中发现,很多反爬系统重点看这几个头是否存在、是否合理:

  • Referer:当前请求从哪个页面跳转过来的,缺少Referer的请求像“凭空出现”,容易触发怀疑。
  • Accept:浏览器能接受的内容类型,一般是text/html、application/xhtml+xml这些。
  • Connection:是否保持连接,浏览器一般带keep-alive。
  • Accept-Language:浏览器界面使用的语言,中文浏览器一般是zh-CN,zh;q=0.9,en;q=0.8。

把这几项统统补上之后,请求头的完整度才像一个真正的浏览器。我通常准备一个默认的headers字典,每次发起请求直接拿来用,不用每次从头手写:

DEFAULT_HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Accept-Encoding": "gzip, deflate", "Connection": "keep-alive" }

有一点要提醒:Accept-Encoding这里有个暗坑。如果你声明了gzip压缩,但urllib默认不会自动解压响应体,你拿到的可能是压缩后的乱码。urllib不像requests那样自带解压功能。所以对新手阶段,我建议先别加Accept-Encoding,或者接受之后手动用gzip模块去解。不然又多了新的东西要处理。

还有一点是Cookie。Cookie是维持登录状态的核心,但写起来内容比较长,而且跟登录流程强相关,我这里不展开,后续笔记会专门写Cookie和Session。这里先提一句:如果你已经通过浏览器登录过某个网站,可以把浏览器里的Cookie字符串直接复制到headers里,伪装成已登录用户去发起请求。很多返回数据的变化,根源就是缺了这步。

4. IP代理的实现与代理池搭建

4.1 代理的工作方式与适用场景

IP代理,在爬虫领域通常指的是正向代理。客户端不直接连目标服务器,而是先把请求发给代理服务器,由代理服务器转发给目标服务器,目标服务器拿到的来源IP是代理IP,不是你本机的IP。

它的作用在爬虫里很明确:当你用同一个IP高频抓取数据时,服务器会从IP维度做频率统计,超了就拒绝响应甚至封IP。代理把请求分散到多个IP上,每个IP的访问频率都降下来了,被限制的概率也就低了。另外一个作用是把本机真实IP藏起来,减少个人信息暴露。

需要说明一下:我这里说的代理,解决的是高频请求下的身份与频率问题,是常规爬虫开发里的正当需求。使用代理去触犯他人服务条款或做违反法律法规的访问行为,不在这篇笔记的讨论范围内。

4.2 ProxyHandler与opener的配合

urllib设置代理的方式比较独特,它引入了Handler和Opener的概念。前面的基础用法中,urlopen是一个快捷函数,它背后其实是使用默认的OpenerDirector去执行了一次open。而build_opener允许你替换或追加Handler,然后把自定义逻辑打包成一个opener对象。

设置代理的代码很简单:

import urllib.request proxy = { "http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080" } handler = urllib.request.ProxyHandler(proxy) opener = urllib.request.build_opener(handler) resp = opener.open("http://example.com/weather?city=北京", timeout=5)

这里的代理格式是协议: 代理服务器地址。字典里的http和https两个key,分别对应不同协议的目标请求走哪个代理。如果代理不需要用户名密码,上面这种写法就够了。需要认证的代理,把地址改成这样:

http://username:password@127.0.0.1:8080

看到build_opener是不是有点懵?我当时也懵。换个角度理解:Opener就是你的“请求执行器”,Handler列表就是执行器的零件包。你往零件包里塞了代理处理器,执行器干活的时候就会先把请求转交给代理处理器处理,再发出去。想去掉代理,就用不带ProxyHandler的opener,或者用一个空ProxyHandler替换掉本来设置的那个。

实际上,urllib的urlopen本身是支持一个空代理配置来屏蔽环境变量的,这里不深究,知道opener是更灵活的用法就够用了。

4.3 免费代理池的搭建与质量检测

代理的坑比UA深多了,尤其是免费代理。免费代理的来源我没法放具体站点,你可以自己去搜“免费代理列表”这种关键词,找到一些会实时更新代理IP的列表页。

但免费代理的质量参差不齐,十个里面有八个活不过一下午,剩下两个还可能速度极慢。所以爬虫里常用套路是:抓一批代理列表存下来,然后自己写个探测器,一个个测试可用性,把能用的留下来放进代理池。

探测器的核心思路特别简单:拿这个代理去请求回显服务,如果请求成功并且返回数据里的来源IP恰好是这个代理IP,说明代理生效了。再加上一个超时时间,超过3秒或者5秒就判定不可用:

import urllib.request import socket def test_proxy(ip, port, protocol="http"): proxy_url = f"{protocol}://{ip}:{port}" handler = urllib.request.ProxyHandler({ "http": proxy_url, "https": proxy_url }) opener = urllib.request.build_opener(handler) try: # 这里用一个短超时,太慢的直接淘汰 resp = opener.open("http://example.com/ip", timeout=5) if resp.status == 200: text = resp.read().decode("utf-8") print(f"{proxy_url} 可用,回显IP:{text}") return True except Exception as e: print(f"{proxy_url} 不可用,{e}") return False

这个探测器写的代码大概长这样。实际测的时候你会发现,返回状态码200不代表一切正常,因为有些代理会偷偷篡改响应,或者返回一个验证页面让你填验证码。所以更可靠的判定方式是:看回显服务返回的IP是不是你测试的那个代理IP,是才是真正生效。

免费代理池的维护是个体力活,我建议你在爬虫脚本里加一个循环:每隔几十秒重新扫描一批代理,把失效的踢出去,把新的加进来。这样至少能保证代理池不彻底断供。

4.4 代理踩坑与策略

代理这块的坑,我列几个特别典型的。

第一个是代理类型不匹配。HTTP代理只能代理HTTP协议的请求,你拿HTTP代理去请求HTTPS的站点,经常会遇到SSL握手失败或者连接被重置的报错。严谨点的做法是,爬虫脚本里把http和https的代理分开配置,测代理的时候也分协议测。

第二个是代理返回验证页。好一点的代理商会直接转发原始内容,烂一点的代理会在你请求的页面上插入自己的广告。这种“污染”很恶心,你是看不出来的。解决办法是在解析阶段做内容校验,如果发现页面结构和预期不符,就换下一个代理重试。

第三个是免费代理几乎都被用烂了。同一个免费代理IP可能同时被几十个人用,访问量一高,目标服务器早就把这个IP拉黑了,你拿来用也访问不到目标。这就导致免费代理的成功率极低。应对策略就是加大样本量,批量采集几百个代理轮流试,总能遇到几个还能用的。要是经济允许,直接买商业代理服务,稳定性完全不是免费代理能比的。

另外还有个小技巧:代理不要全局使用。有些请求走公共API或者回显服务,根本不需要代理,套上代理反而慢一大截。我通常用opener来区分:需要代理的请求走代理opener,不需要的走带UA的默认opener,两者互不干扰。

5. 高频问题与排查方法速查

5.1 抓包对比与代码定位

写爬虫的时候,代码报错反而不是最难排查的,因为堆栈信息会直接告诉你哪一行出了问题。真正难的是代码不报错,但返回结果不对:比如返回了一个意料之外的HTML页面、一段JSON、一个验证码弹窗。

我排这种问题的固定流程是三步:先在浏览器里手动打开目标页面,确认正常情况下应该长什么样。然后打开开发者工具的网络面板,看浏览器发出的请求头、请求体和参数长什么样。最后把代码里的请求方式和浏览器里的做对比,找差异。

这里强烈建议用回显服务来做对照实验,它会把你发的请求完全展开给你看。如果你在回显服务里发的请求跟浏览器请求基本一致,说明代码没问题,问题出在目标服务器的反爬策略上。如果差异很大,那就继续调代码。

开发者工具里面“复制为cURL”的功能也很常用,能把浏览器当前请求完整转成一条cURL命令,你拿到命令行里执行一下,再看它跟代码版差在什么地方。这种对比做多了,对HTTP请求的理解会突飞猛进。

5.2 常见报错速查表

把我自己实战中撞见过的报错整理成检阅用的速查表:

现象可能原因解决方向
HTTP Error 403缺少UA或UA被识别为爬虫设置完整浏览器请求头,检查IP是否被限制
HTTP Error 429请求频率太高触发限流降低抓取频率,换代理,加随机延时
HTTP Error 503服务器过载或主动拒绝等一段时间再试,检查IP是否被封
URLError: <urlopen error [WinError 10054]>对方主动断开连接重试,换代理,检查是否触发反爬
参数传了但服务器不识别GET参数没编码 / POST没转bytes用urlencode处理,data参数记得encode
中文变乱码编码格式不匹配检查目标页面charset,统一编码
返回内容与浏览器不一致缺少Referer或者Cookie补齐请求头,带上已登录Cookie
SSL certificate verify failedHTTPS证书校验失败临时用context忽略校验,但生产环境不建议

最后一行提到SSL证书校验问题,简单解释一句:urllib默认会校验证书,碰到某些自签名证书或者公司内部系统,会直接中断请求。测试阶段可以用SSLContext临时跳过校验,但正式爬虫别这么干,风险很大。这个属于进阶用法,知道有这回事就行。

5.3 爬虫的边界意识

技术能力归技术能力,对待目标网站的态度决定了你能走多远。我自己的习惯是,开工之前先看一眼对方的robots.txt,尽量爬那些明确允许抓取的内容,也严格遵守对方公开接口的使用频率限制。抓取公开数据不算什么见不得人的事,但任何事情一旦把对方的服务器拖垮,性质就变了。

控制频率的方法很简单:每次请求之间加一个随机延时,比如time.sleep(random.uniform(1, 3))。这既降低了服务器压力,也让你的访问模式更接近人类。高并发抓取并不是爬虫能力的体现,能稳定、克制地拿回数据,才是真正考验架构水平的地方。

把这些边界想清楚,再回头去看IP代理、UA轮换这些技能,你就知道它们存在的意义是“让请求更规范地分散负载”,而不是为了搞破坏。这个认知摆正了,后面学任何爬虫进阶技术都不会跑偏。


整理这篇笔记时我最大的感受是:urllib虽然写起来啰嗦,但它把HTTP客户端的那套流程完全暴露在你面前,请求头、参数处理、代理配置这些概念在urllib里都有清清楚楚的对应步骤。等你把这篇笔记里的例子全部跑通,再去看requests库的文档,你会发现大量概念瞬间就对上了号。

我实操中还养成了一个比较受用的习惯:笔记本里专门有一个“报错速查”分区,每次撞见新报错就把它连同上下文抄进去。现在很多问题我都不用去搜索,直接翻自己的笔记就能找到答案。如果这篇笔记对你有用,下一步我准备把Cookie、Session和登录态保持这块写完,这也是爬虫进阶最绕不开的下一道坎。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 23:01:47

Gurobi求解器部署与调优实战:从安装许可到回调进阶

简介&#xff1a;这份资源面向需要数学规划与优化求解的科研人员、工程技术人员及高校学生&#xff0c;提供Gurobi求解器的软件下载包&#xff0c;可用于线性规划、混合整数规划、二次规划等建模求解场景&#xff0c;帮助解决学术研究、课程实验与工程优化中的计算需求。压缩包…

作者头像 李华
网站建设 2026/10/16 23:01:36

Android蓝牙开发避坑指南:从权限适配到稳定通信全链路

简介&#xff1a;本资源是一个面向Android开发初学者与中级工程师的蓝牙通信实战Demo&#xff0c;聚焦经典蓝牙&#xff08;SPP&#xff09;场景下的设备发现、配对连接、消息收发等核心功能实现&#xff0c;有效解决移动端短距离无线通信的入门实践难题。压缩包共1544个文件&a…

作者头像 李华
网站建设 2026/10/16 23:00:39

从朴素贝叶斯到N-gram:构建语言模型的概率方法论

作为 NLP 系列的第 5 篇&#xff0c;我想聊一个承上启下的关键转折&#xff1a;从朴素贝叶斯到 N-gram 语言模型。前面我们用朴素贝叶斯做文本分类&#xff0c;靠的是“词袋 条件独立”这两个假设&#xff1b;但一旦你想让模型理解词序、生成自然句子&#xff0c;或者给一句话…

作者头像 李华
网站建设 2026/10/16 23:00:31

DeepSeek提示词实战指南:从复制模板到写出高质量Prompt

简介&#xff1a;《DeepSeek高级应用指南》是一份面向AI工具新手与职场效率提升者的提示词实战手册。内容围绕职场、自媒体、电商、程序员、副业、个人成长等常见场景&#xff0c;整理出50组可直接套用的高阶提示词&#xff0c;帮助读者摆脱“不会提问、回答泛泛”的困境&#…

作者头像 李华
网站建设 2026/10/16 23:00:11

成为AI工程师:从数学基础到模型部署的完整技能路线图

1. 先理清&#xff1a;人工智能工程师到底在忙什么这几年经常有人问我&#xff1a;想成为人工智能工程师&#xff0c;到底要先学什么&#xff1f;这个问题听起来简单&#xff0c;回答起来却非常费劲。AI工程师不是一个只写模型的岗位&#xff0c;它更像一个连接算法、数据、业务…

作者头像 李华
网站建设 2026/10/16 23:00:06

从requests到BeautifulSoup:豆瓣Top250电影爬虫实战

爬取豆瓣评分前50的电影信息&#xff0c;这个需求我太熟了。前阵子帮一位朋友做可视化课程作业&#xff0c;他要的就是这个——把豆瓣评分最高的前50部电影整理成表格&#xff0c;再画个评分分布图。我一听&#xff0c;这事看着简单&#xff0c;真做起来全是坑。豆瓣的反爬不是…

作者头像 李华