Python爬取链家数据

技术:requests、BeautifulSoup、SQLite
解析页面,存数据到SQLite数据库,到时候你用navicat导出成csv什么的就行

1、确定城市

以天津为例,网页是https://tj.lianjia.com/ershoufang/rs/
在这里插入图片描述
把上面这些地区名字复制

2、爬取数据内容

在这里插入图片描述
在这里插入图片描述
从上面的属性中选择性爬取

3、给爷爬

3.1、创建中文映射

KEYMAP = {
    "房屋户型": "room_num",
    "所在楼层": "floor_location",
    "户型结构": "layout_structure",
    "建筑类型": "building_type",
    "房屋朝向": "house_orientation",
    "建筑结构": "structure_type",
    "装修情况": "decoration_condition",
    "配备电梯": "equipped_elevator",
    "交易权属": "transaction_ownership",
    "房屋用途": "house_usage"
}
CITY_MAP = {
    "天津": "tj",
    "北京": "bj"
}
DISTRICT_MAP = {
    "tj": {
        "和平": "heping", "南开": "nankai", "河西": "hexi", "河北": "hebei", "河东": "hedong", "红桥": "hongqiao", "西青": "xiqing",
        "北辰": "beichen", "东丽": "dongli", "津南": "jinnan", "武清": "wuqing", "滨海新区": "binhaixinqu", "宝坻": "baodi", "蓟州": "jizhou",
        "静海": "jinghai", "宁河": "ninghe"
    },
    "bj": {}
}

3.2、创建数据库

在项目目录下创建House.db文件,建表

import sqlite3

# 根据上面的映射创建表,设置字段名
CREATE_SQL = ('CREATE TABLE House ('
              'hid INTEGER PRIMARY KEY, '
              'rid INTEGER, '
              'title TEXT, '
              'area REAL, '
              'total_price INT, '
              'price INT, '
              'room_num INT, '
              'resblock_name TEXT, '
              'city_name TEXT, '
              'longitude REAL, '
              'latitude REAL, '
              'image TEXT, '
              'floor_location TEXT, '
              'layout_structure TEXT, '
              'building_type TEXT, '
              'house_orientation TEXT, '
              'structure_type TEXT, '
              'decoration_condition TEXT, '
              'equipped_elevator TEXT, '
              'transaction_ownership TEXT, '
              'house_usage TEXT );')
def create_table():
    cursor = conn.cursor()
    try:
        cursor.execute(CREATE_SQL)
        print("创建数据表")
    except:
        print('数据表已存在')
    cursor.close()

conn = sqlite3.connect('House.db')
create_table()

3.3、多线程爬

虽然Python用多线程好像实际还是单线程,我也搞不清楚,但感觉至少有点用处。
注意多线程操作SQLite的时候,每个线程都要自己创建connection,不能共用同一个conn

3.4、爬数据

步骤如下:

  1. 分页爬列表,设置城市、地区、起始页码,如(‘天津’,‘南开’,1),访问网页https://{city}.lianjia.com/ershoufang/{district}/pg{page}。解析出网页元素中的房屋列表,获取一会爬房屋详细信息需要用的hid(房屋Id)和rid(小区id?不知道)
  2. 遍历列表中每一个房子,访问网页https://{city}.lianjia.com/ershoufang/{hid}.html,解析网页元素获取对应数据
  3. 存数据库,由于链家搜索应该加了一些推荐算法,之前爬过的房子你在下一页有可能还会看见他,所以存数据的时候,可以insert_or_update;或者只插入,若数据库中已经存在该房屋就不管他了,跳到下一次循环

其中,第二步爬每个房子的信息的时候可以多线程爬
第一步设置起始页码后,他会从起始页开始,循环爬后面的页,直到没数据了
懒得写了,直接贴全部代码了

import re
import threading

import requests
import time
import json
import sqlite3
import math
from bs4 import BeautifulSoup
import concurrent.futures
import queue

CREATE_SQL = ('CREATE TABLE House ('
              'hid INTEGER PRIMARY KEY, '
              'rid INTEGER, '
              'title TEXT, '
              'area REAL, '
              'total_price INT, '
              'price INT, '
              'room_num INT, '
              'resblock_name TEXT, '
              'city_name TEXT, '
              'longitude REAL, '
              'latitude REAL, '
              'image TEXT, '
              'floor_location TEXT, '
              'layout_structure TEXT, '
              'building_type TEXT, '
              'house_orientation TEXT, '
              'structure_type TEXT, '
              'decoration_condition TEXT, '
              'equipped_elevator TEXT, '
              'transaction_ownership TEXT, '
              'house_usage TEXT );')
KEYMAP = {
    "房屋户型": "room_num",
    "所在楼层": "floor_location",
    "户型结构": "layout_structure",
    "建筑类型": "building_type",
    "房屋朝向": "house_orientation",
    "建筑结构": "structure_type",
    "装修情况": "decoration_condition",
    "配备电梯": "equipped_elevator",
    "交易权属": "transaction_ownership",
    "房屋用途": "house_usage"
}
CITY_MAP = {
    "天津": "tj",
    "北京": "bj"
}
DISTRICT_MAP = {
    "tj": {
        "和平": "heping", "南开": "nankai", "河西": "hexi", "河北": "hebei", "河东": "hedong", "红桥": "hongqiao", "西青": "xiqing",
        "北辰": "beichen", "东丽": "dongli", "津南": "jinnan", "武清": "wuqing", "滨海新区": "binhaixinqu", "宝坻": "baodi", "蓟州": "jizhou",
        "静海": "jinghai", "宁河": "ninghe"
    },
    "bj": {}
}

def create_table():
    cursor = conn.cursor()
    try:
        cursor.execute(CREATE_SQL)
        print("创建数据表")
    except:
        print('数据表已存在')
    cursor.close()


def crawl_house_list(city, district, start_page = 1):
    with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
        city = CITY_MAP[city]
        district_name = district
        district = DISTRICT_MAP[city][district_name]
        total_page = start_page
        current_page = start_page
        tasks = [] # 线程提交任务
        while current_page <= total_page:
            list, total_page = get_house_list(city, district, current_page)
            print(f"{city}-{district}{current_page}/{total_page}】 num:{len(list)}")
            for item in list:
                # 没有记录才插入记录
                if not check_exist(conn, 'House', 'hid', item["hid"]):
                    tasks.append(executor.submit(save_house_data, city, item, district_name))
                '''
                根据hid更新或插入数据
                house_data = get_house_data(city, item["hid"], item["rid"])
                if house_data:
                    house_data["district_name"] = district_name
                    update_or_insert_data(conn, 'House', 'hid', house_data)
                else:
                    print("查询{}出错".format(item))
                '''
            # 阻塞执行
            for future in concurrent.futures.as_completed(tasks):
                future.result()
            tasks = [] # 清空多线程任务列表
            current_page += 1
            # time.sleep(2)

def get_house_list(city, district, page):
    url = f"https://{city}.lianjia.com/ershoufang/{district}/pg{page}"
    response = requests.get(url)
    list = []
    total_page = 0
    if response.status_code == 200:
        soup = BeautifulSoup(response.text, 'html.parser')
        # 获取房子列表
        ul = soup.find('ul', class_='sellListContent')
        if ul:
            li_list = ul.find_all('li')
            for li in li_list:
                rid = li.get('data-lj_action_resblock_id')
                hid = li.get('data-lj_action_housedel_id')
                list.append({"rid": rid, "hid": hid})
        else:
            print("Unable to find ul with class 'sellListContent'")
        # 获取总页数
        page_box = soup.find('div', class_='page-box house-lst-page-box')
        if page_box:
            page_data = page_box.get('page-data')
            if page_data:
                page_data_dict = json.loads(page_data)
                total_page = int(page_data_dict.get('totalPage'))
            else:
                print("No page data attribute found in page-box")
        else:
            print("Unable to find div with class 'page-box house-lst-page-box'")
    else:
        print("Failed to fetch the webpage")
    return list, total_page


def get_house_data(city, hid, rid):
    url = f"https://{city}.lianjia.com/ershoufang/{hid}.html"
    response = requests.get(url)
    if response.status_code == 200:
        soup = BeautifulSoup(response.text, 'html.parser')
        house = {"hid": hid, "rid": rid}
        # 获取房屋信息、小区信息与经纬度
        script_tags = soup.find_all('script')
        for script in script_tags:
            if 'ershoufang/sellDetail/detailV3' in script.text:
                # 使用正则表达式匹配初始化数据对象
                match = re.search(r'init\(({.*?})\);', script.text, re.DOTALL)
                if match:
                    try:
                        data_str = match.group(1)
                        data_str = re.sub(r"\$\.getQuery\(location\.href, '.*?'\)", '1', data_str)  # 去掉jquery代码
                        data_str = re.sub(r"'", '"', data_str)  # 替换单引号为双引号
                        data_str = re.sub(r'(\w+):([^/\\])', r'"\1":\2', data_str)  # 将key用双引号包裹
                        data_str = re.sub(r"(\"isNewHouseReport\": \".*?\"),", r"\1", data_str)
                        data_dict = json.loads(data_str)
                        house["title"] = data_dict["title"]
                        house["area"] = float(data_dict.get("area"))
                        house["total_price"] = int(data_dict.get("totalPrice"))
                        house["price"] = int(data_dict.get("price"))
                        house["resblock_name"] = data_dict.get("resblockName")  # 小区名称
                        house["city_name"] = data_dict.get("cityName")
                        position = data_dict.get("resblockPosition").split(",")
                        house["longitude"] = position[0]
                        house["latitude"] = position[1]
                        images = data_dict.get("images")
                        if len(images) != 0:
                            house["image"] = images[0]["url"]
                        break
                    except:
                        #print("错误:{}".format(data_str))
                        return None
        else:
            print("No script containing the desired data found")
        # 获取额外信息
        intro = soup.find('div', class_="introContent")
        if intro:
            # 基础信息
            base = intro.find('div', class_="base")
            lis = base.find_all('li')
            for li in lis:
                label_tag = li.find('span', class_='label')
                value = label_tag.next_sibling.strip()
                label_tag = label_tag.text
                if label_tag == "房屋户型":
                    value = int(re.sub("(\d)室.*", r"\1", value))
                elif label_tag == "所在楼层":
                    value = re.sub(r" ?\(.*?\)", "", value)
                if KEYMAP.get(label_tag):
                    house[KEYMAP[label_tag]] = value
            # 交易信息
            transaction = intro.find('div', class_="transaction")
            lis = transaction.find_all('li')
            for li in lis:
                spans = li.find_all('span')
                label_tag = spans[0].text
                value = spans[1].text
                if KEYMAP.get(label_tag):
                    house[KEYMAP[label_tag]] = value
        else:
            print("No intro block found")
    else:
        print("Failed to fetch the webpage")
        return None
    return house

def save_house_data(city, item, district_name):
    # 多线程每个都要有自己的conn
    conn = sqlite3.connect('House.db')
    house_data = get_house_data(city, item["hid"], item["rid"])
    if house_data:
        house_data["district_name"] = district_name
        insert_data(conn, 'House', house_data)
    else:
        print("查询{}出错".format(item))


def generate_update_query(table, data, key_column):
    update_query = f"UPDATE {table} SET "
    update_query += ", ".join(f"{key} = ?" for key in data.keys() if key != key_column)
    update_query += f" WHERE {key_column} = ?"
    return update_query


def generate_insert_query(table, data):
    insert_query = f"INSERT INTO {table} ({', '.join(data.keys())}) VALUES ({', '.join(['?'] * len(data))})"
    return insert_query

def update_or_insert_data(conn, table, key_column, data):
    cursor = conn.cursor()
    # 检查是否存在特定键值的数据
    key_value = data[key_column]
    cursor.execute(f"SELECT * FROM {table} WHERE {key_column} = ?", (key_value,))
    existing_data = cursor.fetchone()
    if existing_data:
        # 如果存在数据,则执行更新操作
        update_query = generate_update_query(table, data, key_column)
        values = []
        for key in data.keys():
            if key != key_column:
                values.append(data[key])
        values.append(key_value)
        cursor.execute(update_query, values)
        conn.commit()
        print("Data updated successfully.")
    else:
        # 如果不存在数据,则执行插入操作
        insert_query = generate_insert_query(table, data)
        cursor.execute(insert_query, list(data.values()))
        conn.commit()
        #print("Data inserted successfully.")
    cursor.close()

def check_exist(conn, table, key_column, key_value):
    cursor = conn.cursor()
    # 检查是否存在特定键值的数据
    cursor.execute(f"SELECT * FROM {table} WHERE {key_column} = ?", (key_value,))
    existing_data = cursor.fetchone()
    return existing_data is not None

def insert_data(conn, table, data):
    cursor = conn.cursor()
    insert_query = generate_insert_query(table, data)
    cursor.execute(insert_query, list(data.values()))
    conn.commit()
    #print("Data inserted successfully.")
    cursor.close()

conn = sqlite3.connect('House.db')
if __name__ == '__main__':
    # print(CREATE_SQL)
    create_table()
    districts = ['武清','滨海新区','宝坻','蓟州','静海','宁河'] # 设置你城市中想爬的地区
    for district in districts:
        print("=== Crawling " + district + " ===")
        crawl_house_list('天津', district, 1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/538108.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三天做完pandas数据分析50题第一天

三天做完pandas数据分析50题第一天 第1题 将python的list转换为Series第2题 将字典转换为Series第3题 将Series转换成python的list第4题 使用numpy创建series。第5题 如何为Series添加新的元素&#xff1f;第6题 使用字典创建DataFrame第7题 给DataFrame设置索引列第8题 生成一…

每日一题---OJ题: 合并两个有序链表

嗨!小伙伴们,好久不见啦! 今天我们来看看一道很有意思的一道题---合并两个有序链表 嗯,题目看上去好像不难,我们一起画图分析分析吧! 上图中,list1有3个结点,分别为1,2,3 ; list2中有3个结点,分别为1,3,4, 题目要求我们要将这两个链表合并到一起,并且是升序,最后将链表返回。 …

光威神策PRO PCIe 5.0 SSD发布,国产固态硬盘进入10G俱乐部

全球半导体供应链的紧张局势和闪存资源的短缺让许多行业都面临着不小的压力 &#xff0c; 连带的也让消费者难以获取物美价廉的闪存产品 。但是&#xff0c;总有一些企业能够逆流而上&#xff0c; 像是 光威科技这家国产存储品牌&#xff0c; 最近就给国内消费者 带来了一个惊喜…

mybatis05:复杂查询:(多对一,一对多)

mybatis05&#xff1a;复杂查询&#xff1a;&#xff08;多对一&#xff0c;一对多&#xff09; 文章目录 mybatis05&#xff1a;复杂查询&#xff1a;&#xff08;多对一&#xff0c;一对多&#xff09;前言&#xff1a;多对一 &#xff1a; 关联 &#xff1a; 使用associatio…

Echarts-实现地图并轮播地图信息

目录 ./map-geojson/jinhua.json./CenterMap.vue./center.vue 使用地图组件效果 ./map-geojson/jinhua.json {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"adcode":330…

力扣—2024 春招冲刺百题计划

矩阵 1. 螺旋矩阵 代码实现&#xff1a; /** param matrix int整型二维数组 * param matrixRowLen int matrix数组行数* param matrixColLen int* matrix数组列数* return int整型一维数组* return int* returnSize 返回数组行数 */ int* spiralOrder(int **matrix, int matri…

网工内推 | 网络工程师,13薪,周末双休,华三、华为认证优先

01 路邦远大 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、配合市场销售人员&#xff0c;做好产品的售后服务工作&#xff1b; 2、负责项目方案安装调试指导以及日常客户使用培训&#xff0c;对客户提出的问题提出解决方案&#xff1b; 3、为客户提供专业、规范的…

图片作为背景的闪白问题,6种基础方案, 不会不知道吧

前言 关于【SSD系列】&#xff1a; 前端一些有意思的内容&#xff0c;旨在3-10分钟里&#xff0c; 500-1500字&#xff0c;有所获&#xff0c;又不为所累。 某天&#xff0c;发现有背景图片的弹出框&#xff0c;会出现闪白现象&#xff0c;这&#xff0c;兄弟们&#xff0c;你…

导入芯片原厂SDK Mirror源码到gerrit

下载镜像代码 repo init --mirror --repo-url ssh://xx/repo.git -u ssh://xx/manifests.git -m manifest.xml repo sync 创建AOSP project 对All Project权限修改 创建repo 在刚才下载的codebase根目录执行如下命令&#xff1a; repo forall -c echo $REPO_PROJECT; ssh -p 29…

C++ AVL树底层实现原理

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C知识分享⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 目录 前言 AVL 树 1.1 AVL树的概念 1.2 AVL树…

【Hadoop大数据技术】——Flume日志采集系统(学习笔记)

&#x1f4d6; 前言&#xff1a;在大数据系统的开发中&#xff0c;数据收集工作无疑是开发者首要解决的一个难题&#xff0c;但由于生产数据的源头丰富多样&#xff0c;其中包含网站日志数据、后台监控数据、用户浏览网页数据等&#xff0c;数据工程师要想将它们分门别类的采集…

永恒之蓝(ms17-010)复现

永恒之蓝 永恒之蓝&#xff08;Eternal Blue&#xff09;爆发于2017年4月14日晚&#xff0c;是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限&#xff0c;以此来控制被入侵的计算机。甚至于2017年5月12日&#xff0c; 不法分子通过改造“永恒之蓝”制作了wannacry勒索…

ARM架构麒麟操作系统安装配置Mariadb数据库

、安装配置JDK (1)检查机器是否已安装JDK 执行 java -version命令查看机器是否安装JDK,一般麒麟操作系统默认安装openjdk 1.8。 (2)安装指定版本JDK 如果麒麟操作系统默认安装的openjdk 1.8不符合需求的话,可以卸载机器安装的openjdk 1.8并按需安装所需的openjdk版本…

软件杯 深度学习人体语义分割在弹幕防遮挡上的实现 - python

文章目录 1 前言1 课题背景2 技术原理和方法2.1基本原理2.2 技术选型和方法 3 实例分割4 实现效果5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习人体语义分割在弹幕防遮挡上的应用 该项目较为新颖&#xff0c;适合作为竞…

Python爬虫与API交互:如何爬取并解析JSON数据

目录 前言 一、什么是API和JSON数据 二、准备环境 三、发送API请求并获取数据 四、解析JSON数据 五、完整代码示例 六、总结 前言 随着互联网的发展&#xff0c;越来越多的网站提供了API接口&#xff0c;供开发者获取实时数据。在爬虫领域中&#xff0c;与API交互并解析…

快速实现一个Hibernate的例子

写第一个简单的Hibernate程序&#xff1a; 具体的开始第一个Hibernate程序之前: 找到jar包, hibernate 的核心包, mysql数据库的连接驱动包, junit测试包 ①创建Hibernate配置文件 ②创建持久化类 也是和数据库中数据表一一对应这个类 ③创建对象-关系映射文件 ④通过hibern…

【攻防世界】mfw(.git文件泄露)

首先进入题目环境&#xff0c;检查页面、页面源代码、以及URL&#xff1a; 发现页面无异常。 使用 dirsearch 扫描网站&#xff0c;检查是否存在可访问的文件或者文件泄露&#xff1a; 发现 可访问界面/templates/ 以及 .git文件泄露&#xff0c;故使用 GItHack 来查看泄露的 …

LeetCode题练习与总结:不同路径Ⅱ--63

一、题目描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么从…

如何使用SQL注入工具?

前言 今天来讲讲SQL注入工具&#xff0c;sqlmap。如何使用它来一步步爆库。 sqlmap官方地址如下。 sqlmap: automatic SQL injection and database takeover tool 前期准备&#xff0c;需要先安装好docker、docker-compose。 一个运行的后端服务&#xff0c;用于写一个存在…

竞赛 图像识别-人脸识别与疲劳检测 - python opencv

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是…