拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Python爬虫实战:51job招聘数据采集与岗位分析

简介这是一份面向Python初学者与爬虫实践者的多场景实战代码合集聚焦招聘、影视、电商、婚恋及社交平台等真实数据采集需求帮助学习者掌握requestsBeautifulSoup等主流技术栈的工程化应用。压缩包共6个Python源文件总大小仅9KB轻量易读涵盖51job岗位信息与技能要求爬取、猫眼电影TOP100评分与票房数据抓取、什么值得买商品评价分析、我主良缘用户画像采集、百度贴吧图片批量下载等典型任务每个脚本均体现请求伪装、HTML解析、数据清洗与结构化存储等关键环节。目前已有591人学习下载代码逻辑清晰、注释充分适合作为爬虫入门项目参考、课堂实验素材或求职作品集基础组件可快速复现并拓展至其他垂直网站。 我的秋招季结束之后帮一个学弟做技术岗位调研他问了我一个特别朴素的问题怎么才能知道现在市场上到底要什么技术栈、给多少钱手工一个个翻招聘网站显然不现实于是我又把51job这个老牌招聘网站拿出来折腾了一遍。其实51job的数据接口相对友好反爬强度适中作为Python爬虫练手和真实数据采集的目标都很合适。这篇博文就把完整的采集方案拆开讲清楚从接口分析到数据落地包括我踩过的坑全部记录下来。Python爬虫实战51job招聘数据采集与岗位分析1. 为什么选51job作为爬虫练手目标在开始写代码之前先说说为什么我挑了51job而不是拉勾网、BOSS直聘或者智联招聘。这个选择本身就有讲究选对了目标爬虫项目的成功率会高很多。首先是入库门槛。51job的职位详情页和搜索列表页数据主要在服务端渲染后以JSON格式返回前端页面本身只是一个壳。这意味着我们不需要处理复杂的动态渲染逻辑直接用requests库就能拿到干净的结构化数据。相比之下有些招聘网站的数据是经过前端JS加密后异步加载的需要额外处理__signature、sessionid之类的加密参数对小项目来说成本偏高。其次是反爬策略的宽容度。51job的访问频率限制没有那么敏感只要控制好请求节奏基本不会触发滑块验证或者封禁IP。它的主要防线是Cookie校验和请求头校验这一点对于初学者来说非常友好。你可以用它来练习如何模拟真实浏览器请求这一核心技能。第三个原因是数据质量。51job上岗位信息包含职位名称、公司名称、薪资范围、工作地点、学历要求、经验要求、发布日期、职位描述等字段结构非常规整。有了这些字段你后面做数据分析、薪资统计、技能要求词频统计都方便。数据字段规整能省掉大量清洗时间。我个人给爬虫新手的一个建议是第一个爬虫项目不要选太硬的骨头选一个能快速跑通全流程、又能覆盖完整技术链路的网站。51job正好是这样一个标的有登录态管理、有请求头校验、有分页逻辑、有数据清洗难度都在可控范围内。很多人喜欢一上来就挑战抖音、淘宝这种级别的反爬结果卡在签名算法上信心受挫。先把51job跑通理解了requests会话管理、请求头模拟、数据解析这些基本功再去挑战更复杂的网站节奏就对了。2. 环境准备与合规前提动手写代码之前先把环境梳理清楚。这一步看起来简单但有很多细节会直接影响后面的调试效率。2.1 Python环境与依赖库我用的Python版本是3.10实测3.8到3.12都能正常运行没有遇到兼容性问题。核心依赖有四个pip install requests pip install beautifulsoup4 pip install lxml pip install pandas如果你希望保存数据到Excel建议再装一个openpyxl用pandas写Excel会用到它。pip install openpyxl这里特别说一下lxml。BeautifulSoup4的html.parser解析器在解析复杂HTML时性能一般而51job的职位描述部分含有大量标签嵌套使用lxml会稳定很多解析速度也快。安装时如果遇到报错Windows用户可以用pip install lxml直接装预编译的whl包一般不会踩坑。2.2 网络请求的合规边界这一节我不会讲得太细但每个想写爬虫的人都必须建立这个意识。爬虫采集公开招聘信息本身属于常规数据采集但要注意几个边界请求频率要控制在合理范围不要对目标服务器造成压力采集到的数据不要用于商业销售或大规模公开传播如果需要长期稳定采集优先查看网站的robots.txt规则尊重网站的访问限制声明。个人学习、研究、非盈利的项目做好限速和数据脱敏基本是安全的。这也是我整个项目中一以贯之的原则。2.3 Robots协议与请求头模拟先花一分钟看一下51job的robots.txt长什么样浏览器地址栏输入https://www.51job.com/robots.txt你会发现它并没有完全禁止爬虫访问只是对特定路径做了限制。这意味着基本的数据采集是被允许的但依然要控制频率。请求头模拟是另一个关键点。很多入门教程会告诉你加个User-Agent就行但实际上只加User-Agent远远不够。51job的服务器会校验Referer、Accept、Accept-Language等多个头字段。我在实验中发现如果Referer字段缺失或者与实际来源不符服务器大概率会返回302跳转或者直接拒绝服务。下面是我整理的一份基础请求头配置实测下来通过率很高headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, Referer: https://www.51job.com/, Accept: application/json, text/plain, */*, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Connection: keep-alive, }这里有一个容易忽略的细节Accept字段如果没有显式声明application/json服务器可能会返回HTML页面而不是JSON数据导致解析逻辑失效。我第一次调试时就是栽在这一步。3. 接口链路拆解从搜索页到JSON数据的完整过程很多爬虫项目的核心难点不是代码本身而是把请求链路分析清楚。51job的数据加载链路是一条典型的搜索页→职位列表页→JSON接口结构。3.1 请求链路的初步分析在浏览器里打开51job首页输入Python关键词搜索注意观察浏览器开发者工具F12中的Network面板。你会看到页面先后发起了多个请求但真正包含职位数据的并不是初始的HTML请求而是后续的几个AJAX请求。流程大致是这样的用户在搜索框输入关键词并点击搜索后页面先跳到搜索列表页https://we.51job.com/pc/search?keywordPythonsearchType2sortType0然后这个页面内部会调用一个JSON接口来获取职位列表数据。所以我们要做的核心事情是找到这个JSON接口的完整URL、请求方法、请求参数然后用代码模拟调用。3.2 JSON接口的完整参数解析经过抓包51job当前使用的搜索接口地址是https://we.51job.com/api/job/search-pc这个接口使用POST方法请求头里的Content-Type是application/json请求体是一段JSON格式的数据。核心参数如下{ keyword: Python, searchType: 2, sortType: 0, pageIndex: 1, pageSize: 20, jobArea: , salary: , workYear: , degree: , companyType: , companySize: , industry: }各字段含义如下参数含义可选值示例keyword搜索关键词Python、Java、数据分析searchType搜索类型2表示关键词搜索sortType排序方式0表示默认排序pageIndex页码从1开始pageSize每页数量最大50jobArea工作地区如北京、城市代码salary薪资筛选如10-15万workYear经验要求如1-3年degree学历要求如本科companyType公司类型外资、民营等companySize公司规模如1000-5000人industry行业筛选如互联网/电子商务注意jobArea参数在实际使用中不一定传中文可以传城市代码比如010000代表北京020000代表上海。具体城市的编码可以在接口返回的筛选条件字段中提取也可以直接传中文城市名实测。我在实验中发现直接传城市名称是可以被识别的比如jobArea: 上海就能正常过滤。3.3 用浏览器开发者工具定位接口具体操作路径是这样的打开Chrome浏览器进入51job首页按F12打开开发者工具。切到Network面板勾选Fetch/XHR过滤器。在页面上输入关键词Python并搜索。在请求列表中找到search-pc这个请求。点击该请求在Payload标签页可以看到发送的JSON参数在Preview标签页可以看到返回的数据结构。返回结果是一个标准JSON对象核心在resultbody字段下{ resultbody: { job: { items: [ { jobId: xxx, jobName: Python开发工程师, companyName: 某某科技有限公司, salary: 20-40万/年, jobArea: 上海-浦东新区, workYear: 3-4年, degree: 本科, jobType: 全职, jobTags: [Python, Django, MySQL], issueDate: 2024-03-15, jobUrl: https://jobs.51job.com/... } ], total: 1234, pageIndex: 1, pageSize: 20 } } }有了这个结构解析逻辑就非常简单了。items就是职位列表每个元素是一个职位total字段告诉我们总共有多少条记录可以直接算出总页数。3.4 为什么选择直接请求JSON接口而不是解析HTML这里我要专门展开讲一下。很多爬虫教程的做法是直接请求搜索列表页的HTML然后用BeautifulSoup解析页面上的职位卡片。这个方案在51job上也能跑通但有几个劣势第一HTML页面结构不稳定。前端的样式调整、广告位变化、推荐位插入都会改变DOM结构你的解析选择器就需要跟着改。而JSON接口是给前端程序用的字段名一般不会随意变动。第二HTML页面包含大量噪声数据。导航栏、推荐职位、广告模块都会混在页面里你还需要额外过滤。而JSON接口返回的是纯数据不需要过滤。第三分页处理上JSON接口更加直观。你只需要修改pageIndex参数每次请求都返回一个total字段可以精准计算终止条件不需要在HTML中寻找下一页按钮的链接。所以我的建议是凡是页面内部有清晰JSON接口的网站优先直接请求JSONHTML解析作为备用方案。这不仅仅适用于51job也适用于你后续爬取的其他网站。4. 从请求到落地核心爬虫代码的完整实现环境准备和接口分析都就绪了这一节进入正题写完整的爬虫代码。我先把代码分成几个模块分别讲清楚每个模块的作用和设计思路。4.1 初始化会话和请求封装我习惯先创建一个爬虫类把所有的方法封装在类里面这样代码结构清晰后续扩展功能也方便。import requests import json import time import random import pandas as pd from datetime import datetime class JobCrawler: def __init__(self): self.session requests.Session() self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, Referer: https://www.51job.com/, Accept: application/json, text/plain, */*, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Content-Type: application/json, Origin: https://we.51job.com, } self.base_url https://we.51job.com/api/job/search-pc def fetch_page(self, keyword, page_index, page_size20): payload { keyword: keyword, searchType: 2, sortType: 0, pageIndex: page_index, pageSize: page_size, jobArea: , salary: , workYear: , degree: , companyType: , companySize: , industry: } try: resp self.session.post(self.base_url, jsonpayload, headersself.headers, timeout10) resp.raise_for_status() return resp.json() except requests.exceptions.RequestException as e: print(f请求第{page_index}页失败: {e}) return None这里有几个设计细节值得注意requests.Session()会保持一个会话自动管理Cookie。51job的接口在首次访问时会种下一些Cookie字段后续请求带着这些Cookie会更容易通过校验。如果你每次都用requests.post()裸请求就丢失了会话状态碰到需要Cookie校验的接口就会失败。jsonpayload参数会自动把字典序列化成JSON字符串并且自动设置Content-Type: application/json比手动用json.dumps()再强制指定请求头要干净得多。4.2 数据解析与字段清洗拿到JSON数据后我需要把职位字段提取出来同时做一些基本的清洗比如把薪资范围拆开、把空白字段填上默认值。def parse_items(self, data): if not data: return [] try: items data[resultbody][job][items] except KeyError: return [] parsed_list [] for item in items: salary_str item.get(salary, ) salary_min, salary_max self.parse_salary(salary_str) parsed_list.append({ job_id: item.get(jobId), job_name: item.get(jobName, ).strip(), company_name: item.get(companyName, ).strip(), salary_text: salary_str, salary_min: salary_min, salary_max: salary_max, work_area: item.get(jobArea, ).strip(), work_year: item.get(workYear, ).strip(), degree: item.get(degree, ).strip(), job_type: item.get(jobType, ).strip(), job_tags: |.join(item.get(jobTags, [])), issue_date: item.get(issueDate, ).strip(), job_url: item.get(jobUrl, ).strip(), }) return parsed_list staticmethod def parse_salary(salary_str): if not salary_str: return None, None # 处理类似 20-40万/年 或 1.5-2万/月 的格式 import re pattern r([\d.])-([\d.])万/([年月]) match re.search(pattern, salary_str) if match: low float(match.group(1)) high float(match.group(2)) period match.group(3) if period 月: return low, high else: return low / 12, high / 12 return None, None这里说明一下薪资解析的逻辑。51job的薪资格式一般有两种20-40万/年和1.5-2万/月。为了后续做数据分析方便我统一换算成月薪万元。年包就除以12月薪保持原样。如果你对年薪和月薪不敏感也可以保留原始文本看你的分析目标。4.3 多页抓取与随机休眠策略抓取多个页面时我加了一段随机休眠逻辑避免请求频率过高被服务器限流。def crawl_multi_pages(self, keyword, max_pages10): all_items [] for page in range(1, max_pages 1): print(f正在抓取第{page}页关键词: {keyword}) data self.fetch_page(keyword, page, page_size50) items self.parse_items(data) if not items: print(f第{page}页没有数据停止抓取) break all_items.extend(items) # 随机休眠2-4秒 time.sleep(random.uniform(2, 4)) return all_items这里有几个细节值得展开page_size我设置为50这是接口允许的上限。用50而不是默认的20可以用更少的请求拿更多的数据减轻服务器压力。但如果你访问频率本身就比较快建议改回20稳一点。if not items: break这个判断很关键。当请求的页码超过总页数时接口通常会返回空列表或者items字段缺失。这时候继续循环没有意义直接跳出就好。random.uniform(2, 4)是随机休眠2到4秒。为什么要随机而不是固定3秒因为固定间隔容易被服务器识别出自动化特征随机间隔更接近人的操作习惯。这是我爬虫项目里反复验证过的经验。4.4 数据保存到CSV和Excel数据抓下来之后保存成结构化文件才能继续做分析。我用pandas来做数据持久化一步到位。def save_to_csv(self, items, filename): df pd.DataFrame(items) df.to_csv(filename, indexFalse, encodingutf-8-sig) print(f数据已保存至 {filename}共 {len(df)} 条记录) def save_to_excel(self, items, filename): df pd.DataFrame(items) df.to_excel(filename, indexFalse) print(f数据已保存至 {filename}共 {len(df)} 条记录)encodingutf-8-sig是专门给Windows用户用的。如果直接用utf-8生成的CSV用Excel打开时会乱码。utf-8-sig带BOM头Excel能自动识别编码。4.5 完整调用示例if __name__ __main__: crawler JobCrawler() keyword Python result crawler.crawl_multi_pages(keyword, max_pages5) if result: crawler.save_to_csv(result, python_jobs.csv) crawler.save_to_excel(result, python_jobs.xlsx)运行这段代码如果一切正常你会在当前目录下看到python_jobs.csv和python_jobs.xlsx两个文件。打开后就能看到职位名称、公司名称、薪资等结构化数据。这里我建议你先跑5页试试水确认数据正常后再扩到更多页。一上来就抓全部数据万一解析逻辑有问题白跑一堆请求不说还可能触发限流。5. 踩坑实录请求被拒、数据缺失和编码问题的排查链路这一节是从真实调试过程里提炼出来的价值不亚于爬虫代码本身。我把自己在51job爬虫开发中排过的坑完整走一遍方便你在遇到类似问题时知道从哪里入手排查。5.1 第一道坎POST请求返回HTML而不是JSON我第一次写这段代码时直接把headers里的Accept设置成*/*然后发送POST请求本以为会收到JSON结果返回的全是一段HTML源码里面只有页面加载中之类的提示。排查链路是这样的先看状态码返回200说明请求本身没被拒绝。再看看响应内容发现是HTML说明服务器把请求当作普通页面访问处理了没有走到API逻辑。然后检查请求头发现Accept是*/*这个值不会触发JSON响应。把Accept改成application/json, text/plain, */*之后问题解决。后来我又做了一个交叉验证把Content-Type从application/json改成application/x-www-form-urlencoded结果返回参数报错。这说明接口严格依赖Content-Type判断请求体格式。所以请求头里的Accept和Content-Type必须严格匹配接口要求一个字都不能省。5.2 第二道坎第一页正常第二页开始返回空数据这个坑非常隐蔽。我抓第1页时数据正常第2页、第3页开始返回的items列表为空但接口的total字段显示明明有几百条。排查过程先怀疑是页数参数没传对检查了pageIndex从1开始递增没问题。再怀疑是分页大小问题把pageSize从50改成20依然复现。最后我打印了响应完整的JSON发现一个细节接口返回的resultbody.job.items是None而resultbody里多了一个error字段。原来问题出在请求频率上。51job的接口有一个隐藏的限流机制当同一个IP在短时间内请求超过一定次数接口不会明确返回429状态码拒绝你而是返回一个空的items列表同时携带错误提示。因为我的休眠时间是固定的2秒没有随机性所以正好触发了这个机制。解决办法就是我代码里写的random.uniform(2, 4)随机休眠。改成随机间隔后问题消失了。这里我总结一个经验很多网站的反爬机制不是直接拒绝你的请求而是用返回空数据这种软性手段。排查这类问题时别只看状态码要完整打印返回的JSON体观察是否有隐藏的错误字段或异常结构。5.3 第三道坎Cookie过期导致登录态丢失51job的搜索接口在某些情况下需要登录态才能返回完整数据。第一次调试时我用浏览器登录了账号复制了完整Cookie到代码里顺利抓到数据。第二天再运行代码数据又为空了。排查过程先打印响应的状态码返回302。再看响应头发现Location字段指向登录页。这说明接口检测到登录态过期直接把请求重定向到了登录页。解决办法有两个一是重新打开浏览器复制最新的Cookie到代码中二是用requests.Session()先访问首页让服务器种下基础Cookie再带着会话去请求接口。我在代码里用的是第二种方案加了一个初始化方法def init_session(self): self.session.get(https://www.51job.com/, headersself.headers, timeout10) print(初始化会话成功Cookie:, self.session.cookies.get_dict())在__init__里调用这个方法然后再请求搜索接口就不容易遇到302问题了。当然如果服务器要求严格的账号登录这种方案就失效了需要走更复杂的登录流程。但就51job目前的策略来看匿名会话基本够用。5.4 第四道坎职位描述字段缺失在解析职位详情时我发现有的职位条目里jobDescription字段是空的。排查后发现搜索接口返回的列表数据本身就不包含职位描述全文只有职位名称、公司、薪资这些字段。职位描述需要点进详情页在另一个接口中获取。解决方案是如果只需要列表级数据做统计分析直接忽略职位描述字段即可如果需要职位描述做文本分析比如提取技术栈关键词就要额外开发详情页爬虫用jobUrl字段去请求详情页再解析详情页中的描述内容。这个坑也提醒了我一个通用原则列表接口和详情接口往往是分离的列表数据用于展示概览详情数据需要逐一请求。做爬虫架构设计时要提前想清楚自己需要哪些字段避免做无用功。5.5 第五道坎中文编码导致JSON解析报错这个坑最基础但也最常见。Python的requests库默认会根据响应头中的charset信息自动解码。如果服务器返回的编码声明与实际内容不一致就会导致中文乱码甚至json.decoder.JSONDecodeError。我的处理方案是在请求时显式指定编码resp.encoding utf-8 data resp.json()或者使用resp.content.decode(utf-8)先拿到字符串再交给json.loads()处理。两种方式都可以核心是不要让requests猜编码。这五道坎走下来我对51job的接口特性基本摸透了。爬虫项目就是这样大部分时间不是在写代码而是在排查各种意外情况。6. 数据落地之后岗位数据可以怎么用爬虫代码跑通只是第一步数据拿到手之后真正的价值在于分析和应用。这里我分享几个我自己验证过的数据应用方向你可以根据自己的需求选择。6.1 技能需求词频统计画出岗位技能雷达用csv文件里保存的职位描述或者职位标签字段可以统计技术栈关键词的出现频率。先写一个简单的词频统计脚本import pandas as pd from collections import Counter df pd.read_csv(python_jobs.csv, encodingutf-8-sig) tag_counter Counter() def count_tags(tag_str): if isinstance(tag_str, str): for tag in tag_str.split(|): tag_counter[tag.strip()] 1 df[job_tags].apply(count_tags) print(tag_counter.most_common(20))这样可以看到当前Python岗位中哪些技能标签出现频率最高。我跑了一批样本之后排名靠前的无外乎Django、Flask、MySQL、Redis、Linux、Git这些但不同城市之间会有差异。比如上海岗位的AWS相关标签明显比成都多这个信息对求职者选城市有参考价值。6.2 薪资区间分布了解市场定价把薪资字段解析成salary_min和salary_max后可以用pandas做分组统计df_salary df.dropna(subset[salary_min, salary_max]) city_group df_salary.groupby(work_area)[salary_min].agg([mean, count]).sort_values(mean, ascendingFalse) print(city_group.head(10))需要注意这里的work_area字段包含了城市和区县比如上海-浦东新区。如果你要按城市聚合需要先拆分df_salary[city] df_salary[work_area].str.split(-).str[0] city_group df_salary.groupby(city)[salary_min].agg([mean, count]).sort_values(mean, ascendingFalse)这样就能快速看出当前关键词在不同城市的薪资中位数和岗位数量比看招聘网站上的单条信息直观多了。6.3 学历与经验门槛的交叉分析招聘数据还可以做学历要求和经验要求的交叉分析帮你判断自己是否符合目标岗位的门槛。cross_table pd.crosstab(df[degree], df[work_year]) print(cross_table)pd.crosstab可以快速生成一个交叉表行是学历列是经验要求。从这张表里你可以看到本科3-4年经验的岗位数量最多这一组合是当前市场需求最旺盛的区间。这些分析本身不需要高深的技术用pandas的基本功能就能完成。但前提是你有一个干净、结构化的数据集而爬虫代码就是帮你拿到这个数据集的工具。7. 进阶思路从单页爬虫到分布式采集跑通了基础版之后如果你想把这个项目往更深的方向扩展我列几条进阶思路每条都有实际价值。7.1 关键词队列与多线程采集目前的代码一次只能爬一个关键词如果要爬Python、Java、数据分析、前端开发多个关键词可以维护一个关键词队列用ThreadPoolExecutor开多线程。但注意51job的限流机制对请求频率是有感知的建议线程数控制在3到5个以内并且每个线程的休眠时间设置得稍长一点。from concurrent.futures import ThreadPoolExecutor keywords [Python, Java, 数据分析, 前端开发] def crawl_keyword(kw): crawler JobCrawler() crawler.init_session() result crawler.crawl_multi_pages(kw, max_pages10) if result: crawler.save_to_csv(result, f{kw}_jobs.csv) with ThreadPoolExecutor(max_workers3) as executor: executor.map(crawl_keyword, keywords)这里有个细节每个线程都要维护一个独立的JobCrawler实例和独立的Session不然Cookie会串导致校验失败。7.2 代理池与请求重试如果你需要采集的数据量很大单IP很容易触发限流。这时候可以引入代理池。代理池的搭建和维护是一个更复杂的工程但核心逻辑很简单每次请求前随机选取一个代理IP如果请求失败更换下一个代理并重试。需要注意网上免费的代理IP质量参差不齐连接超时和响应慢的情况非常普遍实际使用时要设置较短的超时时间和较多次数的重试。7.3 增量更新与数据监控招聘数据是动态变化的今天看到的岗位明天可能就下架了。如果你想把数据做成一个持续更新的数据库就需要实现增量爬虫。核心思路是每次爬取前先查数据库记录已有的jobId集合新请求到的数据中不在集合内的就是新增岗位用这个方式做定期更新可以实现岗位动态监控。实现起来并不复杂只要把存储从CSV换成SQLite或MySQL再加一个去重判断即可。7.4 详情页扩展采集前面提到搜索接口不返回完整的职位描述。如果你需要分析岗位的具体职责和技术要求就需要额外写详情页爬虫。详情页地址就是jobUrl字段请求后解析HTML。51job的详情页也是服务端渲染的结构用BeautifulSoup可以解析出完整的职位描述再配合关键词匹配提取岗位职责和任职要求两个部分的文本。这个扩展方向适合有自然语言处理需求的项目比如构建岗位技能图谱或者做简历匹配度分析。我自己在实际项目里是分阶段做的第一版只做了列表页采集第二版加了详情页解析第三版才引入了SQLite存储和增量更新。每次只加一个复杂度项目就不会失控。8. 给爬虫初学者的几条实用建议最后分享几条我在多次爬虫实战中总结出来的心得不一定只适用于51job而是普适性较强的经验。8.1 别用print调试接口返回调试接口返回时不要只print一个resp.json()然后用肉眼盯屏幕。把返回内容保存成文件再检查效率完全不同。with open(response_sample.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2)然后打开文件用JSON格式化工具查看字段的嵌套结构一目了然。用ensure_asciiFalse可以保留中文不会变成\uxxxx转义序列。8.2 优先处理服务器异常而不是代码异常爬虫运行中出现的很多问题根因不在你的代码而在服务器返回的异常数据。所以写代码时对服务器返回的判断优先级要高于对自己的解析逻辑的判断。先检查status_code、检查data是否为空、检查关键字段是否存在再做后续处理。8.3 日志记录比批量print可靠如果你要爬很多页、持续跑很久强烈建议用日志模块记录运行状态而不是用print。print在程序崩溃后会丢失全部输出日志则能保存到文件中方便回溯排查。import logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(crawler.log, encodingutf-8), logging.StreamHandler() ] )8.4 注意爬虫运行时间的边界爬虫不是跑得越快越好。长时间高频请求对服务器资源有消耗也容易给自己惹麻烦。我把每次采集任务的请求总量控制在500个以内单次运行时间控制在10分钟以内做一个守规矩的采集者。整个51job爬虫项目从接口分析到数据落地我前后花了大概一个周末的时间。难度不高但覆盖了爬虫开发的完整链路包括会话管理、请求头模拟、分页抓取、数据清洗、反爬应对、数据存储和简单分析。如果你把这个流程完整跑通再去看其他网站的爬虫会发现套路其实是相通的。最后再多说一句代码尽量自己敲一遍遇到问题再回来对照这篇文章的排查思路收获会比直接复制运行大得多。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门