链家网信息爬虫实践:从网页抓取到数据存储
本文分享了使用Python爬取链家网二手房信息的实现方法。程序采用模块化设计,包含网页获取、数据解析和存储三个核心模块:通过requests模拟浏览器获取HTML源码,使用lxml的XPath解析房源信息(标题、地址、价格等),并保存为CSV文件。文中特别强调了爬虫伦理,建议设置请求间隔、处理反爬机制(如User-Agent、代理IP)和异常处理。代码实现了分页追加存储功能,并提供了完整的实现示例
最近在学习和实践网络爬虫技术,选择了链家网的二手房信息作为目标数据。通过编写一个Python爬虫程序,我成功实现了自动抓取房源信息并保存到本地CSV文件的功能。今天就来分享一下我的实现思路和代码细节。✨
1、前言📖
链家网作为国内知名的房产信息平台,拥有大量真实可靠的房源数据。对于想要了解房地产市场行情或者进行数据分析的人来说,这些数据非常有价值。📊 不过手动复制粘贴效率极低不建议大家这么做,而使用爬虫技术可以快速、批量地获取这些信息。
在开始之前,需要明确一点:爬虫应当遵守网站的robots.txt协议,合理控制访问频率,避免对目标网站造成过大压力。🚦
2、整体设计🏗️
我的爬虫程序主要分为三个核心模块:
- 网页内容获取模块 - 负责从链家网获取HTML源码 🌐
- 数据解析模块 - 从HTML中提取所需的房源信息 🔍
- 数据存储模块 - 将提取的信息保存到CSV文件 💾
这种模块化设计使得代码结构清晰,便于维护和扩展。
3、各功能模块详解 ⚙️
3.1、网页内容获取
def get_html_from_web(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...'
}
response = requests.get(url, headers=headers)
response.encoding = 'utf-8'
return response.text
这个函数的核心是模拟浏览器访问。通过设置合适的User-Agent头部,可以让请求看起来更像是普通用户的访问,减少被网站反爬虫机制拦截的概率。设置UTF-8编码确保中文字符正确显示。
3.2、 数据解析模块
这是爬虫中最关键也最复杂的部分,需要仔细分析网页结构:
def parse_house_info(html_content):
html = etree.HTML(html_content)
house_items = html.xpath('//ul[contains(@class, "resblock-list-wrapper")]/li[contains(@class, "resblock-list")]')
houses = []
for item in house_items:
# 提取标题、地址、价格等信息
# ...
我使用了lxml库的XPath来定位和提取数据。XPath是一种在XML和HTML文档中查找信息的语言,比正则表达式更直观易用。🎯
在解析过程中,我特别注意了异常处理。因为网页结构可能会变化,或者某些房源信息可能不完整,使用try-except块可以避免程序因个别解析错误而崩溃。⚠️
3. 3、数据存储模块
def save_to_csv(houses, csv_file):
file_exists = os.path.exists(csv_file)
with open(csv_file, 'a', newline='', encoding='utf-8') as f:
fieldnames = ['标题', '地址', '均价', '总价', '详情链接']
writer = csv.DictWriter(f, fieldnames=fieldnames)
if not file_exists:
writer.writeheader()
for house in houses:
writer.writerow(house)
这个函数负责将提取的数据保存到CSV文件。我使用了Python内置的csv模块,它提供了简单易用的接口来处理CSV格式。📋
这里有一个小技巧❤️:我首先检查文件是否已存在,如果不存在则写入表头,如果已存在则直接追加数据。这样设计使得程序可以分多次运行,每次都会在原有文件基础上添加新数据,而不会覆盖之前的内容。🔄
4、主控制流程
主函数的逻辑很清晰:
def get_house_info(url):
# 获取网页内容
html_content = get_html_from_web(url)
# 解析房源信息
houses = parse_house_info(html_content)
# 保存到CSV文件
save_to_csv(houses, 'lianjia_house_info.csv')
程序执行流程是线性的:获取数据 → 解析数据 → 保存数据。在main部分,只需要调用这个函数并传入目标URL即可开始爬取。
5、注意事项 ⚠️
在实际使用这个爬虫时,有几点需要特别注意:
- 遵守爬虫道德:设置合理的请求间隔,避免对目标网站造成过大压力。我通常在请求之间添加1-2秒的延迟。⏱️
- 处理反爬机制:链家网有一定的反爬措施,除了设置User-Agent外,可能还需要处理验证码、IP封禁等问题。可以考虑使用代理IP池或Selenium模拟真实用户行为。🛡️
- 网页结构变化:网站前端可能会改版,导致XPath选择器失效。需要定期检查并更新解析逻辑。🔧
- 数据使用合规:爬取的数据应仅用于个人学习或研究,不得用于商业用途或侵犯他人权益。⚖️
完整的代码已经分享在下面,希望对同样对爬虫技术感兴趣的朋友有所帮助❤️。
# Author : atg
# coding=utf-8
import requests
from lxml import etree
import csv
import os
# 从网络获取数据
def get_html_from_web(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299'
}
response = requests.get(url, headers=headers)
response.encoding = 'utf-8'
return response.text
# 解析房源信息
def parse_house_info(html_content):
html = etree.HTML(html_content)
# 使用更通用的XPath选择器,匹配所有可能的房源列表项
house_items = html.xpath('//ul[contains(@class, "resblock-list-wrapper")]/li[contains(@class, "resblock-list")]')
houses = []
for item in house_items:
try:
# 提取标题和详情链接
title_elem = item.xpath('.//div[contains(@class, "resblock-name")]/h2/a')
if not title_elem:
continue # 如果没有标题元素,跳过此房源
title = title_elem[0].text.strip() if title_elem[0].text else "无标题"
detail_url = title_elem[0].get('href', '')
# 补全URL
if detail_url.startswith('/'):
detail_url = 'https://xm.fang.lianjia.com' + detail_url
# 提取地址信息
location_elem = item.xpath('.//div[contains(@class, "resblock-location")]')
if not location_elem:
full_address = "地址不详"
else:
# 获取区域信息
area_parts = location_elem[0].xpath('./span/text()')
area_parts = [part.strip() for part in area_parts if part.strip()]
# 获取详细地址
address_elem = location_elem[0].xpath('./a')
address = address_elem[0].text.strip() if address_elem and address_elem[0].text else ""
# 完整地址
full_address = ' '.join(area_parts) + ' ' + address if area_parts or address else "地址不详"
# 提取价格信息
price_elem = item.xpath('.//div[contains(@class, "resblock-price")]')
if not price_elem:
avg_price = "价格待定"
total_price = "价格待定"
else:
# 获取均价
avg_price_elem = price_elem[0].xpath('.//span[contains(@class, "number")]')
avg_price = avg_price_elem[0].text.strip() + '元/㎡' if avg_price_elem and avg_price_elem[0].text else "价格待定"
# 获取总价范围
total_price_elem = price_elem[0].xpath('.//div[contains(@class, "second")]')
total_price = total_price_elem[0].text.strip() if total_price_elem and total_price_elem[0].text else "价格待定"
# 保存到列表
houses.append({
'标题': title,
'地址': full_address,
'均价': avg_price,
'总价': total_price,
'详情链接': detail_url
})
except Exception as e:
print(f'解析房源时出错: {e}')
return houses
# 保存信息到CSV文件
def save_to_csv(houses, csv_file):
# 检查文件是否存在
file_exists = os.path.exists(csv_file)
# 写入CSV文件
with open(csv_file, 'a', newline='', encoding='utf-8') as f:
fieldnames = ['标题', '地址', '均价', '总价', '详情链接']
writer = csv.DictWriter(f, fieldnames=fieldnames)
# 如果文件不存在,写入表头
if not file_exists:
writer.writeheader()
# 写入数据
for house in houses:
writer.writerow(house)
# 获取房源信息主函数
def get_house_info(url):
# 如果提供了HTML文件路径,则从本地读取
html_content = get_html_from_web(url)
# 解析房源信息
print('正在解析房源信息...')
houses = parse_house_info(html_content)
print(f'共解析到 {len(houses)} 条房源信息')
# 保存到CSV文件
csv_file = 'lianjia_house_info.csv'
save_to_csv(houses, csv_file)
print(f'房源信息已保存到 {csv_file}')
if __name__ == '__main__':
url = 'https://xm.fang.lianjia.com/loupan/'
get_house_info(url)
更多推荐
所有评论(0)