爬取10w条链家租房数据

2,008 阅读10分钟

爬取10w条链家租房数据

一.背景

  • 因为女票的工程实践选了python数据分析,要分析北上广深的租房现状,然后我就只能扒拉一下以前的python代码,看看能不能爬个几万条数据给她分析(感觉都是老生常谈的分析了~)
  • 因为我是做前端的,所以网页数据解析使用的是pyquery这个库,使用语法有点像jquery
  • 并且这个项目可能会成为毕业设计,所以我尽量使用多种方式。例如地区数据使用csv文件存放,租房详情数据使用pymysql存放;地区url使用selenium获取,而详情数据使用requests进行获取

二.分析

  • 以深圳为例,链家租房的网址是:https://sz.lianjia.com/zufang/ ; 从而一个个去看北上广深,得到城市数组 ['sz','sh','bj','gz']
  • 分析深圳翻页接口:https://sz.lianjia.com/zufang/pg100/#contentList;当页数大于100的时候,得到的数据还是和100页的时候一样,所以改变pg后面的数字是无法得到所有数据的
  • 思考:一个城市分为多个区,一个区分为多个街道之类的,那么每个街道的页数上限也是100的话,那么即使偶尔有一两个超过100页的街道,得到的数据量也是远大于我们100页的数据量

三.问题

  • 在实际编码中遇到了一些问题
  • 获取到的数据中存在一定数量的广告:经过观察发现广告没有对应房子地址的元素,所以通过这个判断是否是广告,如果是广告则爬取下一条数据
  • 链家反爬策略: 链家的反爬相对来说还是很友好的,即使被抓到了,人机验证几次就又可以了,所以我这里只是通过time.sleep()休眠方式来进行反反爬。有钱的朋友可以去淘宝买ip弄个ip池,这样啥反爬都不慌了,至于那些免费ip的网站基本没啥用。
  • 爬到一半结果被反爬:我的解决方案比较low,就是手动把csv文件中已经用到的url删除,下一次运行就去爬剩下还没爬取的数据

四.实现

  1. 首先建立数据库表
  • 在navicat创建数据库lianjia,创建表的语句如下:
create table shanghai(
  id int(5) PRIMARY KEY NOT NULL auto_increment,
  city VARCHAR(200),
  hName VARCHAR(200),
  way VARCHAR(200),
  address VARCHAR(200) ,
  area VARCHAR(200) ,
  position VARCHAR(200),
  type VARCHAR(200),
  price VARCHAR(200),
  time VARCHAR(200),
  url VARCHAR(200)
)
  1. 获取城市的区数据
  • 首先会在首行先导入所有依赖库
from selenium import webdriver
import time
import csv
from pyquery import PyQuery as pq
import requests
import pymysql
import random
  • 下面的获取区的代码
def getArea():
    brow = webdriver.Chrome()
    cityArr = ['sz','sh','bj','gz']
    file = open('area.csv''a', encoding='utf-8', newline='')
    # 打开文件,准备追加内容
    writer = csv.writer(file)
    for city in cityArr:
        url = 'https://' + city + '.lianjia.com/zufang/'
        brow.get(url)
        doc=pq(brow.page_source,parser='html')
        ul=doc('#filter ul').items()
        # 获得区的url
        for item in ul:
            tem = item.attr('data-target')
            if(tem == 'area'):
                for li in item.items('li'):
                    if(li.text()!='不限'):
                        str = url.split('/zufang')[0] + li.children('a').attr('href')
                        writer.writerow(str.split(','))
        time.sleep(10)
    # 退出
    file.close()
    brow.quit()
  1. 获取区的街道等信息
def getDetail():
    # 读
    arr = []
    with open('area.csv''r') as f:
        reader = csv.reader(f)
        for row in reader:
            arr.append(row[0])
    f.close()
    # 写
    file_detail = open('detail.csv''a', encoding='utf-8', newline='')
    writer_detail = csv.writer(file_detail)
    brow = webdriver.Chrome()
    for val in arr:
        brow.get(val)
        doc = pq(brow.page_source, parser='html')
        ul = doc('#filter ul').items()
        for i, item in enumerate(ul):
            if (i == 3):
                for li in item.items('li'):
                    if (li.text() != '不限' and li.children('a')):
                        str = val.split('/zufang')[0] + li.children('a').attr('href')
                        writer_detail.writerow(str.split(','))
                        print(str)
        time.sleep(3)
    file_detail.close()
    brow.quit()
  1. 爬取详细租房信息
def run():
    with open('detail.csv''r') as f:
        reader = csv.reader(f)
        for row in reader:
            time.sleep(random.randint(20,100))
            pgRes = requests.get(row[0])
            pgDoc = pq(pgRes.text, parser='html')
            pgSum = int(pgDoc('.content__pg').attr('data-totalpage'))
            pg = 0
            # 还需要根据页数来进行爬取。有些再加一层循环
            while(pg < pgSum):
                pg+=1
                url =row[0] + 'pg%d'%pg
                print(url)
                res = requests.get(url)
                doc = pq(res.text, parser='html')
                city = doc('.content__title a')[0]
                str = ''
                time.sleep(random.randint(2,20))
                if(city.text == '深圳'):
                    str = 'shenzhen'
                elif(city.text == '广州'):
                    str = 'guangzhou'
                elif(city.text == '上海'):
                    str = 'shanghai'
                elif(city.text == '北京'):
                    str = 'beijing'
                else:
                    Exception('城市名称错误')

                list = doc('.content__list .content__list--item').items()
                for li in list:
                    # 需要先建立数据库,表格
                    db = pymysql.connect(host='localhost', user='root', password='123456', db='lianjia')
                    tem = li.find('.content__list--item--des')
                    arr = tem.text().split('/')
                    way = li.find('.content__list--item--title a').text().split('·')
                    house_data = (
                        city.text,
                        tem.children('a')[2].text if tem.children('a').length>1 else '广告',
                        way[0] if way[0] else '',
                        arr[0] if arr.__len__() > 0 else '',
                        arr[1] if arr.__len__() > 1 else '',
                        arr[2] if arr.__len__() > 2 else '',
                        arr[3] if arr.__len__() > 3 else '',
                        li.find('.content__list--item-price em').text(),
                        li.find('.content__list--item--time').text(),
                        ('https://sz.lianjia.com' + li.find('.twoline').attr('href'))if(li.find('.twoline').attr('href')) else ''
                    )
                    if(house_data[1]=='广告'):
                        continue
                    # 声明游标
                    cursor = db.cursor()
                    sql = "insert into "+ str +"(city,hName,way,address,area,position,type,price,time,url)" \
                          "  values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
                    cursor.execute(sql, house_data)
                    db.commit()  # 提交数据库
                    db.close()

五.完整代码

  • 现在写的版本还是比较那啥,反爬之后需要手动删除csv文件中的url,而且可能会有重复/遗漏的数据
  • 在博客园看到有大佬用移动端反编译的方式找到接口完成爬取,我还没试验过,但是看起来很有料,有兴趣的可以去试试:https://www.cnblogs.com/mengyu/p/9115832.html
  • 因为掘金的字数限制。所以只把最后调用的代码贴上来~
if __name__=='__main__':
    getArea()
    getDetail()
    run()