前言:为什么要学习爬虫 在当今数据驱动的时代,获取网络上的招聘信息对于求职者分析市场趋势、企业了解竞争对手薪资水平都非常有价值。本教程将带你从零开始,使用Playwright构建一个能够爬取Boss直聘岗位详情的爬虫程序。
即使你是编程新手,只要跟着本教程一步步操作,也能成功爬取到所需数据!
一、环境准备与安装 1.1 安装Python 首先确保你的电脑上安装了Python(3.7或更高版本)。可以从Python官网 下载安装。
安装完成后,打开终端(Mac/Linux)或命令提示符(Windows),输入以下命令检查是否安装成功:
1 2 3 python --version python3 --version
1.2 安装必要库 我们需要安装几个Python库来支持我们的爬虫:
1 pip install playwright yaml pyyaml
1.3 安装Playwright浏览器 Playwright需要安装浏览器内核才能工作:
1 playwright install chromium
这个过程可能会花费一些时间,因为它需要下载Chromium浏览器。
二、爬虫工作原理 2.1 导入必要的库 1 2 3 4 5 6 7 8 9 10 11 12 import asyncioimport jsonimport loggingimport osimport reimport yamlimport timefrom datetime import datetimefrom typing import List , Dict , Any , Optional from pathlib import Pathfrom playwright.async_api import async_playwright, Browser, Page, BrowserContext
这些库各自的作用:
asyncio
: 用于异步编程,让爬虫可以高效处理多个任务
json
和yaml
: 用于处理配置文件和保存数据
logging
: 用于记录程序运行情况,方便调试
playwright
: 核心库,用于控制浏览器
2.2 配置日志系统 1 2 3 4 5 logging.basicConfig( level=logging.INFO, format ='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger("boss_job_detail_crawler" )
日志可以帮助我们了解程序运行状态,当出现问题时可以快速定位。
2.3 创建爬虫类 1 2 3 4 5 6 7 class BossJobDetailCrawler : def __init__ (self, headless: bool = False ): self.headless = headless self.browser = None self.context = None self.page = None self.playwright = None
2.4 启动浏览器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 async def start (self ): logger.info("启动浏览器..." ) self.playwright = await async_playwright().start() user_data_dir = os.path.expanduser('~/boss_crawler_data' ) os.makedirs(user_data_dir, exist_ok=True ) self.browser = await self.playwright.chromium.launch_persistent_context( user_data_dir=user_data_dir, headless=self.headless, channel="chrome" , args=[ '--disable-blink-features=AutomationControlled' , '--disable-web-security' , '--start-maximized' ], viewport={'width' : 1280 , 'height' : 800 }, user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" )
这里有几个关键点:
launch_persistent_context
使用持久化上下文,可以保存cookies和登录状态
headless=False
表示显示浏览器界面,方便调试
设置用户代理(User-Agent)让请求看起来像来自真实浏览器
2.5 处理登录状态 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 async def ensure_logged_in (self ): logger.info("检查登录状态..." ) await self.page.goto("https://www.zhipin.com" , wait_until="domcontentloaded" ) await asyncio.sleep(3 ) login_indicators = [ 'a[href*="/web/geek/chat"]' , '.nav-figure img' , 'a[ka="header-username"]' , ] for indicator in login_indicators: try : element = await self.page.query_selector(indicator) if element: logger.info("检测到已登录状态" ) return True except : continue logger.info("未检测到登录状态,请手动登录..." ) logger.info("请在浏览器中完成登录,然后按Enter继续" ) input ("按Enter继续..." ) for _ in range (60 ): await asyncio.sleep(5 ) for indicator in login_indicators: try : element = await self.page.query_selector(indicator) if element: logger.info("登录成功!" ) return True except : continue logger.info("等待登录中..." ) logger.error("登录超时" ) return False
这个函数确保我们已经登录Boss直聘,因为未登录状态下无法查看岗位详情。
2.6 提取岗位详情 这是最核心的部分,我们需要从网页中提取所需信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 async def extract_job_details (self, job_url: str ) -> Dict [str , Any ]: logger.info(f"开始提取岗位详情: {job_url} " ) try : await self.page.goto(job_url, wait_until="domcontentloaded" ) await asyncio.sleep(3 ) job_details = { 'url' : job_url, 'title' : await self._extract_text('.name h1' ), 'salary' : await self._extract_text('.salary' ), 'city' : await self._extract_text('.text-city' ), 'experience' : await self._extract_text('.text-experience' ), 'education' : await self._extract_text('.text-degree' ), 'company' : await self._extract_text('.company-info .name' ), 'company_type' : await self._extract_text('.company-info .type' ), 'company_size' : await self._extract_text('.company-info .size' ), 'job_description' : await self._extract_text('.job-sec-text' ), 'extracted_at' : datetime.now().isoformat() } tags = await self._extract_tags() if tags: job_details['tags' ] = tags return job_details except Exception as e: logger.error(f"提取岗位详情失败: {e} " ) screenshot_path = f"error_screenshot_{int (time.time())} .png" await self.page.screenshot(path=screenshot_path) logger.info(f"已保存错误截图: {screenshot_path} " ) return { 'url' : job_url, 'error' : str (e), 'extracted_at' : datetime.now().isoformat() }
2.7 辅助提取方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 async def _extract_text (self, selector: str ) -> str : """提取文本内容""" try : element = await self.page.query_selector(selector) if element: text = await element.text_content() return text.strip() if text else "" except : pass return "" async def _extract_tags (self ) -> List [str ]: """提取标签信息""" tags = [] try : tag_elements = await self.page.query_selector_all('.job-tags span' ) for tag_element in tag_elements: tag_text = await tag_element.text_content() if tag_text and tag_text.strip(): tags.append(tag_text.strip()) except : pass return tags
这两个辅助方法使用CSS选择器来定位和提取页面上的特定元素。
三、运行你的第一个爬虫 具体代码见 boss_crawl 。
3.1 创建配置文件 在代码同一目录下创建job_links.yaml
文件:
1 2 3 4 5 6 7 8 job_links: - https://www.zhipin.com/job_detail/第一个岗位的ID.html - https://www.zhipin.com/job_detail/第二个岗位的ID.html - https://www.zhipin.com/job_detail/第三个岗位的ID.html output: dir: ./job_details format: json
将上面的URL替换为你想爬取的实际岗位链接。
3.2 运行爬虫 保存代码为boss_crawl.py
,然后在终端中运行:
第一次运行时会弹出浏览器窗口,你需要手动登录Boss直聘。登录成功后,按终端中的Enter键,爬虫就会开始工作。
3.3 查看结果 爬虫完成后,会在job_details
目录下生成一个包含时间戳的JSON或CSV文件,里面包含所有爬取到的岗位信息。
四、常见问题与解决方案 4.1 爬虫被检测到怎么办?
调整user-agent
,模拟真实浏览器
增加随机延迟,模拟人类操作
使用代理IP轮换请求
4.2 页面结构变化怎么办? 如果Boss直聘更新了页面结构,你需要更新代码中的CSS选择器。可以使用浏览器开发者工具检查元素,找到新的选择器。
4.3 如何提高爬取效率?
使用异步并发处理多个页面
合理设置请求延迟,既不太快触发反爬,也不太慢影响效率
五、注意事项
遵守Robots协议 :检查Boss直聘的robots.txt文件,尊重网站的爬虫规则
限制爬取频率 :不要过于频繁地请求,以免给网站服务器造成压力
合理使用数据 :爬取的数据仅用于个人学习和研究,不要用于商业用途或侵犯他人隐私
注明数据来源 :如果公开使用这些数据,请注明来自Boss直聘
结语 通过本教程,你学会了如何使用Playwright构建一个完整的爬虫程序,从环境搭建到实际运行,从页面导航到数据提取。这个技能不仅适用于Boss直聘,稍作修改也可以用于其他网站。
爬虫技术是一把双刃剑,希望你能合理使用它,在遵守法律和道德的前提下,获取对你有价值的数据。
如果你在实践过程中遇到任何问题,欢迎在评论区留言,我会尽力解答!