Skip to content

Latest commit

 

History

History
169 lines (123 loc) · 5.02 KB

File metadata and controls

169 lines (123 loc) · 5.02 KB

老旧小区自动化分析:地址解析与数据爬取

日期: 2024-12-29 主题: 地址信息提取、网络爬虫与数据分析


背景

继续完善小区分析工具。这次的需求是从原始地址中提取小区名称,然后从网上获取建成年份信息。


小区名称提取

地址格式五花八门,需要智能提取:

def extract_community_name(self, address: str) -> str:
    """优化后的小区名称提取"""
    # 定义小区关键词
    community_keywords = ['花园', '小区', '广场', '家园', '苑', '新村', '公寓', '别墅', '城']

    # 1. 首先尝试匹配完整的小区名称
    for keyword in community_keywords:
        pattern = f'([^号路街巷]{1,20}?{keyword})'
        match = re.search(pattern, address)
        if match:
            community = match.group(1)
            # 清理门牌号
            community = re.sub(r'^\d+号', '', community)
            return community.strip()

    # 2. 检查是否有"村"结尾的地址
    village_match = re.search(r'([^\d]+村)[^\d]*$', address)
    if village_match:
        return village_match.group(1)

    # 3. 提取路名
    road_match = re.search(r'([^省市区县镇村\d]+?[路街道])', address)
    if road_match:
        return road_match.group(1)

    return address

提取策略:

  1. 优先匹配带关键词的小区名(花园、小区等)
  2. 其次匹配村庄名称
  3. 最后提取路名
  4. 去除门牌号等干扰信息

数据获取

从房产网站获取小区信息:

class LianjiaAnalyzer:
    def __init__(self):
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X)',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9'
        }
        self.old_community_year = 2010

    def get_community_info(self, community_name: str) -> Dict:
        try:
            search_url = f"https://m.ke.com/changzhou/xiaoqu/rs{quote(community_name)}/"
            response = requests.get(search_url, headers=self.headers, timeout=10)

            # 匹配小区链接
            detail_pattern = r'href="(/changzhou/xiaoqu/\d+/)"'
            matches = re.findall(detail_pattern, response.text)

            if matches:
                detail_url = f"https://m.ke.com{matches[0]}"
                detail_response = requests.get(detail_url, headers=self.headers)

                # 提取建成年份
                year_pattern = r'建成年代</div><div[^>]*>(\d{4})[年-](\d{4})?年?'
                year_match = re.search(year_pattern, detail_response.text)

                if year_match:
                    start_year = int(year_match.group(1))
                    end_year = int(year_match.group(2)) if year_match.group(2) else start_year
                    return {
                        "建成年份": f"{start_year}-{end_year}",
                        "最早建成": start_year
                    }
            return {}

        except Exception as e:
            print(f"获取小区信息出错: {e}")
            return {}

技术要点:

  • 模拟移动端请求,兼容性更好
  • 用正则提取年份,处理年份范围(如 2000-2008 年)
  • 完善的错误处理

批量分析

def analyze_communities(filename: str, limit: int = 10) -> pd.DataFrame:
    """分析小区列表"""
    analyzer = LianjiaAnalyzer()
    results = []

    with open(filename, 'r', encoding='utf-8') as f:
        addresses = [line.strip() for line in f if line.strip()][:limit]

    for idx, address in enumerate(addresses, 1):
        result = analyzer.analyze_community(address)
        results.append(result)
        time.sleep(2)  # 控制请求频率

    return pd.DataFrame(results)

测试结果

原地址 提取结果
劳动西路99号新世界花园2-5号 新世界花园
延政西大道325号湖畔春秋花园22区202幢 湖畔春秋花园
南夏墅街道三河村委金家塘71号 金家塘村

最佳实践

数据获取

  • 控制请求频率,避免被封
  • 使用合适的 User-Agent
  • 实现错误重试
  • 保存中间数据

数据处理

  • 规范化清洗流程
  • 保存详细日志
  • 定期验证准确性

今日反思

今天主要在写正则表达式,提取地址中的小区名称。

地址数据比想象中混乱。有的写"xx路xx号xx小区",有的写"xx小区xx栋",有的直接写村名。没有通用的解析规则,只能用多个正则依次尝试。这让我理解了为什么 NLP 中的命名实体识别(NER)是个难题。

爬虫部分比较顺利,但要注意法律风险。爬取公开信息用于内部分析一般没问题,但不能用于商业用途或公开发布。另外要控制请求频率,不能给目标网站造成压力。

一个小技巧:用移动端 User-Agent 通常能获得更简洁的页面,更容易解析。


延伸学习

  • 中文地址解析库(如 cpca)
  • 命名实体识别(NER)
  • 爬虫框架(Scrapy)
  • 数据采集的合规性