链家网二手房数据爬虫项目分享

该文章已生成可运行项目,

链家网二手房数据爬虫项目分享

1 写在前面

大家好!今天给同学们分享一个实用的项目——《链家网二手房数据爬虫》。这个项目基于Python语言的lxml库,通过xpath路径解析获取数据,并结合多线程并发爬取,对速度和异常处理都做了很好的优化。

数据爬取字段包括:‘标题’, ‘关注’, ‘小区’, ‘位置’, ‘城市’, ‘房屋类型’, ‘面积’, ‘单价’, ‘总价’, ‘介绍’, ‘详情网址’, ‘图片’。我提供了两种数据存储方式:CSV文件保存和MySQL数据库保存。代码中已添加详细注释,方便同学们理解。

项目主要涉及的技术模块:request、pandas、lxml、threading、csv。

PS:代码以西安市为例,同学们只需修改关键字即可爬取指定城市的数据!

2 目标网站分析

在这里插入图片描述

本次爬取的是链家网的二手房房源数据。通过分析网页结构,我们可以采集以下字段:‘标题’, ‘关注’, ‘小区’, ‘位置’, ‘城市’, ‘房屋类型’, ‘面积’, ‘单价’, ‘总价’, ‘介绍’, ‘详情网址’, ‘封面图片’。

2.1 网页结构分析

打开开发者工具(F12或右键检查),可以发现房源信息位于ul标签下的li元素中,每个li对应一个房源对象。
在这里插入图片描述

2.2 获取房源标题

房源标题位于a标签的文本中,获取方式如下:

  1. 先获取所有房源列表:li_List = tree.xpath("//*[@class='sellListContent']/li")
  2. 在相对位置获取标题:title = li.xpath('./div/div/a/text()')[0]

在这里插入图片描述

2.3 获取图片URL

图片URL位于img标签的data-original属性中,获取方式为:li.xpath('./a/img/@data-original')[0]
在这里插入图片描述

2.4 反爬虫处理

链家网在爬取超过5个页面(约150条数据)后会触发反爬机制。解决方案有多种,可以使用Selenium库进行自动化测试,也可以直接使用Cookie绕过。
在这里插入图片描述

2.5 获取Cookie的方法
  1. 在开发者工具中选择"网络(Network)"选项卡
  2. 筛选XHR请求
  3. 找到相关请求并复制Cookie值
    在这里插入图片描述

3 完整代码实现

import requests
import threading
import pandas as pd
from lxml import etree
import csv
from datetime import datetime
import pymysql

# 数据库连接配置
cnx = pymysql.connect(
    host="localhost",
    user="root",
    password="123456",
    database="secondhouse_xian"
)
cursor = cnx.cursor()

# 全局变量
count = []  # 全部信息列表
city = '西安'  # 目标城市
filename = f'{city}.csv'  # CSV文件名(带时间戳)


# 生成1-10页URL
def url_creat():
    url = 'https://xa.lianjia.com/ershoufang/pg{}/'
    return [url.format(i) for i in range(1, 51)]


# CSV存储函数
def save_to_csv(data):
    # 定义CSV表头
    headers = ['标题', '关注', '小区', '位置', '城市', '房屋类型', '面积', '单价', '总价', '介绍', '详情网址', '图片']

    # 首次写入时创建文件并写入表头
    with open(filename, 'a', newline='', encoding='utf-8-sig') as f:
        writer = csv.DictWriter(f, fieldnames=headers)
        if f.tell() == 0:
            writer.writeheader()
        writer.writerow(data)


# 页面解析函数
def url_parse(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0',
        'Cookie': 'lianjia_uuid=6d0f2fe3-bef2-43cd-a750-939bb2e2f2f7; select_city=330100; lianjia_ssid=1807dc6c-353e-4e7c-a4a6-caaae625f69d; '
                 'crosSdkDT2019DeviceId=-if7r1q-bohvfg-83zboka4c1llock-iihmr2p59; login_ucid=2000000456398621; '
                 'lianjia_token=2.00142d0bba43fa714b0580228b73f70576; lianjia_token_secure=2.00142d0bba43fa714b0580228b73f70576; '
                 'security_ticket=UgAs85ty1j+KdeDNa49PrErJN6hc68OmqtitvxeskqSNS6hdJJ2PBLT0Bh62YyMhnstpwUB/x7a3/9fvfcAjhPMF8c1id9nGUCwZ1GgRxjwWsijqvaEobK0A2AHw6QdsLDuHKa9YmDuzfWDIjmgE7QDLVHyv81Ff4eM9apx1eV4=; '
                 'ftkrc_=ef9af662-ad8b-41fd-a832-9eaad5228488; lfrc_=8cb541c4-bed6-4f20-a39b-bdeff87fb5ec; '
                 'Hm_lvt_46bf127ac9b856df503ec2dbf942b67e=1746682060; HMACCOUNT=36F711E17DDBF3F9;'
                 ' _qzjc=1; srcid=eyJ0Ijoie1wiZGF0YVwiOlwiZDY2ODNiZTlmMjJlMDUxMDA2ZDU4NTg2NjJlOTA2NjY5MzU3Zjc5ODRlYjFiNWJiOWI2NDFmYjMyOTBkYTc2ZmY0MzdlNzNmNzNkZDlmYWU1Mjk1ZThkN2UzZmNkMGQ1MTkyYmIyNTI4YzY1NjNmYzNjMGJkOTA2ZWNkYzBmZGJlOGYxM2NkODA5MWQ1YjZlZWJlZjQ2ZWMzMjgwZmMxNDUwMjIyNDA4NjA4YjU2MzliYmYyMDkwNGI4NmIyODMzNTg0ZWYwYzAyYzVlZjQzMzQ5YTQ4NjM0N2Y0YmIxM2VmNTVlOGUzNDU0NWQ2NTc3NDY5MmUzMTMxODAwNDcyOFwiLFwia2V5X2lkXCI6XCIxXCIsXCJzaWduXCI6XCJjMzU0MWE0OVwifSIsInIiOiJodHRwczovL2h6LmxpYW5qaWEuY29tL2Vyc2hvdWZhbmcvcGcxLyIsIm9zIjoid2ViIiwidiI6IjAuMSJ9; _jzqa=1.3628376045562550000.1746682061.1746682061.1746682061.1; '
                 '_jzqc=1; _jzqx=1.1746682061.1746682061.1.jzqsr=clogin%2Elianjia%2Ecom|jzqct=/.-; _jzqckmp=1; sajssdk_2015_cross_new_user=1; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22196ae5d21e91cd9-07056658c83a518-4c657b58-1327104-196ae5d21ea35ce%22%2C%22%24device_id%22%3A%22196ae5d21e91cd9-07056658c83a518-4c657b58-1327104-196ae5d21ea35ce%22%2C%22props%22%3A%7B%7D%7D; _ga=GA1.2.1014061828.1746682072; '
                 '_gid=GA1.2.288150949.1746682072; _gat=1; _gat_past=1; _gat_global=1; _gat_new_global=1; _gat_dianpu_agent=1; '
                 '_ga_W9S66SNGYB=GS2.2.s1746682072$o1$g0$t1746682072$j0$l0$h0; _ga_1W6P4PWXJV=GS2.2.s1746682072$o1$g0$t1746682072$j0$l0$h0; '
                 '_qzja=1.1120861123.1746682060329.1746682060329.1746682060329.1746682060329.1746682085924.0.0.0.2.1; _qzjb=1.1746682060329.2.0.0.0; '
                 '_qzjto=2.1.0; _jzqb=1.2.10.1746682061.1; Hm_lpvt_46bf127ac9b856df503ec2dbf942b67e=1746682086'
    }
    
    try:
        response = requests.get(url=url, headers=headers, timeout=10)
        response.encoding = 'utf-8'
        tree = etree.HTML(response.text)

        li_List = tree.xpath("//*[@class='sellListContent']/li")
        lock = threading.RLock()

        with lock:
            for li in li_List:
                # 数据提取
                title = li.xpath('./div/div/a/text()')[0]
                link = li.xpath('./div/div/a/@href')[0]
                attention = li.xpath('./div/div/text()')[0].split('人')[0]
                # 位置
                postion = li.xpath('./div/div[2]/div/a/text()')[0] + li.xpath('./div/div[2]/div/a[2]/text()')[0]
                # 类型
                types = li.xpath('./div/div[3]/div/text()')[0].split(' | ')[0]
                # 面积
                area = li.xpath('./div/div[3]/div/text()')[0].split(' | ')[1]
                # 房屋信息
                info = li.xpath('./div/div[3]/div/text()')[0].split(' | ')[2:-1]
                info = ''.join(info)
                # 总价
                count_price = li.xpath('.//div/div[6]/div/span/text()')[0]
                # 单价
                angle_price = li.xpath('.//div/div[6]/div[2]/span/text()')[0]
                # 图片链接
                pic_link = li.xpath('./a/img/@data-original')[0]

                dic = {
                    '标题': title,
                    '关注': attention,
                    '小区': postion.split(' ')[0],
                    '位置': postion.split(' ')[1],
                    '城市': city,
                    '房屋类型': types,
                    '面积': area,
                    "单价": angle_price,
                    '总价': count_price,
                    '介绍': info,
                    "详情网址": link,
                    '图片': pic_link
                }
                print(dic)

                # 实时保存到CSV
                save_to_csv(dic)
                count.append(dic)

    except Exception as e:
        print(f"Error processing {url}: {str(e)}")


# 主运行函数
def run():
    links = url_creat()
    threads = []

    # 创建并启动线程
    for url in links:
        t = threading.Thread(target=url_parse, args=(url,))
        threads.append(t)
        t.start()

    # 等待所有线程完成
    for t in threads:
        t.join()

    # 可选:将所有数据再次保存为完整CSV(防止漏存)
    pd.DataFrame(count).to_csv(filename, index=False, encoding='utf-8-sig')
    print(f"数据已保存至 {filename},共爬取 {len(count)} 条记录")


# CSV数据导入MySQL数据库
def csv_to_mysql_simple(csv_file):
    """极简版CSV数据入库"""
    with open(csv_file, 'r', encoding='utf-8-sig') as f:
        reader = csv.DictReader(f)
        # 批量插入SQL模板
        sql = """INSERT INTO House(title, attention, community, location, city, house_type, area, unit_price, total_price, description, detail_url, image_url)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""

        # 遍历每一行数据
        for row in reader:
            try:
                cursor.execute(sql, (
                    row['标题'],
                    int(row['关注']),  # 转换为整数
                    row['小区'],
                    row['位置'],
                    row['城市'],
                    row['房屋类型'],
                    row['面积'],
                    row['单价'],
                    row['总价'],
                    row['介绍'],
                    row['详情网址'],
                    row['图片']
                ))
                cnx.commit()  # 提交事务
            except Exception as e:
                print(f"插入失败: {row['标题']},错误原因: {str(e)}")
                cnx.rollback()  # 回滚当前事务

    print("数据导入完成!")


if __name__ == '__main__':
    run()  # 注释该行会只将爬取的CSV文件存储在MySQL数据库
    csv_to_mysql_simple('西安.csv')  # 注释该行会只执行爬取数据到CSV文件

在这里插入图片描述
在这里插入图片描述

项目总结

这个链家网二手房数据爬虫项目展示了如何使用Python进行网页数据抓取,包括:

  1. 使用requests库发送HTTP请求
  2. 使用lxml和xpath解析网页内容
  3. 使用多线程提高爬取效率
  4. 处理反爬虫机制(通过Cookie)
  5. 数据存储到CSV文件和MySQL数据库

同学们可以根据自己的需求修改代码,例如更改目标城市、调整爬取字段或优化存储方式。希望这个项目对大家学习网络爬虫有所帮助!

如果有任何问题,欢迎在评论区留言讨论。

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员熊百涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值