python入门实践:如何爬取自如数据

48次阅读

共计 5219 个字符,预计需要花费 14 分钟才能阅读完成。

首先代码地址奉上 https://github.com/liangyuqi/…
一、简介
use python to catch the information from ziru(彩蛋见最后)

适合一起刚入门 python 的同学,我也是萌新,所以代码可能不是很优雅
爬取思路分析见第五部分
二、环境
Python
python –version
(mac 自带)
brew install python
pip
pip –version
pip 是 Python 包管理工具,该工具提供了对 Python 包的查找、下载、安装、卸载的功能
curl https://bootstrap.pypa.io/get… -o get-pip.py
sudo python get-pip.py
三、安装依赖
pip freeze >package.txt
sudo pip install -r package.txt
四、启动
cd index
chmod a+x ziru_room.py
python ziru_room.py
五、思路分析
1. 反反爬虫
一般公司都有安全部门,防止大规模的撞库或者带宽挤占,那爬取的时候肯定会被拦截,定位然后律师函警告。
所以我觉得一个爬虫系统最重要的就是反 反爬虫。
我们先分析一下,一般简单的反爬虫什么思路?
用户请求的 Headers,用户行为,网站目录和数据加载方式
headers 里面主要根据 userAgent 查重。userAgent 属性是一个只读的字符串,声明了浏览器用于 HTTP 请求的用户代理头的值。简单来说就是浏览器向服务器”表明身份“用的。
用户行为主要靠 ip。ip 的话不用讲了,和身份证号差不多,所以我们发起请求应该用动态的,同一 ip 多次访问就可能被拉入 ip 黑名单,而且会导弹定位到你的服务器所在位置。
第三个方式比较高端了,我这次没有展示。前两种是爬虫伪装成浏览器读取数据,但是第三种是模拟出一个浏览器进行用户点击提交等操作,它本身就是一个没有界面的浏览器,从填写表单到点击按钮再到滚动页面,全部都可以模拟。这时候就可以根据一些其它方式,如识别点触式(12306)或者滑动式的验证码。
整理好思路开始实现,我们的目标是实现一个动态的 ip 和 userAgent 池,每次请求伪装成不一样的来源
step1: 我们去爬取一个开放代理 ip 的网站。。。然后试试他开放的 ip 可不可用,可用的话加入我们的 ip 池。详见 代码 ziru_room.py
# 经测试可用 ip
usefulIp = []

# 获取代理 ip 地址
uriGetIp = ‘http://www.xicidaili.com/wt/’

# 检测 ip 是否可用地址
testGetIp = ‘http://icanhazip.com/’

usefulIp = getUsefulIPList(uriGetIp, testGetIp, userAgent)
”’
获取可用的 ip 列表
”’
def getUsefulIPList(uriGetIp, testGetIp, userAgent):
# 全部代理 ip
allProxys = []

# 经测试可用 ip
usefulIp = []
ipList = requests.get(
uriGetIp, headers={‘User-Agent’: random.choice(userAgent)})

ipData = bs4.BeautifulSoup(ipList.text, “html.parser”)

ip = ipData.select(“#ip_list > tr > td:nth-of-type(2)”)

port = ipData.select(“#ip_list > tr > td:nth-of-type(3)”)

protocol = ipData.select(“#ip_list > tr > td:nth-of-type(6)”)

for ip, port, protocol in zip(ip, port, protocol):
proxy = ip.get_text().strip()+’:’+port.get_text().strip()
allProxys.append(proxy)

print(‘ 正在初始化 ip 数据池,请耐心等待 …’)

process.max_steps = len(allProxys)

process.process_bar = process.ShowProcess(process.max_steps)

# 筛选可用 ip
for proxy in allProxys:
process.process_bar.show_process()
# time.sleep(0.05)
try:
theIp = requests.get(testGetIp, headers={‘User-Agent’: random.choice(userAgent)}, proxies={
‘http’: proxy}, timeout=1, allow_redirects=False)
except requests.exceptions.Timeout:
# print(‘ 超过 1s’)
continue
except requests.exceptions.ConnectionError:
# print(‘ 连接异常 ’)
continue
except requests.exceptions.HTTPError:
# print(‘http 异常 ’)
continue
except:
# print(“ 其他错误 ”)
continue
else:
if (theIp.status_code == 200 and len(theIp.text) < 20):
usefulIp.append(proxy)
# print(theIp.text)

print(‘ 可用 ip 池为下:’+’,’.join(usefulIp))
return usefulIp

step2: 构造 userAgent 池
userAgent = [‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36’,
‘Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0’,
‘Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10’
]
这个不像 ip 会经常挂,所以写死问题不大。
2. 爬取数据
我们的原材料准备好了,开始爬取,可以看见用的是 random.choice() 去 ip,userAgent 池取得随机配置 组成 get 请求。详见 代码 ziru_room.py
def computedData(usefulIp, userAgent, ipIndex=0):
# debugger
# pdb.set_trace()
fhandle = open(‘../output/output.txt’, ‘a’) # 追加写入文件

# Get 请求 - 并传递 headers
try:
data = requests.get(“http://www.ziroom.com/z/nl/z3-r3-o2-s5%E5%8F%B7%E7%BA%BF-t%E5%8C%97%E8%8B%91%E8%B7%AF%E5%8C%97.html”,
headers={‘User-Agent’: random.choice(userAgent)}, proxies={‘http’: random.choice(usefulIp)}, timeout=(3, 7))
#
pass
except:
print “Error: 请求失败 ”
computedData(usefulIp, userAgent)
return
pass
else:
roomDate = bs4.BeautifulSoup(data.text, “html.parser”)
# 标题
title = roomDate.select(“#houseList > li > div.txt > h3 > a”)
# 地点 改版没了 ////
# place = roomDate.select(“#houseList > li > div.txt > h4 > a”)
# 距离
distance = roomDate.select(
“#houseList > li > div.txt > div > p:nth-of-type(2) > span”)
# 价格
price = roomDate.select(“#houseList > li > div.priceDetail > p.price”)
# 面积
area = roomDate.select(
“#houseList > li > div.txt > div > p:nth-of-type(1) > span:nth-of-type(1)”)
# 楼层
floor = roomDate.select(
“#houseList > li > div.txt > div > p:nth-of-type(1) > span:nth-of-type(2)”)
# 房间配置
room = roomDate.select(
“#houseList > li > div.txt > div > p:nth-of-type(1) > span:nth-of-type(3)”)
#
print(‘ 北京市自如数据如下 ’)
fhandle.write(‘ 北京市 ’+time.strftime(“%Y-%m-%d %H:%M:%S”,
time.localtime()) + ‘ 自如数据如下 ’+’\n’)

for title, price, area, floor, room, distance in zip(title, price, area, floor, room, distance):
last_data = {
“ 名称 ”: title.get_text().strip(),
# “ 地段 ”: place.get_text().strip(),
“ 距离 ”: distance.get_text().strip(),
“ 价格 ”: price.get_text().replace(‘ ‘, ”).replace(‘\n’, ”),
“ 面积 ”: area.get_text().strip(),
“ 楼层 ”: floor.get_text().strip(),
“ 房间大小 ”: room.get_text().strip()
}

fhandle.write(“ 名称:”+title.get_text().strip())
# fhandle.write(“ 地段:”+place.get_text().strip())
fhandle.write(“ 距离:”+distance.get_text().strip())
fhandle.write(
“ 价格:”+price.get_text().replace(‘ ‘, ”).replace(‘\n’, ”))
fhandle.write(“ 面积:”+area.get_text().strip())
fhandle.write(“ 楼层:”+floor.get_text().strip())
fhandle.write(“ 房间大小:”+room.get_text().strip() + ‘\n’)

# print json.dumps(last_data).decode(‘unicode-escape’)
# print json.dumps(last_data,ensure_ascii=False)
print json.dumps(last_data, encoding=’UTF-8′, ensure_ascii=False)

fhandle.write(“************************************************”+’\n’)
fhandle.close()
print(time.strftime(“%Y-%m-%d %H:%M:%S”, time.localtime()))
pass

3. 其他部分
因为爬取可用的 ip 组成 ip 池,是一个比较耗时的过程,所以加入了图像化的等待显示,详见 代码 process.py
自动化爬取要有点节操,所以得加入延时,详见 代码 ziru_room.py
while(True):
computedData(usefulIp, userAgent)
time.sleep(60)

python 一点其他感触,写起来很简洁,这个换行缩进还有 dict 对象中文 Unicode 搞了很久。。。目前和 node 相比优缺点在哪里还没有分析好,可以留言探讨下。
码字辛苦,代码粗糙后续会有优化,点小手 star 一下谢谢 https://github.com/liangyuqi/…
最后送上彩蛋,这位老哥最后根据 github 里 qq 找到的我,反反爬虫不算太失败吧,爬取的也不是什么关键数据,手动滑稽,仅供萌新学习练手

正文完
 0