标题略显浮夸...大家不要介意@_@
最近要去上海打工,网上查了一下,很多人说豆瓣租房小组租房子挺靠谱的,于是就百度了一下,点进去的界面是这样的...
看到首页的时候就感觉有点头大,有种零几年逛贴吧论坛的感觉,就直接罗列标题,没有缩略图,你要点进去才能查看具体的房源信息,房子图片,有点麻烦。
为了方便浏览,我决定利用所学知识对该界面进行优化,把纯文字的界面转化成这种“标题+图片的形式”,浏览信息更直观方便一些,下面先上效果图,图中没有图片的地方是因为原贴就没有图片。
1. 思路
先解释一下,我说的优化的意思不是写个浏览器脚本或插件,然后打开这个网页会自动转化,这种技术对于我来说太难了......
我的想法是,先爬取这个网页的关键数据,保存成json文件,然后再写一个静态网页展示这些数据,当然一些链接也会爬取下来,所以最后即使我做的是静态网页,但点击相应的帖子,我也能跳转到原贴的详情页,营造出一种真实网站的感觉
2. 涉及的技术
- Python:要爬取网页信息,这里我选择用
Python
,同时使用到了requests
库和lxml
库。 - 前端技术:
HTML
、CSS
,JavaScript
素质三连,为了便于展示数据我用到了Vue2.0
,懒得写分页,所以也用到了Bootstrap
的分页组件。
3. 代码实现
3.1 爬取网页信息
import json
import requests
from lxml import html
class spider:
def __init__(self):
self.page = 0
self.url = 'https://www.douban.com/group/search?start={}&cat=1013&group=146409&sort=time&q=%E9%97%B5%E8%A1%8C'.format(self.page)
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
}
def get_html(self, url):
response = requests.get(url, headers=self.headers).text
html_tree = html.fromstring(response)
return html_tree
def get_img(self, href):
html_data = self.get_html(href)
img = html_data.xpath('//*[@id="link-report"]//*[@class="image-wrapper"]/img/@src')[:4]
return img
def save_files(self, filename, array):
try:
with open(filename, 'w', encoding='utf-8') as json_data:
json.dump(array, json_data, ensure_ascii=False)
print('**********写入成功**********')
except EnvironmentError:
print('写入失败')
def get_data(self):
json_data = []
data = {}
html_data = self.get_html(self.url)
title_list = html_data.xpath('//*[@class="olt"]/tbody/tr/td[1]/a/@title')
href_list = html_data.xpath('//*[@class="olt"]/tbody/tr/td[1]/a/@href')
time_list = html_data.xpath('//*[@class="olt"]/tbody/tr/td[2]/@title')
for title, href, time in zip(title_list, href_list, time_list):
img = self.get_img(href)
data['title'] = title
data['href'] = href
data['img'] = img
data['time'] = time
json_data.append(data)
print('已获取: ' + title)
data = {}
return json_data
def next_page(self):
self.page += 50
self.url = 'https://www.douban.com/group/search?start={}&cat=1013&group=146409&sort=time&q=%E9%97%B5%E8%A1%8C'.format(self.page)
def run(self, page_num):
json_data = []
flag = page_num
while page_num > 0:
json_data += self.get_data()
print('已获取第', flag - page_num + 1, '页')
self.next_page()
page_num -= 1
self.save_files('./data.json', json_data)
s = spider()
s.run(5) # 爬取5页
纯粹的笨方法爬取,没有多线程也没有异步,下面解释一下代码:
-
get_html(url)
:获取原始网页,整理成树结构,方便xpath语法解析。 -
get_img(href)
:获取帖子中的图片地址。 -
get_data()
:获取title文本,链接,发布时间等信息,return
返回json
数据。 -
next_page(url)
:爬取下一页,观察url信息,发现每下一页,url中的某个参数递增50,所以直接+50。 -
save_files(filename, array)
:把json
数据保存成data.json
文件。 -
run(page_num)
:设定爬取的页数,然后执行。
运行后爬取到data.json
文件:
3.2 用静态网页展示
- html代码
豆瓣租房小组
豆瓣租房小组图片预览
-
{{ index + 1 + (curPage - 1) * 15}}
{{ info.time }}
- CSS代码
body {
border: 1px solid #ddd;
margin: auto;
width: 1080px;
box-shadow: 2px 2px 10px 2px #ccc;
}
#header {
margin-bottom: 5em;
text-align: center;
}
#fileImport {
font-size: 18px;
}
#content ul {
margin: 0;
padding: 0;
}
#content ul li {
list-style: none;
margin: 1em 0.5em;
padding: 1em 0;
border-top: 1px solid #999;
}
.info-list .title-list {
display: flex;
}
.info-list .title-list span {
display: inline-block;
width: 3em;
font-style: italic;
font-size: 1.5em;
color: #eb5f00;
text-align: center;
}
.info-list .title-list a {
text-decoration: none;
font-size: 1.5em;
color: black;
}
.info-list .title-list a:hover {
color: #e79600;
}
.info-list .img-box {
display: flex;
margin-left: 4em;
}
.info-list .img-box img {
margin: 0.5em;
width: 200px;
height: 200px;
}
.info-list .release-time {
margin-left: 4em;
padding-top: 1em;
font-size: 16px;
color: #999;
}
- js代码
const index = new Vue({
el: '#content',
data: {
json_data: [], // 租房信息数据
num: 0, //多少条数据
pageNum: [], //总页数转化成列表
pageSize: 15, //每一页的数量
totalPage: 1, //总页数
curPage: 1, //当前页数
showPageList: 0, //当前显示的页面
},
// 监听器
watch: {
json_data(newData){
this.pageNum = []; // 若数据更新,页码先清零
this.num = newData.length;
this.totalPage = Math.ceil(this.num / this.pageSize);
this.totalPage = this.totalPage == 0 ? 1 : this.totalPage;
this.change_list();
this.getShowPageList(newData, this.curPage);
},
curPage(newPage){
this.getShowPageList(this.json_data, newPage);
}
},
// 事件处理方法
methods: {
// 点击触发选择文件按钮
files_click: function(){
$("#files").click();
},
// 读取json文件
get_json_data: function(e){
var self = this; // 保存外部作用域
var selectedFile = e.target.files[0];
var reader = new FileReader();
reader.readAsText(selectedFile);
reader.onload = function () {
self.json_data = JSON.parse(this.result); // 读取的是字符串,转换数组
}
},
// 页数转换成数组
change_list: function(){
for (let i = 1; i <= this.totalPage; i++) {
this.pageNum.push(i)
}
},
// 获取当前页应展示的数据
getShowPageList: function(data, index){
let begin = (index - 1) * this.pageSize;
let end = index * this.pageSize;
this.showPageList = data.slice(begin, end);
},
// 上一页
prevPage: function(e){
if(this.curPage != 1){
this.curPage --;
}
},
// 下一页
nextPage: function(e){
if(this.curPage != this.totalPage){
this.curPage ++;
}
},
// 点击页面跳转
link_curPage: function(e){
console.log(e.target.text);
let num = parseInt(e.target.text);
this.curPage = num;
}
}
})
最后我是用js的FileReader
接口读取的json
文件,查资料的时候有人说可以用Ajax读取,但我试了一下发现会产生跨域。手动导入文件显得太笨了......没办法,能力有限,后续有空再尝试优化一下这个问题。
代码就不解释了,思路就是读取json
文件,本质上这些json
数据都是数组,使用Vue可以轻松的展示这些数据