ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

05-Python爬虫工程化完善

05-Python爬虫工程化完善 前面4篇学会了发请求、提数据、抓表格、存CSV。能跑通一个小爬虫了但跑多了就会发现跑了半小时最后一页网络超时崩了前面数据全白干请求太快被网站封了 IP页面结构偶尔变一下程序直接报错退出网络不稳定程序卡在那里一动不动今天这篇是数据采集部分的收尾——把前面的知识串起来加上几个关键的防护盾让你的爬虫遇到问题不崩溃、失败之后能重试、被封之前懂克制。今天教五招全是干货。第一招超时设置 timeout爬虫跑着跑着突然不动了也不报错大概率是网络问题——请求发出去了但服务器一直不响应。requests默认没有超时限制会一直等。给它设一个耐心上限importrequests# 最多等10秒超过就报错responserequests.get(https://example.com,timeout10)超时还能分开设置连接超时和读取超时。# 连接超时5秒读取超时10秒responserequests.get(https://example.com,timeout(5,10))新手不用纠结太细统一 10 秒基本够用。第二招异常捕获 try-except光设超时还不够——超时了程序会报错退出。我们希望的是这一页失败了没关系记下来继续爬下一页别整个程序崩了。最常见的几种异常异常类型什么时候触发requests.exceptions.Timeout请求超时requests.exceptions.ConnectionError连不上服务器requests.exceptions.RequestException所有请求异常的老祖宗兜底用importrequeststry:responserequests.get(url,timeout10)response.raise_for_status()# 状态码不是200也会报错print(请求成功)exceptrequests.exceptions.Timeout:print(f请求超时{url})exceptrequests.exceptions.ConnectionError:print(f连接失败{url})exceptrequests.exceptions.RequestExceptionase:print(f请求出错{url}错误信息{e})注意response.raise_for_status()很重要。它检查 HTTP 状态码404、500 这种错误也会抛出异常。不然你拿到一个 404 页面还以为成功了。第三招请求休眠 time.sleep爬虫爬太快服务器压力大而且请求太快容易被识别成爬虫、封 IP。加个休眠既是礼貌也是自保。importrequestsimporttimeforurlinurls:try:responserequests.get(url,timeout10)exceptExceptionase:print(f爬取失败{url}{e})# 每次请求后休眠1秒time.sleep(1)进阶随机休眠更像真人。固定时间也容易被识别——哪有真人每次都精确等 1 秒importrandomimporttime# 随机休眠1到3秒time.sleep(random.uniform(1,3))第四招User-Agent 伪装 请求头完善很多网站检查请求头发现你是python-requests直接就拒了。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://example.com/,# 从哪个页面点过来的Accept:text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8,Accept-Language:zh-CN,zh;q0.9,}responserequests.get(url,headersheaders,timeout10)记住一个原则伪装得越像浏览器越不容易被封。第五招重试机制有时候失败只是暂时的——网络抖一下、服务器刚好忙不过来重试一下说不定就成了。importrequestsimporttimedeffetch_url(url,headers,max_retries3): 带重试的请求函数 max_retries: 最多重试次数 forattemptinrange(1,max_retries1):try:responserequests.get(url,headersheaders,timeout10)response.raise_for_status()returnresponse# 成功就返回exceptrequests.exceptions.RequestExceptionase:print(f第{attempt}次请求失败{e})ifattemptmax_retries:# 重试间隔递增第1次等2秒第2次等4秒wait_timeattempt*2print(f等待{wait_time}秒后重试...)time.sleep(wait_time)else:print(f已重试{max_retries}次最终失败)returnNone三个设计细节最多重试 3 次不能无限重试不然死循环间隔递增越失败越有耐心指数退避的简化版失败返回 None让调用方决定怎么处理失败五招合体完整健壮爬虫模板五招都教完了把它们全部整合成一个可以直接拿去改的完整模板importrequestsimporttimeimportrandomimportcsvfrombs4importBeautifulSoup# 配置区 BASE_URLhttps://example.com/list?page# 目标网站基础URLTOTAL_PAGES10# 总页数OUTPUT_FILEdata.csv# 输出文件名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://example.com/,}MAX_RETRIES3# 最大重试次数SLEEP_MIN1# 最短休眠时间秒SLEEP_MAX3# 最长休眠时间秒TIMEOUT10# 请求超时时间秒# 核心函数 deffetch_page(url):带重试的页面请求函数forattemptinrange(1,MAX_RETRIES1):try:responserequests.get(url,headersHEADERS,timeoutTIMEOUT)response.raise_for_status()response.encodingutf-8returnresponseexceptrequests.exceptions.Timeout:print(f第{attempt}次请求超时{url})exceptrequests.exceptions.ConnectionError:print(f第{attempt}次连接失败{url})exceptrequests.exceptions.RequestExceptionase:print(f第{attempt}次请求异常{e})ifattemptMAX_RETRIES:time.sleep(attempt*2)# 重试间隔递增else:print(f{url}重试{MAX_RETRIES}次后仍失败跳过)returnNonedefparse_data(html_text):解析页面提取数据根据实际网站结构修改soupBeautifulSoup(html_text,html.parser)items[]foriteminsoup.select(.item):items.append({标题:item.select_one(.title).get_text(stripTrue),日期:item.select_one(.date).get_text(stripTrue),})returnitemsdefsave_to_csv(data_list,file_name,modea):保存数据到CSVifnotdata_list:returnfieldnamesdata_list[0].keys()withopen(file_name,mode,newline,encodingutf-8-sig)asf:writercsv.DictWriter(f,fieldnamesfieldnames)ifmodew:writer.writeheader()writer.writerows(data_list)# 主程序 defmain():print(爬虫开始运行)all_data[]failed_pages[]# 记录失败的页面forpageinrange(1,TOTAL_PAGES1):urlBASE_URLstr(page)print(f\n正在爬取第{page}页{url})responsefetch_page(url)ifresponseisNone:failed_pages.append(page)time.sleep(random.uniform(SLEEP_MIN,SLEEP_MAX))continuetry:itemsparse_data(response.text)all_data.extend(items)print(f 提取到{len(items)}条数据)exceptExceptionase:print(f 解析失败{e})failed_pages.append(page)ifpageTOTAL_PAGES:time.sleep(random.uniform(SLEEP_MIN,SLEEP_MAX))# 收尾print(f\n爬取完成成功{TOTAL_PAGES-len(failed_pages)}页失败{len(failed_pages)}页)print(f共获取{len(all_data)}条数据)ifall_data:save_to_csv(all_data,OUTPUT_FILE,modew)print(f数据已保存到{OUTPUT_FILE})else:print(没有获取到任何数据)if__name____main__:main()模板的特点✅ 配置区和代码区分开改参数不用翻代码✅ 每个函数只干一件事清晰好维护✅ 失败的页面会记录方便补爬✅ 有进度提示运行过程不黑盒✅ 最后有统计汇总以后要爬别的网站改改配置区和parse_data函数就行。新手常见错误避坑坑反面教材正确做法try-except 写太宽except: pass只捕获预期异常异常信息打印出来休眠太短/太长0.1秒没加10秒太慢小网站2-3秒大网站1秒左右重试没有次数限制while True设最大重试次数3次够了所有页面同一个UA太明显量大时准备UA列表随机选# UA列表随机选USER_AGENTS[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0,Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Version/17.0,Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0,]headers{User-Agent:random.choice(USER_AGENTS)}真实运行效果五招合体的完整模板用本地模拟数据跑一遍模拟第 2 页第一次请求超时、重试后成功可以看到第 2 页模拟了一次超时→自动重试→成功的完整过程失败的页面被记录最终成功页数、失败页数、总数据量一目了然数据正常写入 CSV。这就是工程化爬虫该有的样子——出问题不崩、失败能重试、收尾有统计。总结到这里数据采集部分的5篇就全部学完了第1篇requests 发请求第2篇BeautifulSoup 提取数据第3篇表格数据抓取第4篇数据存 CSV 翻页第5篇本篇工程化完善让爬虫扛造5篇下来你已经能独立写出一个健壮、不容易崩、懂礼貌的小爬虫了。这比网上那种三行代码爬XX的玩具爬虫实用多了。完整可跑工程版健壮爬虫模板 robust_spider.py在配套资源包里。接下来进入 Pandas 的世界数据怎么清洗、怎么分析、怎么发现价值。梅雅达编程工作室 · Python数据实战系列第5篇五招下来爬虫从能跑变成耐造了。数据采集收尾下一篇进 Pandas 的世界。
返回列表