爬取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刪除,下一次運行就去爬剩下還沒爬取的數據
四.實現
- 首先創建數據庫表
- 在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)
)
複製代碼
- 獲取城市的區數據
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()
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()
複製代碼
- 獲取區的街道等信息
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()
複製代碼
- 爬取詳細租房信息
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()
複製代碼