第十一章 数据可视化之数据采集
第一节 python爬虫基础
一 、爬虫概述
URL由三部分构成:协议://用户名:密码@子域名.域名.顶级域名:端口号/目录/文件名.文件后缀?参数=值#标志
二、数据获取
1 网络请求request库安装
Requests是用python语言基于urllib库编写、封装的。
方法 | 解释 | 代码 |
---|---|---|
requests.request() | 构造一个请求,支持以下各种方法 | |
requests.get() | 获取html的主要方法 | |
requests.head() | 获取html头部信息的主要方法 | |
requests.post() | 向html网页提交post请求的方法 | |
requests.put() | 向html网页提交put请求的方法 | |
requests.patch() | 向html提交局部修改的请求 | |
requests.delete() | 向html提交删除请求 | |
requests.options() | |
案例:来源:[简书]
import requests
url='http://www.httpbin.org'
r=requests.get(url).text
print(r)
# 导入requests库
import requests
# 定义base_url作为基础被测URL
base_url = 'http://httpbin.org'
# 发送get请求;打印响应状态码
r = requests.get(base_url+'/get')
print(r.status_code)
# 发送POST请求;打印响应状态码
r = requests.post(base_url+'/post')
print(r.status_code)
# 发送PUT请求;打印响应状态码
r = requests.put(base_url+'/put')
print(r.status_code)
# 发送DELETE请求,打印响应状态码
r = requests.delete(base_url+'/delete')
print(r.status_code)
2 参数传递
Requests库允许使用params关键词传递URL参数,以字符串字典来提供这些参数。如在GET请求中使用查询字符串(Query String)传递参数,在POST请求的请求体(Request Body)中传递参数
参数 | 含义 | 代码 |
params | 字典或字节序列, 作为参数增加到url中,使用这个参数可以把一些键值对以?key1=value1&key2=value2的模式增加到url中 | params= {'key1':' values', 'key2': 'values'} r = requests.get('http://www.httpbin.org/get', params=params) |
data | 字典,字节序或文件对象,重点作为向服务器提供或提交资源是提交,作为request的内容,与params不同的是,data提交的数据并不放在url链接里, 而是放在url链接对应位置的地方作为数据来存储。它也可以接受一个字符串对象。 | |
json | json格式的数据, json合适在相关的html,http相关的web开发中非常常见, 也是http最经常使用的数据格式, 他是作为内容部分可以向服务器提交。 | |
headers | 字典是http的相关语,对应了向某个url访问时所发起的http的头i字段, 可以用这个字段来定义http的访问的http头,可以用来模拟任何我们想模拟的浏览器来对url发起访问。 | hd = {'user-agent': 'Chrome/10'} r = requests.post('http://www.httpbin.org', headers=hd) |
cookies | 字典或CookieJar,指的是从http中解析cookie | |
auth | 元组,用来支持http认证功能 | |
files | 字典, 是用来向服务器传输文件时使用的字段。 | fs = {'files': open('data.txt', 'rb')} r = requests.post('http://python123.io/ws', files=fs) |
案例:来源:[简书]
# 导入requests库
import requests
# 定义base_url作为基础被测URL
base_url = 'http://httpbin.org'
# 定义请求所需的参数,参数之间以英文逗号隔开
param_data = {'uname':'Test00001','pwd':'123456'}
# 发送GET请求,格式如:requests.get(url,params)
r = requests.get(base_url+'/get',params=param_data)
print(r.url) # 输出请求的url
print(r.status_code) #输出响应的状态码
import requests
base_url = 'http://httpbin.org'
form_data = {'uname':'Test00002','pwd':'123456'}
# 发送POST请求,格式如:requests.post(url,data)
r = requests.post(base_url+'/post',data=form_data)
print(r.text) # 返回响应内容
案例:传递header参数,模拟浏览器的访问行为,以应对网站的反爬虫策略
import requests
header = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
r = requests.get('https://www.zhihu.com/explore',headers=header)
print(r.text)
3 属性
属性 | 说明 |
---|---|
r.status_code | http请求的返回状态,若为200则表示请求成功。 |
r.text | http响应内容的字符串形式,即返回的页面内容 |
r.encoding | 从http header 中猜测的相应内容编码方式 |
r.apparent_encoding | 从内容中分析出的响应内容编码方式(备选编码方式) |
r.content | http响应内容的二进制形式 |
案例:
import requests
r = requests.get(url='https://api.github.com/events')
print(r)
print(r.status_code)
print(r.encoding)
print(r.apparent_encoding)
print(r.json)
print(r.content)
案例:爬取网页通用代码
try:
r=requests.get(url,timeout=30)#请求超时时间为30秒
r.raise_for_status()#如果状态不是200,则引发异常
r.encoding=r.apparent_encoding #配置编码
return r.text
except:
return "产生异常"
案例:爬虫网页、图片及文件[源码]
import requests
keyword ='html_image'
try:
keyword={'f':keyword}
r = requests.get("http://www.w3school.com.cn/tiy/t.asp",params = keyword)
print(r.url)
print(r.text)
except:
print("error")
import requests
keyword ='html_image'
print(keyword)
try:
keyword={'f':keyword}
r = requests.get("http://www.w3school.com.cn//i/eg_mouse.jpg")
print(r.url)
print(r.content)
f = open('w3c.jpg', 'wb')
f.write(r.content)
f.close()
except:
print("error")
案例:使用r.encoding解决requests库中文乱码[源码]
import requests
response = requests.get('http://www.dytt8.net/index.htm')
# 使用apparent_encoding可以获得真实编码
# response.encoding = response.apparent_encoding
response.encoding = 'gb2312'
print(response.text[200:300])
案例:使用open函数保存豆瓣短评[源码]
import requests
from lxml import etree
#发送Request请求
url = 'https://book.douban.com/subject/1054917/comments/'
head = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36'}
#解析HTML
r = requests.get(url, headers=head)
s = etree.HTML(r.text)
comments = s.xpath('//div[@class="comment"]/p/text()')
#print(str(comments))#在写代码的时候可以将读取的内容打印一下
#保存数据open函数
with open('E:/alienbrain/Projects/Asm/D3/pinglun.txt','w',encoding='utf-8') as f:#使用with open()新建对象f
for i in comments:
print(i)
f.write(i+'\n')#写入数据,文件保存在上面指定的目录,加\n为了换行更方便阅读
第二节 数据解析
BeautifulSoup用于从html/xml中提取数据,实现文档导航、查找、修改。
1 基本API
eautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐安装。
解析返回的4种对象
对象 | 属性 | 代码 |
Tag | 有两个重要的属性,name 和 attrs | print soup.name print soup.a.name print soup.attrs print soup.p.attrs #在这里,我们把 p 标签的所有属性打印输出了出来,得到的类型是一个字典。 print soup.p['class'] #单独获取某个属性 print soup.p.get('class') ##单独获取某个属性 跟上面一样的 |
NavigableString | 获取标签内部的文字 | print soup.p.string print type(soup.p.string) #<class 'bs4.element.NavigableString'> |
BeautifulSoup | 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称: | print type(soup.name) #<type 'unicode'> print soup.name # [document] print soup.attrs #{} 空字典 |
Comment | 其实输出的内容仍然不包括注释符号 | #判断代码如下: if type(soup.a.string)==bs4.element.Comment: print soup.a.string
print soup.a print soup.a.string print type(soup.a.string) |
搜索文档树
(1)find_all( name , attrs , recursive , text , **kwargs )
find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件,可以根据标签名,属性,内容查找文档。
其他一些类似的用法:
find_parents()返回所有祖先节点,find_parent()返回直接父节点。
find_next_siblings()返回后面所有兄弟节点,find_next_sibling()返回后面第一个兄弟节点。
find_previous_siblings()返回前面所有兄弟节点,find_previous_sibling()返回前面第一个兄弟节点。
find_all_next()返回节点后所有符合条件的节点, find_next()返回第一个符合条件的节点
find_all_previous()返回节点后所有符合条件的节点, find_previous()返回第一个符合条件的节点
(2)CSS选择器
CSS 时标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list
.表示class #表示id
标签1,标签2 找到所有的标签1和标签2
标签1 标签2 找到标签1内部的所有的标签2
[attr] 可以通过这种方法找到具有某个属性的所有标签
[atrr=value] 例子[target=_blank]表示查找所有target=_blank的标签
案例:[源码]
#导入库 bs4 lxml requests
from bs4 import BeautifulSoup
import re
#使用官方字符串来演示
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
#创建 beautifulsoup 对象:
soup = BeautifulSoup(html,'lxml')
#传正则表达式
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
#列表
soup.find_all(["a", "b"])
print(tag.name)
#可以匹配任何值,
for tag in soup.find_all(True):
print(tag.name)
#CSS
#通过标签名查找
print(soup.select('title'))
print (soup.select('a'))
print (soup.select('#link1'))
print (soup.select('p #link1'))
案例:[源码]
from bs4 import BeautifulSoup
import requests
try:
r=requests.get("http://www.w3school.com.cn/tiy/t.asp?f=jseg_text")
soup = BeautifulSoup(r.text,'html.parser')
href=soup.find_all('a')
print(href)
except:
print("error")
案例:[源码]
import re
str='data=["中国","美国","日本","英国"];city=["广州","北京","上海","深圳"];'
result = re.match(r'data=(.*?);',str)
print(result.group(1))
第三节 数据存储和知识图谱可视化
一 存储数据
1 存储为文本文件
data=["学生成绩花名册",
"姓名 语文 数学",
"张三 100 98",
"李四 99 100",
"王五 99 99"]
with open('data.txt','w+',encoding='utf-8') as f:
for d in data:
f.writelines(d+'\n')
2 对mysql数据库的操作
python3使用pymysql连接mysql服务器。
注:
spyder3中运行import pymysql 报错
解决:安装pymysql
anacoda的cmd中输入 conda install pymysql
爬虫只对数据库进行增删改查(CRUD)操作,因此需要先创建数据库及表。
案例:[源码]
创建一个数据库spiderDB,初始账号root,密码123456,在MySQL-Front中创建如下表格:
import pymysql
data=[['张三', '100', '99','97'],
['李四', '97', '98','99'],
['王五', '99', '100','100'],
['赵六','100','99','98']]
# pymysql.connect(数据库url,用户名,密码,数据库名 )
db = pymysql.connect("localhost", "root", "123456", "spiderdb", charset = 'utf8')
cursor = db.cursor()
try:
for d in data:
cursor.execute("INSERT INTO scores2019(name,Chinese,Maths,English) VALUES(%s,%s,%s,%s)", (d[0], d[1], d[2],d[3]))
print("ok")
db.commit()
except:
db.rollback()
db.close()
效果
3 对json操作
案例:写入json字典
#dict写入json
import json
dictObj = {
'张三':{
'age': 23,
'city': '武汉',
'skill': 'python'
},
'李四': {
'age': 33,
'city': '北京',
'skill': 'js'
}
}
jsObj = json.dumps(dictObj)
fileObject = open('写入json.json', 'w+',encoding='utf-8')
fileObject.write(jsObj)
fileObject.close()
4 用pandas库读取和存储数据[原文链接]
pandas库是python数据处理领域非常重要也是非常强大的一个库。
使用pandas库读取数据的常用函数:
import pandas as pd
table = pd.read_table('http:\\somelink.csv') # 读取任一结构型文本数据
csv = pd.read_csv(r'c:\data.csv') # 读取csv文件
excel = pd.read_excel(r'c:\data.xlsx') # 读取Excel文件
使用 pd.read_table( ) 函数时,可以通过sep变量自定义数据的分割符,从而方便地对txt文本数据进行结构化读取:
df = pd.read_table(path, sep = '|') # 用“|”对每行数据拆分
此外,还可以自定义表格的标题行:
user_cols = ["Name", 'Data_A', 'Data_B']
df = pd.read_table(path, sep = '|', head = None, names = user_cols)
还可以自定义读取表格的某些行和列:
# 读取特定的列(按顺序编号)
df = pd.read_excel(filename, sheetname, usecols = [0,1,4,5,6,7,12,13])
# 跳过特定行
df = pd.read_excel(filename, sheetname, skiprows=[0]) # 跳过第一行
对于导入的 df 数据,可以通过向屏幕打印如下参数来对数据的导入结果进行快速检验:
df.head() # 返回DataFrame的前几行
df.describe() # 返回DataFrame中数字部分的概要信息
df.shape() # 返回DataFrame的大小(列数x行数)
df.dtypes() # 返回DataFrame中各列的数据类型
用pandas库的 pd.read_sql_query 函数读取PostSQL数据库中的数据:
注:anaocoda可能需要 pip install psycopg2,或者升级anacoda到新版本,会自动升级psycopg2
import psycopg2 # 用psycopg2库连接数据库
import pandas as pd # 用pd.read_sql_query在本地创建数据表
# 定义数据库的名称,用户名,密码,链接地址和接口
dbname = 'database'
username = "admin"
password = "password"
hosturl = "test.redshift.amazonaws.com"
portnum = '5400'
# 连接数据库,返回一个数据库对象
try:
conn = psycopg2.connect("dbname=%s port=%s user=%s host=%s password=%s" %(dbname, portnum, username, hosturl, password))
except:
print("Fail in connect the database!") #如果连接失败,打印该语句到屏幕上
# 定义查询语句query
# 这里需要注意:
# 1. 查询语句和SQLWorkbench中的几乎一模一样,唯一的区别是语句的最后要以英文分号结尾
# 2. 在Python中无法在字符串中直接输入单引号,要在单引号前加上反斜杠 \
query = 'SELECT data, teststep FROM db.database WHERE rownumber < 100;')
# 根据query的语句到conn数据库中查询,并将结果返回给data这个DataFrame
df = pd.read_sql_query(query, conn) # 两个参数:检索语句和连接的数据库对象
pandas库导出数据到Excel的方法如下:[源码]
#导入包import pandas as pd
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randn(10,4))#创建随机值
#print(df.head(2))#查看数据框的头部数据,默认不写为前5行,小于5行时全部显示;也可以自定义查看几行
print(df.tail())##查看数据框的尾部数据,默认不写为倒数5行,小于5行时全部显示;也可以自定义查看倒数几行
df.to_csv('E:/Projects/PandasNumpy.csv')#存储到CSV中
#df.to_excel('E:/Projects/PandasNumpy.xlsx')#存储到Excel中(需要提前导入库 pip install openpyxl)
案例:爬取豆瓣书评,并保存为excel[源码]
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 26 00:35:08 2019
@author: Administrator
"""
import requests
from lxml import etree
#发送Request请求
url = 'https://book.douban.com/subject/1054917/comments/'
head = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36'}
#解析HTML
r = requests.get(url, headers=head)
s = etree.HTML(r.text)
comments = s.xpath('//div[@class="comment"]/p/text()')
#print(str(comments))#在写代码的时候可以将读取的内容打印一下
#保存数据pandas函数 到CSV 和Excel
import pandas as pd
df = pd.DataFrame(comments)
#print(df.head())#head()默认为前5行
df.to_csv('E:/alienbrain/Projects/Asm/D3/PandasNumpyCSV.csv')
#df.to_excel('E:/alienbrain/Projects/Asm/PandasNumpyEx.xlsx')
如果需要在一个Excel中保存多个sheet,需要先定义一个指向Excel路径的对象,然后向该对象中添加sheet,最后将Excel保存并关闭:
writer = pd.ExcelWriter(filename) # 定义一个向Excel写入数据的对象
df1.to_excel(writer,'Data1') # 向该Excel中写入df1到Data1这个sheet
df2.to_excel(writer,'Data2') # 向该Excel中写入df2到Data2这个sheet
writer.save() # 保存Excel表格
writer.close() # 关闭Excel表格
在输出数据的时候,还可以自定义输出时列的先后顺序:
# 写入数据到sheet中,并按output_cols的顺序对列进行排序
output_cols = ['Data_A', 'Data_B', 'Name']
df.to_excel(writer,'Data', columns = output_cols)