导入所需要包:
import requests # 用于获取响应 from lxml import etree # 用于解析HTML网页 import time # 用于控制时间 import pymysql # 用于连接数据库
完成所有库的安装后就可以进行数据的爬取。
爬取步骤解析:
在浏览器中打开链家网站上海租房:上海租房信息_上海出租房源|房屋出租价格【上海贝壳租房】 (lianjia.com)
创建.py文件,导入所需的包:
在导包之前可以在根目录下创建一个.py文件,在文件中将Python连接MySQL的方法写入到其中,这么做的原因是因为我们后面要多次使用到这个方法一遍遍的去写连接数据库的方法太过于复杂。
# 导入python连接MySQL的包 import pymysql # 使用 connect 创建连接对象 conn = pymysql.connect( host='localhost', # MySQL服务器地址 port=3306, # MySQL服务器端口号 user='root', # 用户名 password='010531', # 密码 database='爬虫数据', # 数据库名称 charset='utf8' # 连接编码,存在中文的时候,连接需要添加charset='utf8',否则中文显示乱码。 ) # 创建游标对象 cur = conn.cursor()
导入所需的包,第一段代码是设置编码为utf-8,防止乱码。
# -*- codeing = utf-8 -*- import requests # 用于获取响应 from lxml import etree # 用于解析HTML网页 import time # 用于控制时间 # 导入写好的连接数据库的包 import sql as s
获取网页响应并解析
在浏览器中打开网页后点击键盘上的“F12”,按下后会打开控制台,如下图所示:
打开控制台后点击刷新网页-network-找到最前面的(zufang)点击进去。
点开后找到-heaers-在下面找到-User-Agent,找到后将这两个里面包含的信息复制下来。
# 伪装爬虫,伪装成浏览器进行访问 headers = { 'User-Agent': 'Mozilla/5.0(Windows NT 6.1; WOW64) AppleWebKit / 537.36(KHTML, like Gecko) ' } # 定义网页 url = 'https://sh.lianjia.com/zufang' # 使用requests.get()方法获取响应 # 其中第二个参数:headers= 等号右边的变量是你上面所定义好的headers res = requests.get(url, headers=headers) # 用于解决etree.HTML解析异常 html = etree.HTML(res.text)
使用Xpath获取字段信息
Xpath扩展程序安装大家可以安装这个文章进行安装(https://www.cnblogs.com/ubuntu1987/p/11611111.html)
XPath语法可以看一下教程(https://www.w3school.com.cn/xpath/index.asp)
使用XPath获取所需字段信息,将鼠标停留在标题上,右击–检查
点击检查后会跳出控制台,控制台会自动帮助我们选中该数据所在标签,此时,我们只需要将鼠标再次停留在该标签上方进行-右击-Copy-CopyXpath,这样一个完整的XPath语法就复制下来了。
下面验证一下上面复制的XPath语法是否正确,在浏览器中打开XPath扩展,将复制的XPath语法粘贴在左边,右边会显示你XPath语法所选中的数据,在浏览器页面也会将能够获取的数据进行标黄。
但是我们发现它只选中了第一个房源的标题,通过观察网页源代码后发现每一个房源都是用一个div标签装起来的,所以判断是否是因为倒数第二个div标签只选择了第一个div标签的原因,去除[1]之后就可以选中该页面所有的标题。
获取到所需数据的XPath语法后将语法使用html.xpath()方法获取每一个字段的数据,每一个XPath语法的最后需添加/text()表示文本。
# 使用xpath获取 房源标题 信息 name = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[1]/a/text()') # 去除列表中的 换行符 /n name = [x.strip() for x in name if x.strip() != ''] print(name) # 所在区 district = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/a[1]/text()') print(district) # 所在路 street = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/a[2]/text()') print(street) # 房屋面积 floor_space1 = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/text()[5]') # 定义一个空列表 floor_space = [] for i in floor_space1: # 将获取到的数据去除不需要的符号、空格、换行符 floor_space1 = str(i).replace('㎡', '').replace("/n", '').replace(' ', '') floor_space.append(floor_space1) # 使用.append()方法将处理过的数据已追加的方存入列表中 print(floor_space) # 朝向 orientation = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/text()[6]') print(orientation) # 房型 house_type = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/text()[7]') # 去除列表中的 换行符 /n house_type = [x.strip() for x in house_type if x.strip() != ''] print(house_type) # 价格 price = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/span/em/text()') print(price, '/n')
将获取的数据存入MySQL数据库中,这一步需要你的电脑中是有MySQL数据库的。
# 使用 execute() 方法执行 SQL,如果表存在则删除 s.cur.execute("DROP TABLE IF EXISTS LJ_sh") # 使用预处理语句创建表 sql = """CREATE TABLE LJ_sh( name VARCHAR(255), district VARCHAR(255), street VARCHAR(255), floor_space INT, orientation VARCHAR(255), house_type VARCHAR(255), price INT )""" # 执行sql语句 s.cur.execute(sql) # 将爬取的数据存入MySQL数据库中 for i in range(0, len(name)): sql = """INSERT INTO LJ_sh VALUES('%s','%s','%s','%s','%s','%s','%s')""" / %(name[i], district[i], street[i], floor_space[i], orientation[i],house_type[i], price[i]) # 执行sql语句 s.cur.execute(sql) # 提交到数据库执行 s.conn.commit()
第一页的数据我们成功爬取后,我们发现一页有60条数据,网站不止有一页,我们可以对网页进行翻页处理。
通过观察网页我们发现当我们点击第二页时,网站的链接发生了变化:
当我们再次点击翻页时又再次发送变化:
由此我们可以得出我们可以根据网址中变化进行翻页处理,可以使用循环语句进行翻页。
# 使用循环语句控制爬虫爬取几页 # 从第几页开始爬取,爬到多少页结束 for i in range(1, 100): print('第', i, '页') time.sleep(10) # 控制程序访问时间,防止请求过快被对方的服务器禁止请求 url = f'https://sh.lianjia.com/zufang/pg{i}'
以上就是我们爬虫的基本流程。
了解流程后我们需要对爬虫进行代码优化:
以下是完整的爬虫代码:
# -*- codeing = utf-8 -*- import requests # 用于获取响应 from lxml import etree # 用于解析HTML网页 import time # 用于控制时间 # 导入写好的连接数据库的包 import sql as s class LJ_sh(object): def send_request(self, url): # 伪装爬虫,伪装成浏览器进行访问 headers = { 'User-Agent': 'Mozilla/5.0(Windows NT 6.1; WOW64) AppleWebKit / 537.36(KHTML, like Gecko) ' } # 获取响应 res = requests.get(url, headers=headers) # 将html字符串转换成element对象,用于解决etree.HTML解析异常 html = etree.HTML(res.text) return html def parse_data(self, html): # 创建空列表用于存放数据 data = {} # 使用xpath获取 房源标题 信息 name = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[1]/a/text()') # 去除列表中的 换行符 /n data['name'] = [x.strip() for x in name if x.strip() != ''] print(data['name']) # 所在区 data['district'] = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/a[1]/text()') print(data['district']) # 所在路 data['street'] = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/a[2]/text()') print(data['street']) # 房屋面积 floor_space1 = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/text()[5]') # 定义一个空列表 floor_space = [] for i in floor_space1: # 将获取到的数据去除不需要的符号、空格、换行符 floor_space1 = str(i).replace('㎡', '').replace("/n", '').replace(' ', '') floor_space.append(floor_space1) # 使用.append()方法将处理过的数据已追加的方存入列表中 data['floor_space'] = floor_space print(data['floor_space']) # 朝向 data['orientation'] = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/text()[6]') print(data['orientation']) # 房型 house_type = html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/p[2]/text()[7]') # 去除列表中的 换行符 /n data['house_type'] = [x.strip() for x in house_type if x.strip() != ''] print(data['house_type']) # 价格 data['price']= html.xpath('//*[@id="content"]/div[1]/div[1]/div/div/span/em/text()') print(data['price']) # 调用存储数据的方法 self.store_data(data) def store_data(self, data): # 将爬取的数据存入MySQL数据库中 for i in range(0, data['name']): sql = """INSERT INTO LJ_sh VALUES('%s','%s','%s','%s','%s','%s','%s')""" / % (data['name'][i], data['district'][i], data['street'][i], data['floor_space'][i], data['orientation'][i], data['house_type'][i], data['price'][i]) # 执行sql语句 s.cur.execute(sql) # 提交到数据库执行 s.conn.commit() def start(self): # 使用循环语句控制爬虫爬取几页 # 从第几页开始爬取,爬到多少页结束 for i in range(1, 50): print('第', i, '页') time.sleep(10) # 控制程序访问时间,防止请求过快被对方的服务器禁止请求 url = f'https://sh.lianjia.com/zufang/pg{i}' # 将网页传入方法中获取响应 html = self.send_request(url) # 获取数据 self.parse_data(html) # 关闭游标对象 s.cur.close() # 关闭数据库连接 s.conn.close() if __name__ == '__main__': # 使用 execute() 方法执行 SQL,如果表存在则删除 s.cur.execute("DROP TABLE IF EXISTS LJ_sh") # 使用预处理语句创建表 sql = """CREATE TABLE LJ_sh( name VARCHAR(255), district VARCHAR(255), street VARCHAR(255), floor_space INT, orientation VARCHAR(255), house_type VARCHAR(255), price INT )""" # 执行sql语句 s.cur.execute(sql) LJ = LJ_sh() LJ.start()
数据成功存放MySQL数据库中:
在MySQL中进行数据清洗
查询数据是否有完全重复的数据,如果有则只保留一条,其余删除。
delete from lj_sh_2 where id not in ( select a.max_id from (select max(id) as max_id from lj_sh_2 group by name,district,street,floor_space,orientation,house_type,price) as a );
在PyCham中进行数据可视化
在此文章中选用的是pyecharts模块进行数据可视化的呈现。
pyechats教程:简介 – pyecharts – A Python Echarts Plotting Library built with love.
pyecharts图例示范:中文简介 – Document (pyecharts.org)
在进行数据可视乎之前需要导入相对应的第三方库:
如何安装第三方库如果忘记就看看前面。
# 导入相关模块与所需包 from pyecharts.charts import * from pyecharts import options as opts # 导入写好的连接数据库的包 import sql as s
1、每个区平均房价
# 导入相关模块与所需包 from pyecharts.charts import * from pyecharts import options as opts # 导入写好的连接数据库的包 import sql as s # 查询每个区平均房价 # 执行sql语句 s.cur.execute("""SELECT district,ROUND(AVG(price),0) FROM lj_sh GROUP BY district ORDER BY AVG(price) DESC;""") data = s.cur.fetchall() print(data) # 画图 ( Pie() # 饼图 # 导入数据 设置内外半径 设置标签样式 .add("", data, radius=["40%", "65%"], label_opts=opts.LabelOpts(formatter="{b}:{c}")) .set_global_opts( # 设置标题 设置标题位置 title_opts=opts.TitleOpts(title="每个区平均房价", pos_left='center'), # 设置不显示图例 legend_opts=opts.LegendOpts(is_show=False) ) # 进行图形渲染 .render("test1.html") )
执行上述代码后会生成一个test1.html文件,在浏览器打开该文件:
根据上图我们可以看出上海当前平均房价最高的是徐汇区。
2、每个区域房源信息
# 导入相关模块与所需包 from pyecharts.charts import * from pyecharts.components import * # 导入写好的连接数据库的包 import sql as s # 查询每个区的名字 s.cur.execute("""SELECT district FROM lj_sh GROUP BY district;""") # 获取查询出来的所有信息 name = s.cur.fetchall() # 拆分元组,转换成列表 name = [j for i in name for j in i] print(name) # 创建空列表用于存放每个区房源信息 data = [] for i in name: # 查询每个区的房源信息 s.cur.execute(f"""SELECT * FROM lj_sh WHERE district = "{i}" ORDER BY price;""") data.append(s.cur.fetchall()) print(data) # 定义表头 title = ['标题', '区', '街道', '房屋面积', '朝向', '房型', '价钱'] # 定义选择卡方法 tab = Tab() for i in range(0, len(name)): # 画图 table = ( Table() # 表格图例 .add(title, data[i]) # 设置表头、数据 ) # 设置每一页的图例以及每一选择卡的标签 tab.add(table, name[i]) # 图例渲染 tab.render("test1.html") # 关闭连接 s.conn.close() s.cur.close()
执行上述代码后会生成一个test1.html文件,在浏览器打开该文件:
根据上图大家可以根据自己的需求去寻找自己想要的房源。
3、当前那种户型最多
# 导入相关模块与所需包 from pyecharts.charts import * from pyecharts import options as opts # 导入写好的连接数据库的包 import sql as s # 查询户型及或许数量 s.cur.execute("""SELECT house_type, COUNT(house_type) FROM lj_sh GROUP BY house_type;""") # 获取所有查询得到的数据 data = s.cur.fetchall() data = [i for i in data] print(data) # 画图 chart = ( WordCloud() # 词云图 # 添加数据,设置字体格式 is_draw_out_of_bound: bool = False .add("", data, word_size_range=[1, 300], textstyle_opts=opts.TextStyleOpts(font_family="cursive")) .set_global_opts( # 设置标题,位置 title_opts=opts.TitleOpts(title="户型", pos_left="center") ) # 图形渲染 .render("test1.html") )
执行上述代码后会生成一个test1.html文件,在浏览器打开该文件:
根据上图我们可以看出当前较多的户型为三室二厅一卫。
文章到这里就结束啦。
感谢大家的观看!!
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/python/278342.html