网络爬虫
网络爬虫(web crawler),也叫网络蜘蛛(spider),是一种用来自动抓取Web信息数据的程序或者脚本。搜索引擎和其他一些网站使用网路爬虫更新他们的内容。
简介
获取页面
以下是基于Python 3 编程语言的爬虫。
request
request模块是Python的urllib标准库下的模块,直接使用即可。request模块可以方便获取URL内容。
from urllib.request import urlopen
html = urlopen('https://www.baidu.com')
print(html.read())
requests
requests是一个Python第三方HTTP库。该软件的目的是使HTTP请求更简单,更人性化。
可以使用pip安装requests:
pip install requests
使用 Requests 发送网络请求非常简单。如获取某个网页,使用get函数,会返回一个Response对象,我们可以从这个对象中获取所有我们想要的信息。
import requests
response = requests.get('https://www.baidu.com')
print(response.content)
了解更多 >> Requests文档 Requests源代码
解析提取
BeautifulSoup
Beautiful Soup是一个Python第三方库,可以用来解析html文档,方便提取需要的数据。
可以使用pip安装:
pip install beautifulsoup4
如使用BeautifulSoup提取一个页面标题
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen('https://www.baidu.com')
bs = BeautifulSoup(html.read(), 'html.parser')
bs.title
XPath
XPath即XML路径语言(XML Path Language),一种查询语言,用于在XML中选择节点。在Python中,可以使用lxml这个第三方库来解析页面,然后通过XPath来选取内容。
可以使用pip安装lxml:
pip install lxml
如下简单示例,使用requests获取页面,使用lxml解析并用XPath提取标题文本。
import requests
from lxml import etree
response = requests.get('https://www.baidu.com')
html = etree.HTML(response.content)
title = html.xpath('//title//text()')
表格提取
Pandas
如果只要获取页面中的表格,可以使用Pandas的read_html()
函数,简单方便。可以使用pip安装Pandas:
pip install pandas
获取某个网页上所有表格,生成一个由DataFrame对象组成的列表:
import pandas as pd
dfs = pd.read_html('https://pandas.pydata.org/docs/reference/io.html')
read_html()
函数默认使用lxml解析,还可以设置为BeautifulSoup4和html5lib。函数格式pandas.read_html(*args, **kwargs)
。可选参数
BeautifulSoup + Pandas
XPath + Pandas
pdfplumber
pdfplumber是一个Python库,可以用于PDF查看信息,提取表格等。可以使用pip安装pdfplumber:
pip install pdfplumber
如获取某个PDF文件上多个表格:
import pdfplumber
import pandas as pd
tables = []
with pdfplumber.open("path/to/file.pdf") as pdf:
for i in range(len(pdf.pages)):
page = pdf.pages[i]
table = page.extract_table()
tables.extend(table[1:])
df = pd.DataFrame(data=tables, columns=table[0])
print(df)
了解更多 >> GitHub:jsvine/pdfplumber
URL解析
名称 | 描述 | 示例 |
---|---|---|
urllib.parse | 用于解析 URL,Python标准库,使用时导入即可。from urllib import parse
常用函数: urlparse() 将URL字符串分割成其组成部分。quote() URL编码。 unquote() URL解码。
|
parse.unquote('https://example.com/%E9%A6%96%E9%A1%B5')
|
数据存储
二进制文件
简单方便,数据不经过解码编码处理,直接将二进制格式保存为文件。一般用于图片,音频,视频,或响应的原文件等。下面使用Python语言保存图片:
import requests
import os
res = requests.get('http://img0.bdstatic.com/static/searchresult/img/logo-2X_32a8193.png')
with open('download.png', 'wb') as f: # 'wb'表示以二进制格式打开文件用于写入
f.write(res.content) # res.content为响应的二进制格式内容
print("图片保存完成。")
实际存储中,可以进一步完善,如保存过的文件不再下载,文件名与网页中相同。示例如下:
import os
import requests
def download_file(url, save_path,headers=None):
save_path = os.path.expanduser(save_path)
file_name = url.split('/')[-1]
file_path = os.path.join(save_path, file_name) #直接加号拼接需注意:windows使用\和liunxs使用/分割路径。
if not os.path.exists(save_path): #判断是否已存在下载目录
os.makedirs(save_path) #新建下载目录
if not os.path.exists(file_path): #判断是否已存在
res = requests.get(url,headers=headers)
with open(file_path, 'wb') as f: # 'wb'表示以二进制格式打开文件用于写入
f.write(res.content) # res.content为响应的二进制格式内容
print("{}文件保存完成。".format(file_path))
else:
print("文件已存在。")
download_file('http://img0.bdstatic.com/static/searchresult/img/logo-2X_32a8193.png', '~/图片保存目录')
Python读写文件主要使用open()函数,该函数返回一个文件对象(file object),如果执行失败会抛出OSError。文件对象共有3种类别:原始二进制文件, 缓冲二进制文件,文本文件。
函数open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
最常用的是file和modec参数。如`file_test = open('test.txt', 'r')`表示以只读文本模式创建文件对象,存储在file_test变量中。参数mode是一个可选字符串,用于指定打开文件的模式,默认值是 'r' 。文件对象格式默认为文本模式('t'),可以指定二进制模式('b'),可用的模式有:
模式 | 描述 | 文件不存在 | 文件的指针位置 | 写操作下原文件内容 |
---|---|---|---|---|
r | 只读 | 抛出FileNotFoundError | 文件的开头 | 不能写 |
rb | 二进制模式只读 | 抛出FileNotFoundError | 文件的开头 | 不能写 |
r+ | 读写 | 抛出FileNotFoundError | 文件的开头 | 清空内容 |
rb+ | 二进制模式读写 | 抛出FileNotFoundError | 文件的开头 | 清空内容 |
w | 只写 | 创建文件 | 文件的开头 | 清空内容 |
wb | 二进制模式只写 | 创建文件 | 文件的开头 | 清空内容 |
w+ | 读写 | 创建文件 | 文件的开头 | 清空内容 |
wb+ | 二进制模式读写 | 创建文件 | 文件的开头 | 清空内容 |
a | 只写追加 | 创建文件 | 文件结尾 | 不变 |
ab | 二进制模式只写追加 | 创建文件 | 文件结尾 | 不变 |
a+ | 读写追加 | 创建文件 | 文件结尾 | 不变 |
ab+ | 二进制模式读写追加 | 创建文件 | 文件结尾 | 不变 |
注:
- r+, rb+ 模式下,如果文件对象先调用read()再调用write(),等同与追加模式
- FileNotFoundError为OSError的子类
在处理文件对象时,最好使用 with 关键字。能够在结束时自动关闭文件对象,不需要自己调用文件对象的close()函数。
纯文本文件
纯文本文件也简单方便,不用转为其他数据格式,直接存储即可。如:
# 保存文本
with open('test.txt', 'w') as f:
data = 'txt data'
f.write(data)
print("保存完成")
# 读取文本
with open('test.txt', 'r') as f:
data = f.read()
print(data)
CSV文件
CSV (Comma Separated Vaules) 格式是电子表格和数据库中最常见的输入、输出文件格式。csv格式一般为:一行一条记录,记录间不同字段值用逗号分隔。如
code, open, high, low, close, apple, 12, 16, 11.2, 14, pear, 12.5, 15, 12.5, 13.5
在python中处理csv格式可以使用标准库csv,直接导入即可。方便用户进行列表或字典格式与csv格式转化。
csv文件的读取,使用csv.reader()或csv.DictReader()函数,函数会生成一个Reader对象。csv.reader()读取的每行以列表形式保存在可迭代对象Reader中;csv.DictReader()生成的Reader对象,每行为字典格式(python 3.8开始)。
csv文件的写入,使用csv.writer()或csv.DictWriter()函数,函数会生成一个Writer对象。Writer对象有2个写入函数:writerow()函数,用于写入单行;writerows(rows)函数,用于写入多行。
import csv
# 列表数据,写入csv文件
head_row = ['code', 'open', 'high', 'low', 'close',]
rows = [['apple',12, 16, 11.2, 14],
['pear', 12.5, 15, 12.5, 13.5],
]
with open('test.csv','w', newline='') as f:
csv_writer = csv.writer(f) #生成一个Writer对象
csv_writer.writerow(head_row) #writerow写入单行
csv_writer.writerows(rows) #writerows写入多行
print('test.csv 保存完成 \n')
# 字典数据,写入csv文件
fieldnames = ['first_name', 'last_name']
row = {'first_name': 'Baked', 'last_name': 'Beans'}
rows = [{'first_name': 'Lovely', 'last_name': 'Spam'},
{'first_name': 'Wonderful', 'last_name': 'Spam'},
]
with open('test.csv', 'w', newline='') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerow(row)
writer.writerows(rows)
print('test.csv 保存完成 \n')
# 直接读取文本
with open('test.csv') as f:
data = f.read()
print('test.csv直接读取文本内容如下:')
print(data)
# 读取csv文件
with open('test.csv','r', ) as f:
rows = csv.reader(f)
print('读取csv每行到列表:')
for row in rows:
print(row)
CSV文件的存储读取,也可以使用其他软件,如Pandas。
了解更多 >> Python文档:标准库csv
JSON文件
JSON (JavaScript Object Notation) 是一种流行的数据交换格式。 JSON数据类型有:字符串(string)、数值(number)、true、false、 null、无序的键值对集合(object)或者有序的列表(array)。因为都是常见的数据结构,使得JSON格式数据可以在编程语言之间进行交换数据。object和array的值可以嵌套,这两种数据类型描述如下: * 无序的键值对集合(object):使用{ }
表示,键与值之间用:
分隔,键值对之间使用,
分割。如{‘name’:‘a’, ‘age’:12} * 有序的列表(array):使用[ ]
表示,值之间用,
分隔。如[‘a’, 12]
下面一个json格式数据:
{ "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "http://www.example.com/image/481989943", "Height": 125, "Width": 100 }, "Animated": false, "IDs": [116, 943, 234, 38793] } }
Python中处理JSON格式可以使用标准库json。json编码器和解码器默认如下转化:
JSON数据类型 | 描述 | 转为python数据类型 | 由python数据类型转入 |
---|---|---|---|
string | 字符串,用双引号括起来 | str | str |
number | 数值 | int或float | int, float, int 和 float 派生的枚举 |
true | 布尔值 true | True | True |
false | 布尔值 false | False | False |
null | 空值 | None | None |
object | 无序的键值对集合 | dict | dict |
array | 有序列表 | list | list, tuple |
json常用两个函数:dumps()将python数据类型编码为json格式字符串,loads()将json格式字符串解码为python数据类型。对于从文件读写json格式数据,可以使用dump()和load()函数,会自动转换格式。
import json
data = {"name":"image", 'Height':800,'Animated':False}
# 格式转换
json_data = json.dumps(data) #转为json格式字符串
print(type(json_data), json_data)
data = json.loads(json_data) #转为python数据类型
print(type(data),data)
# 读取保存数据,并自动转换相应格式
with open('test.txt', 'r+') as f:
json.dump(data, f) #转化为json格式文本对象,并写入文件
print("保存完成")
with open('test.txt', 'r+') as f:
data = json.load(f) #从文件读取json格式,并转化为python数据类型
print(type(data),data)
关系型数据库
常用关系型数据库SQLite,Mysql,PostgreSQL等。
SQLite使用简单,不需要安装配置,一个数据库就是一个文件。Python中可以使用标准库sqlite3来操作。先创建一个 Connection 对象,它代表数据库。再调用Connection对象的cursor()方法创建一个 Cursor 游标对象。然后调用 Cursor对象的 execute() 方法来执行 SQL 语句。也可以使用Pandas软件来操作。
SQLite一共5种数据类型,如下:
SQLite类型 | 描述 | 从Python类型转入 | 默认转换为Python类型 |
---|---|---|---|
NULL | 空值 | None | None |
INTEGER | 整数,根据值的大小存储,最大8字节,即最大2^63-1=9223372036854775807。 | int | int |
REAL | 浮点数, 存储为 8 字节的 IEEE 浮点数字。 | float | float |
TEXT | 文本字符串,使用数据库编码(UTF-8、UTF-16BE 或 UTF-16LE)存储。 | str | 取决于 text_factory , 默认为 str |
BLOB | 二进制值,直接存储,不经过转换,如存储图片等。 | bytes | bytes |
下面使用Python的标准库sqlite3操作示例:
import sqlite3
# 生成Connection 对象,它代表数据库。没有会创建一个。
conn = sqlite3.connect('test.db')
# 生成Cursor对象
cur = conn.cursor()
# 查询是否存在product表
sql = "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='product';"
result = cur.execute(sql) #调用execute函数执行sql语句
has_table = result.fetchone() #调用一次fetchone()函数,获取查询结果集的下一行
if has_table[0]: # 查询结果的每一行为元组,
print('已存在product表。')
else:
# sql语句
sql = '''CREATE TABLE product
(id INTEGER PRIMARY KEY AUTOINCREMENT ,
product_id INTEGER NOT NULL,
product_name TEXT,
supplier_id INTEGER NOT NULL,
price REAL,
qty real,
create_time TimeStamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 当前当地时间,以text数据类型保存
create_time2 TimeStamp NOT NULL DEFAULT (datetime('now','localtime')), -- 以text数据类型保存
create_time3 TimeStamp NOT NULL DEFAULT (datetime('now','utc')) -- 以text数据类型保存
)'''
# 调用execute函数执行sql语句, 创建product表
cur.execute(sql)
print('product表创建成功。')
# 插入单行所有字段数据
cur.execute("INSERT INTO product VALUES (5, 37932,'apple','2812', 6.5, '545', 15, 2020,2)")
# 插入单行部分字段数据
sql = '''INSERT INTO product (product_id,product_name,supplier_id,price,qty)
VALUES (37932,'apple','2812', 6.5, 120);'''
cur.execute(sql)
conn.commit()
conn.close()
# 查询product表所有内容
conn = sqlite3.connect('test.db')
cur = conn.cursor()
result = cur.execute("SELECT * FROM product")
print("produc表内容如下:")
for row in result.fetchall():
print(row)
#删除表
cur.execute("DROP TABLE product")
conn.commit()
conn.close()
print("已删除product表")