引言
作为一个刚毕业两年的打工人,在深圳这种房价压死人的城市,买房是不可能买房了,只能寄希望于租到一个便宜又舒适的房子。今天给大家带来的案例是tesseract破解css反爬抓取自如租房信息,将好房源尽收囊中。
一、分析页面
1、进入自如租房网站(url链接:https://sz.ziroom.com/z/p1/),打开页面,如图所示,城市选择深圳,可以看到,有很多租房信息,包括:房源名称、面积/层数、地理位置、价格,其中价格是比较重要的数据。
2、我们点击到第2页,可以看到网址变成了:https://sz.ziroom.com/z/p2/,如图所示。
3、因此只需要改变https://sz.ziroom.com/z/p{}/中的{}部分,就可以遍历爬取所有50页的内容。
4、我们按F12解析一下网页元素,可以看到,房源名称、面积/层数、地理位置等信息都位于标签div[@class=“info-box”]下相应位置,用xpath或者bs4可以快速定位获取,但价格信息却找不到。
5、由于价格属于较为重要的数据,此处做了css反爬,价格数据不是直接的数字,而是是通过数字图片和偏移像素定位的方式获得,再通过css渲染出来。因此要爬取该数据需先找到图片,然后用OCR文本识别方式识别文本,再通过相应的匹配方式还原出原始数据。废话不多说,开干!
二、代码实现
1、采用面向对象的方式实现,首先导入可能要用到的库,此处需要用到pytesseract和PIL库,需要先pip install安装。
import requests
import pytesseract
from PIL import Image
import time
from lxml import etree
import re
import csv
2、定义类并初始化类属性及实例属性,此处需要预先安装tesseract-ocr软件,并将其安装路径添加进来。
class ZiruSpider(object):
pytesseract.pytesseract.tesseract_cmd = r'D:\Program Files\Tesseract-OCR\tesseract.exe'
tessdata_dir_config = r'--tessdata-dir "D:\Program Files\Tesseract-OCR\tessdata"'
csv_headers = ['name', 'area_floor', 'location', 'price']
def __init__(self, page_num):
self.urls = ['https://sz.ziroom.com/z/p{}/'.format(page) for page in range(1, page_num+1)]
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36"
}
self.offsets = ['-0px', '-21.4px', '-42.8px', '-64.2px', '-85.6px', '-107px', '-128.4px', '-149.8px', '-171.2px', '-192.6px']
3、定义获取响应内容的方法
def get_response(self, url, headers):
response = requests.get(url, headers=headers)
response.encoding = 'utf-8'
cont = response.text
return cont
4、定义构造字典的方法,实现获取数字图片并识别出数字,并和像素偏移量一一对应形成字典。
def make_dict(self, uprice):
price_style = uprice.xpath('./span[@class="num"]/@style')[0]
image_url = 'https:' + re.findall('background-image: url\((.*?)\);', price_style)[0]
r = requests.get(image_url, headers=self.headers)
with open('ziru.png', 'wb') as f:
f.write(r.content)
image = Image.open('ziru.png')
text = pytesseract.image_to_string(image, lang='eng', config=self.tessdata_dir_config)
nums = []
for num in text:
if num != ' ' and num != '\n':
nums.append(num)
dic = {}
for k, v in zip(self.offsets, nums):
dic[k] = v
# print(dic)
return dic
5、定义获得房租价格的方法。
def get_price(self, uprice, num_dic):
styles = uprice.xpath('./span[@class="num"]/@style')
# print(styles)
price = ''
for style in styles:
# print(style)
px_num = style.split(';')[-1].split(':')[-1].strip()
# print(px_num)
num = num_dic[px_num]
price += num
# print(price)
symble = uprice.xpath('./span[@class="rmb"]/text()')[0]
unit = uprice.xpath('./span[@class="unit"]/text()')[0]
price = symble + price + unit
return price
6、定义处理网页并获取全部数据的方法。
def parse_page(self, cont):
html = etree.HTML(cont)
data_list = []
items = html.xpath('//div[@class="Z_list-box"]/div')
for item in items:
try:
item_data = {}
item_data['name'] = item.xpath('.//div[@class="info-box"]/h5/a/text()')[0]
item_data['area_floor'] = item.xpath('.//div[@class="info-box"]/div[@class="desc"]/div[1]/text()')[0]
item_data['location'] = item.xpath('.//div[@class="info-box"]/div[@class="desc"]/div[@class="location"]/text()')[0].strip()
u_price = item.xpath('.//div[@class="info-box"]/div[@class="price "]')[0]
img_dic = self.make_dict(u_price)
item_data['price'] = self.get_price(u_price, img_dic)
# print(uprice)
print(item_data)
data_list.append(item_data)
except Exception as e:
print('数据异常')
return data_list
7、定义保存数据到csv的方法和实现主要逻辑的run方法,并定义main模块,这里我们演示爬取10页数据为例。
def save_data(self, index, data):
with open('ziru_info.csv', 'a', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, self.csv_headers)
if index == 0:
writer.writeheader()
writer.writerows(data)
def run(self):
for index, url in enumerate(self.urls):
cont = self.get_response(url, self.headers)
page_data = self.parse_page(cont)
# print(page_data)
self.save_data(index, page_data)
time.sleep(1)
if __name__ == '__main__':
ziru_1 = ZiruSpider(10)
ziru_1.run()
8、运行程序,如图所示,就得到想要的数据啦。
9、核对数据是否正确,如:我们挑选第3条数据,房源为“合租·港铁天颂5居室-南卧”的数据,可以发现,数据完全正确。大功告成!
小伙伴,你学会了吗?扫描下方二维码关注公众号,在后台回复“爬取自如租房”即可获取源代码。