1. 项目背景与需求分析
最近在帮一位刚来扬州工作的朋友找人才公寓时,发现扬州人才公寓官网的信息浏览起来不太方便。每次查看不同区域的房源都需要手动翻页,而且无法快速对比各个公寓的剩余房间数。作为一个经常和数据打交道的开发者,我决定写一个自动化脚本来解决这个问题。
这个Python脚本的核心功能是通过Selenium实现浏览器自动化操作,从扬州人才公寓官网(https://www.yzrcgy.com/#)爬取公寓信息,包括图片地址、公寓名称、详细地址和剩余房间数等关键数据,最后将这些信息整理保存到Excel文件中。相比手动操作,这个脚本可以:
- 自动翻页获取多页数据
- 快速提取关键信息
- 生成结构化数据表格
- 节省大量重复操作时间
2. 技术选型与环境准备
2.1 为什么选择Selenium
在技术选型上,我对比了几种常见的爬虫方案:
- Requests+BeautifulSoup:适合静态页面,但扬州人才公寓官网有较多动态加载内容
- Scrapy框架:功能强大但学习曲线较陡,对于这个简单项目有点杀鸡用牛刀
- Selenium:能完美模拟浏览器行为,处理动态加载内容,虽然效率略低但最适合本项目
最终选择Selenium的原因在于:
- 网站使用JavaScript动态加载内容
- 需要模拟点击翻页操作
- 可能需要处理登录状态
- 开发调试直观方便
2.2 开发环境搭建
基础环境配置
bash复制# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# 安装核心依赖
pip install selenium openpyxl
ChromeDriver配置
- 查看已安装的Chrome浏览器版本
- 从ChromeDriver官网下载对应版本的驱动
- 将chromedriver.exe放在项目目录或系统PATH包含的目录中
注意:Chrome和ChromeDriver的版本必须匹配,否则会报错。建议使用较新的稳定版本。
3. 核心代码实现解析
3.1 浏览器初始化与配置
python复制from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
# 配置浏览器选项
options = Options()
options.add_argument('--disable-blink-features=AutomationControlled') # 禁用自动化控制提示
options.add_argument('--start-maximized') # 启动时最大化窗口
# 设置无头模式(可选)
# options.add_argument('--headless')
# 初始化浏览器驱动
service = Service(executable_path='chromedriver.exe') # 驱动路径
driver = webdriver.Chrome(service=service, options=options)
这段代码做了几件重要的事情:
- 创建浏览器配置对象,添加了反检测参数
- 设置了窗口最大化(可视化调试时很有用)
- 可以通过取消注释启用无头模式(生产环境推荐)
- 初始化Chrome浏览器实例
3.2 页面访问与元素定位
python复制from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 访问目标网站
driver.get('https://www.yzrcgy.com/#')
# 使用显式等待确保页面加载完成
wait = WebDriverWait(driver, 10)
login_button = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '.logon-button .logon'))
)
login_button.click()
这里有几个关键改进:
- 使用
WebDriverWait替代固定的time.sleep,更智能地等待元素加载 - 通过CSS选择器精准定位登录按钮
- 添加了异常处理机制(实际代码中需要补充)
3.3 数据爬取核心逻辑
python复制def scrape_apartment_data(driver, page_num):
apartment_data = []
# 处理翻页逻辑
if page_num > 1:
next_button = driver.find_element(By.CSS_SELECTOR, '.btn-next')
driver.execute_script("arguments[0].click();", next_button)
WebDriverWait(driver, 10).until(
EC.staleness_of(next_button)
)
# 获取当前页所有公寓项
items = driver.find_elements(By.CSS_SELECTOR, '.house-item')
for item in items:
try:
data = {
'图片地址': item.find_element(By.CSS_SELECTOR, '.house-img').get_attribute('src'),
'公寓名称': item.find_element(By.CSS_SELECTOR, '.house-site').text,
'区域信息': item.find_elements(By.CSS_SELECTOR, '.house-site-info')[0].text,
'详细地址': item.find_elements(By.CSS_SELECTOR, '.house-site-info')[1].text,
'剩余房间': item.find_element(By.CSS_SELECTOR, '.red-num').text,
'爬取时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
apartment_data.append(data)
except Exception as e:
print(f'Error scraping item: {e}')
continue
return apartment_data
这个核心函数实现了:
- 智能翻页处理,使用JavaScript直接点击避免元素拦截
- 结构化数据提取,包含所有关键字段
- 自动记录爬取时间戳
- 完善的异常处理,单条数据出错不影响整体
3.4 数据存储实现
python复制from openpyxl import Workbook
from datetime import datetime
def save_to_excel(data, filename):
wb = Workbook()
ws = wb.active
ws.title = "人才公寓数据"
# 设置表头
headers = ["图片地址", "公寓名称", "区域信息", "详细地址", "剩余房间", "爬取时间"]
ws.append(headers)
# 设置列宽
ws.column_dimensions['A'].width = 30
ws.column_dimensions['B'].width = 20
ws.column_dimensions['C'].width = 25
ws.column_dimensions['D'].width = 40
ws.column_dimensions['E'].width = 15
ws.column_dimensions['F'].width = 20
# 写入数据
for item in data:
ws.append([
item['图片地址'],
item['公寓名称'],
item['区域信息'],
item['详细地址'],
item['剩余房间'],
item['爬取时间']
])
# 保存文件
filename = f"扬州人才公寓_{datetime.now().strftime('%Y%m%d_%H%M')}.xlsx"
wb.save(filename)
print(f"数据已保存到 {filename}")
存储模块的特色功能:
- 动态生成包含时间戳的文件名
- 优化了Excel列宽提升可读性
- 完整的字段映射和格式化
- 清晰的保存确认提示
4. 高级技巧与优化方案
4.1 反反爬虫策略
在实际运行中,我发现网站有一些基本的反爬措施,以下是应对方案:
python复制# 在浏览器初始化时添加
options.add_argument('--disable-blink-features=AutomationControlled')
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
# 随机延迟函数
import random
import time
def random_delay(min=1, max=3):
time.sleep(random.uniform(min, max))
# 使用示例
random_delay() # 在关键操作间插入随机延迟
4.2 登录状态保持
如果需要登录才能查看完整信息,可以使用以下方案:
python复制def login(driver, username, password):
driver.get('https://www.yzrcgy.com/#/login')
# 等待登录表单加载
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, 'username'))
)
# 填写登录信息
driver.find_element(By.ID, 'username').send_keys(username)
driver.find_element(By.ID, 'password').send_keys(password)
# 点击登录按钮
driver.find_element(By.CSS_SELECTOR, '.login-btn').click()
# 验证登录成功
WebDriverWait(driver, 10).until(
EC.url_contains('dashboard')
)
# 保存cookies以便复用
cookies = driver.get_cookies()
return cookies
4.3 性能优化技巧
- 启用缓存:减少重复请求
python复制options.add_argument("--disk-cache-dir=./selenium-cache")
options.add_argument("--disk-cache-size=104857600") # 100MB缓存
- 禁用图片加载:提升爬取速度
python复制prefs = {"profile.managed_default_content_settings.images": 2}
options.add_experimental_option("prefs", prefs)
- 并行处理:使用多线程爬取不同页面
python复制from concurrent.futures import ThreadPoolExecutor
def scrape_page(page_num):
# 每个线程需要独立的driver实例
driver = init_driver()
try:
return scrape_apartment_data(driver, page_num)
finally:
driver.quit()
with ThreadPoolExecutor(max_workers=3) as executor:
results = list(executor.map(scrape_page, range(1, total_pages+1)))
5. 常见问题与解决方案
5.1 元素定位失败问题
问题现象:
code复制NoSuchElementException: Unable to locate element: .house-item
解决方案:
- 检查网页结构是否变化,更新CSS选择器
- 增加等待时间
- 尝试其他定位方式(XPath、ID等)
- 确认是否在iframe中需要切换
python复制# 示例:使用XPath作为备选方案
try:
element = driver.find_element(By.CSS_SELECTOR, '.house-item')
except NoSuchElementException:
element = driver.find_element(By.XPATH, '//div[contains(@class,"house-item")]')
5.2 验证码处理方案
应对策略:
- 手动输入(开发阶段)
- 使用第三方验证码识别服务
- 降低请求频率避免触发
- 维护cookie池
python复制# 手动处理验证码示例
captcha = input("请输入页面上显示的验证码:")
driver.find_element(By.ID, 'captcha').send_keys(captcha)
5.3 数据不完整问题
排查步骤:
- 检查网络请求是否完整
- 确认页面是否完全加载
- 验证元素选择器是否准确
- 查看是否有动态加载内容
python复制# 滚动到元素位置确保可见
element = driver.find_element(By.CSS_SELECTOR, '.house-item')
driver.execute_script("arguments[0].scrollIntoView();", element)
6. 项目扩展与进阶方向
6.1 数据可视化分析
爬取的数据可以进一步分析:
python复制import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_excel('扬州人才公寓信息.xlsx')
# 各区域房源数量分析
district_counts = df['区域信息'].value_counts()
district_counts.plot(kind='bar', title='各区域人才公寓数量分布')
plt.savefig('区域分布.png')
6.2 自动化监控系统
可以扩展为定期监控系统:
python复制import schedule
import time
def job():
data = scrape_all_pages()
save_to_excel(data)
send_email_notification()
# 每天上午9点执行
schedule.every().day.at("09:00").do(job)
while True:
schedule.run_pending()
time.sleep(60)
6.3 集成到Web应用
使用Flask创建简单查询界面:
python复制from flask import Flask, render_template
import pandas as pd
app = Flask(__name__)
@app.route('/')
def index():
df = pd.read_excel('扬州人才公寓信息.xlsx')
return render_template('index.html', apartments=df.to_dict('records'))
这个Selenium爬虫项目从最初的简单脚本,经过不断优化已经发展成为一个功能完善的自动化数据采集解决方案。在实际使用中,建议根据具体需求选择合适的优化方案,平衡开发效率和运行性能。