昨日爬虫学习与改造记录 在今天的学习与实践中,我对一个使用 Selenium 进行招聘信息抓取的爬虫脚本进行了深入改造。通过代码优化与问题解决,我对如何设计、优化和调试爬虫有了更深入的理解。本文将详细记录今天的学习过程、遇到的问题以及改进后的代码。
1. 初步爬虫脚本构建 最初的爬虫脚本结构相对简单,主要实现了以下功能:
页面加载与点击 :通过 Selenium 模拟浏览器操作,访问目标招聘网站并点击分类页面。
数据提取 :逐一遍历分类页面,提取每个职位的详细信息,包括职位名称、公司、地点、薪资等,并保存到数据库。
数据库操作 :每获取到一个职位信息,都会连接数据库并插入数据。
初始代码(部分摘录) browser = webdriver.Edge() index_url = 'https://www.zhipin.com/?city=100010000&ka=city-sites-100010000' browser.get(index_url) show_ele = browser.find_element(by=By.XPATH, value='//*[@id="main"]/div/div[1]/div/div[1]/dl[1]/dd/b' ) show_ele.click() today = datetime.date.today().strftime('%Y-%m-%d' ) for i in range (85 ): current_a = browser.find_elements(by=By.XPATH, value='//*[@id="main"]/div/div[1]/div/div[1]/dl[1]/div/ul/li/div/a' )[i] current_category = current_a.find_element(by=By.XPATH, value='../../h4' ).text sub_category = current_a.text print ("{}正在抓取{}--{}" .format (today, current_category, sub_category)) current_a.click() time.sleep(5 ) browser.execute_script("window.scrollTo(0, document.body.scrollHeight);" ) time.sleep(10 ) browser.execute_script("window.scrollTo(0, document.body.scrollHeight);" )
这个代码的问题在于:
页面元素加载问题 :在页面元素尚未完全加载时,Selenium 可能无法找到目标元素,从而导致失败。
StaleElementReferenceException :当页面刷新或 DOM 结构发生变化时,Selenium 引用的元素会失效,导致操作失败。
频繁的数据库连接 :每获取一个职位信息都会新建一次数据库连接,导致效率低下。
手动处理验证码 :当网站检测到异常行为时,可能会触发验证码验证,这会中断爬虫运行。
2. 改进与优化 针对上述问题,我进行了多方面的改进,以下是主要的优化措施和相应的代码改变。
2.1 引入重试机制 为了应对页面元素加载失败或 DOM 变化带来的问题,我增加了一个 retry_on_failure
函数。该函数允许在遇到 StaleElementReferenceException
或 TimeoutException
时,自动重试指定次数,提高脚本的稳定性。
代码示例:
from selenium.common.exceptions import StaleElementReferenceException, TimeoutExceptiondef retry_on_failure (max_retries, function, *args, **kwargs ): retries = 0 while retries < max_retries: try : return function(*args, **kwargs) except (StaleElementReferenceException, TimeoutException) as e: retries += 1 print (f"Error: {e} . Retrying {retries} /{max_retries} ..." ) time.sleep(1 ) raise Exception(f"Failed after {max_retries} retries" )
使用示例:
current_a = retry_on_failure(3 , browser.find_elements, By.XPATH, '//*[@id="main"]/div/div[1]/div/div[1]/dl[1]/div/ul/li/div/a' )[i]
2.2 优化页面跳转与元素定位 为了减少页面跳转带来的定位失效问题,我在点击元素前引入了显式等待,并结合了动作链(ActionChains)来确保元素能够正确点击。这有效减少了 StaleElementReferenceException
发生的概率。
同时,为了避免在复杂的页面场景中可能出现的点击失效问题,我将一些关键点击操作改为使用 JavaScript 强制执行。这种方式在某些情况下比 Selenium 的普通点击操作更为可靠。
代码示例:
from selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.action_chains import ActionChainsshow_ele = WebDriverWait(browser, 10 ).until( EC.element_to_be_clickable((By.XPATH, '//*[@id="main"]/div/div[1]/div/div[1]/dl[1]/dd/b' )) ) actions = ActionChains(browser) actions.move_to_element(show_ele).perform() browser.execute_script("arguments[0].click();" , current_a)
2.3 增加 sleep
等待 为了确保页面元素完全加载,我在一些关键步骤前增加了 sleep
等待。这种方式虽然简单,但在处理需要等待页面完全渲染的场景时非常有效。
代码示例:
time.sleep(5 ) browser.execute_script("window.scrollTo(0, document.body.scrollHeight);" ) time.sleep(10 ) browser.execute_script("window.scrollTo(0, document.body.scrollHeight);" )
2.4 优化数据库连接 为了提高数据库操作的效率,我在程序启动时建立了一次数据库连接,并在整个脚本运行过程中复用该连接。这样做不仅减少了每次操作时的连接开销,还提升了程序的整体性能。
代码示例:
db = DBUtils('数据库地址' , '用户名' , '密码' , '数据库名' ) db.insert_data( "insert into job_info(category, sub_category, job_title, province, job_location, job_company, job_industry, job_finance, job_scale, job_welfare, job_salary_range, job_experience, job_education, job_skills, create_time) values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" , args=(current_category, sub_category, job_title, province, job_location, job_company, job_industry, job_finance, job_scale, job_welfare, job_salary_range, job_experience, job_education, job_skills, today) )
2.5 处理验证码与异常页面 在爬虫运行过程中,有时会遇到页面弹出验证码的情况。为了确保爬虫的持续运行,我增加了一个等待用户手动操作的功能,使得用户可以在需要时手动完成验证,然后继续自动化抓取。
代码示例:
def wait_for_user (): input ("请手动完成操作后在控制台输入 'ok' 继续: " ) wait_for_user()
2.6 日志记录与进度反馈 为了更好地监控爬虫的运行状态,我在脚本中增加了详细的日志输出,每个关键步骤都会记录日志,以便在发生问题时能够快速定位。此外,还增加了进度反馈,使得用户可以清楚地了解脚本的处理进度。
代码示例:
print (f"正在处理第 {i} 个分类(咱程序员喜欢从0开始数你懂得...)" )print (f"{today} 正在抓取 {current_category} -- {sub_category} " )
3. 改造后的爬虫运行效果 经过优化后的爬虫,运行更加稳定高效,处理数据的速度有了明显提升,并且在应对复杂页面场景时表现更佳。以下是改造后脚本的一些显著优点:
稳定性提升 :通过重试机制和显式等待,有效减少了因元素定位失败导致的脚本中断。
效率提升 :通过优化数据库连接和减少页面跳转次数,大幅提高了数据抓取的效率。
更好的用户交互 :增加的日志输出和手动操作提示,使得脚本在自动化与人工干预之间达到了更好的平衡。