爬取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()
复制代码