最近在学习和实践网络爬虫技术,选择了链家网的二手房信息作为目标数据。通过编写一个Python爬虫程序,我成功实现了自动抓取房源信息并保存到本地CSV文件的功能。今天就来分享一下我的实现思路和代码细节。✨

1、前言📖

        链家网作为国内知名的房产信息平台,拥有大量真实可靠的房源数据。对于想要了解房地产市场行情或者进行数据分析的人来说,这些数据非常有价值。📊 不过手动复制粘贴效率极低不建议大家这么做,而使用爬虫技术可以快速、批量地获取这些信息。

在开始之前,需要明确一点:爬虫应当遵守网站的robots.txt协议合理控制访问频率,避免对目标网站造成过大压力。🚦


2、整体设计🏗️

我的爬虫程序主要分为三个核心模块:

  1. 网页内容获取模块 - 负责从链家网获取HTML源码 🌐
  2. 数据解析模块 - 从HTML中提取所需的房源信息 🔍
  3. 数据存储模块 - 将提取的信息保存到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. 遵守爬虫道德:设置合理的请求间隔,避免对目标网站造成过大压力。我通常在请求之间添加1-2秒的延迟。⏱️
  2. 处理反爬机制:链家网有一定的反爬措施,除了设置User-Agent外,可能还需要处理验证码、IP封禁等问题。可以考虑使用代理IP池或Selenium模拟真实用户行为。🛡️
  3. 网页结构变化:网站前端可能会改版,导致XPath选择器失效。需要定期检查并更新解析逻辑。🔧
  4. 数据使用合规:爬取的数据应仅用于个人学习或研究,不得用于商业用途或侵犯他人权益。⚖️

完整的代码已经分享在下面,希望对同样对爬虫技术感兴趣的朋友有所帮助❤️。

# 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)

Logo

更多推荐