爬取10w條鏈家租房數據

爬取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[0if way[0else '',
                        arr[0if arr.__len__() > 0 else '',
                        arr[1if arr.__len__() > 1 else '',
                        arr[2if arr.__len__() > 2 else '',
                        arr[3if 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()
複製代碼
相關文章
相關標籤/搜索