第十周:网络爬虫和信息提取
1.简介
网络爬虫,Web Spider 或 Web Crawler,是一种自动访问 Web 页面提交数据的自动化程序
网络爬虫应用可以采用 Requests 和 Beautiful Soup 库
Sitemap: https://pypi.org/sitemap.xml
网站地图: https://pypi.org/sitemap.xml
User-agent: *
Disallow: /simple/
Disallow: /packages/
Disallow: /_includes/
Disallow: /pypi/*/json
Disallow: /pypi/*/*/json
Disallow: /pypi*?
Disallow: /search*
用户代理: *
不允许:/simple/
不允许:/packages/
不允许:/_includes/
不允许:/pypi/*/json
不允许:/pypi/*/*/json
不允许:/pypi*?
禁止:/search*
2.Request 库
Request 库是一个简洁且简单的处理 HTTP 请求的第三方库
Request 库总体根据 HTTP 协议处理方式设计其功能函数
HTTP:万维网访问的基本协议
Request 库的网页请求函数:
函数 | 描述 |
get(url [, timeout=n]) | 对应于 HTTP 的 GET 方式,是获取网页最常用的方法,可以增强 timeout=n 参数,设定每次请求时间为 n 秒 |
post(url, data = {'key':'value'}) | 对应于 HTTP 的 POST 方式,其中,字典用于传递客户数据 |
delete(url) | 对应 HTTP 的 DELETE 方式 |
head(url) | 对应 HTTP 的 HEAD 方式 |
options(url) | 对应 HTTP 的 OPTIONS 方式 |
put(url, data = {'key':'value'}) | 对应 HTTP 的 PUT 方式,其中,字典用于传递客户数据 |
Request 库的 get()函数用于访问网页,返回内容保存为 Request 对象
import request
r = request.get("http://www.baidu.com")#使用get方法打开百度链接
type(r)#返回Response对象
<class 'requests.models.Response'>
Request 对象的属性
属性 | 描述 |
status_code | HTTP 请求的返回状态,整数,200 表示链接成功,404 表示失败 |
text | HTTP 响应内容的字符串形式,即 url 对应的页面内容 |
encoding | HTTP 响应内容的编码形式 |
content | HTTP 响应内容的二进制形式 |
>>>import requests
>>>r = requests.get("heep://www.baidu.com")
>>>r.status_code#返回状态
200
>>>r.text#观察返回的内容,中文字符是否能正常显示
...
>>>r.encoding#默认编码方式不对,所以中文为乱码
'ISO-8859-1'
>>>r.encoding = 'utf-8'#更改编码方式
>>>r.text#更改成功后,中文即可正常显示
Request 对象的方法
方法 | 描述 |
json() | 如果 HTTP 响应内容包含 JSON 格式数据,则该解析 JSON 数据 |
raise_for_status() | 如果不是 200,则产生异常 [ 之后就可以用 try-except 捕获异常 ] |
受限于网络条件,使用 Request 库功能可能会触发异常:ConnectionError HTTPError Timeout……
import requests
def getHTMLText(url):
try:
r = requests.get(url,timeout=30)#url是地址,timeout为请求超时时间
r.raise_for_status()#如果状态不是200,引发异常
r.encoding='utf-8'
return r.text
except:
return ""
url = "http://www.baidu.com"
print(getHTMLText(url))
扩展(HTTP 的 GET 和 POST):
定义了客户端与服务器交互的不同方法,一般而言,GET 可以根据某链接获得内容,POST 用于发送内容。然而,GET 也可以向链接提交内容,区别如下:
- GET 方式可以通过 URL 提交数据,待提交数据是 URL 的一部分;采用 POST 方式。待提交数据放置在 HTML HEADER 内
- GET 方式提交的数据最多不超过 1024 字节,POST 没有对提交内容的长度限制
- 安全性问题。使用 GET 时参数会显示在 URL 中,而 POST 不会。所以,如果这些数据是非敏感数据,那么使用 GET;如果提交数据是敏感数据,建议采用 POST 方式
3.实例十七:关键词搜索爬虫
向搜索引擎自动提交关键词并查询,是个技术活
<div ... data-tools= '{"title":"...","url":"..."}'>...</div>
'''
利用beautifulsoup4库找到data-tools属性值,提取带有title的字符串
data-tool内部由{}形成的数据是典型的JSON格式,可用json库将其转换为字典
'''
扩展(CAPTCHA 验证码):
"全自动区分计算机和人类的图灵测试"
4.HTML 标识和 Web 页面
HTML(hypertext markup language)是超文本标记语言,它是专门为 Web 页面组织内容而创建的语言
HTML 语言本质上是键值对的标识,采用<key>value</key>方式表达键 key 对应的值 value
<!DOCTYPE HTML>
<html>
<body>
<meta charset=gb2312>
<h2 align=center>2024 年 1 月部分大中城市新建商品住宅销售价格指数 </h2>
<table border='1' align="center" width=70%>
<tr bgcolor='orange'>
<th width="40%">城市</th>
<th width="30%">环比</th>
<th width="30%">同比</th>
</tr>
<tr><td align="center">北京</td><td align="center">99.9</td><td align="center">101.3</td></tr>
<tr><td align="center">上海</td><td align="center">100.4</td><td align="center">104.2</td></tr>
<tr><td align="center">广州</td><td align="center">99.2</td><td align="center">96.4</td></tr>
<tr><td align="center">深圳</td><td align="center">99.3</td><td align="center">95.9</td></tr>
<tr><td align="center">沈阳</td><td align="center">99.4</td><td align="center">98.6</td></tr>
</table>
</body>
</html>
解释:HTML 语法中,由<table>表格内容</table>形成的是一个表格,<tr>表格的一行</tr>表示表格的一行。<tr>标签中<th>表头列</th>表示表格表头的一列;<td>内容列</td>表示表格行中的一列。
扩展(Web 前端开发):
Web 前端指开发基于 HTML 的展示效果,经历静态网页制作、动态网页制作和 Web2.0 开发等几个阶段。开发语言主要是 HTML5、CSS、JavaScript
#CSV2HTML.py
seg1 = '''
<!DOCTYPE HTML>\n<html>\n<body>\n<meta charset=gb2312>
<h2 align=center>2024 年 1 月部分大中城市新建商品住宅销售价格指数 </h2>
<table border='1' align="center" width=70%>
<tr bgcolor='orange'>\n '''
seg2 = "</tr>\n"
seg3 = "</table>\n</body>\n</html>"
def fill_data(locls):
seg = '<tr><td align="center">{}</td><td align="center">' + \
'{}</td><td align="center">{}</td></tr>\n'
seg = seg.format(*locls)
return seg
with open("price202401.csv", encoding='utf-8') as fr:
ls = []#将CSV文件读入列表ls
for line in fr:
line = line.replace("\n","")#格式化字符串方法将ls中内容写入HMTL文件
ls.append(line.split(","))
with open("price202401.html", "w",encoding='utf-8') as fw:
fw.write(seg1)
seg = '<th width="40%">{}</th>\n<th width="30%">{}</th>\n'+\
'<th width="30%">{}</th>\n'
fw.write(seg.format(*ls[0]))
fw.write(seg2)
for i in range(len(ls)-1):
fw.write(fill_data(ls[i+1]))
fw.write(seg3)
#限于技术有限,我的编码不太对
5.HTML 语言概述
- 标签(Tags) :HTML文档由各种标签组成,每个标签用于定义文档的不同部分。标签通常成对出现,包括开始标签和结束标签。例如,
<p>
和</p>
分别表示段落的开始和结束。有些标签是自闭合的,如<br>
或<img>
,它们不需要结束标签。 - 元素(Elements) :元素是由开始标签、内容和结束标签组成的整体。例如,
<p>这是一个段落。</p>
就是一个元素,其中<p>
是开始标签,</p>
是结束标签,中间的内容是元素的内容。 - 属性(Attributes) :属性提供了关于元素的额外信息。属性通常出现在开始标签内,格式为
属性名="属性值"
。例如,<a href="[http://www.example.com](http://www.example.com) ">这是一个链接</a>
中的href
是一个属性,指定了链接的目标地址。 - 注释(Comments) :注释用于在HTML代码中添加说明,不会被浏览器解析。注释的格式是
<!-- 这是一个注释 -->
。 - 文档类型声明(DOCTYPE) :文档类型声明是HTML文档的第一行,用于告诉浏览器文档使用的HTML版本。例如,
<!DOCTYPE html>
声明这是一个HTML5文档。 - 基本结构:一个标准的HTML文档通常包含以下几个部分:
-
<!DOCTYPE html>
:文档类型声明。<html>
:HTML文档的根元素。<head>
:包含文档的元数据,如标题、字符集等。<title>
:定义文档的标题,显示在浏览器的标题栏或标签页上。<body>
:包含文档的实际内容,如文本、图像、链接等。
- 特殊字符和实体(Entities) :HTML中有一些特殊字符需要使用实体来表示,例如
<
表示小于号<
,>
表示大于号>
。 - 嵌套和层次结构:HTML元素可以嵌套,形成层次结构。例如,一个段落可以包含多个句子,每个句子可以包含单词和标点符号。
- 大小写不敏感:HTML标签和属性名不区分大小写,例如
<p>
和<P>
是相同的。
通过这些基本语法形式,可以构建出复杂的网页结构和内容。
6.Beautiful Soup 库
Beautiful Soup 库能够用来解析 HTML 和 XML 的第三方库,也称 beautifulsoup4 或 bs4
使用 Request 库获取 HTML 页面并将其转换成字符串后,需要进一步解析 HTML 页面格式,提取有效信息,这需要处理 HTML 和 XML 的函数库
pip install -U beautifulsoup4
from bs4 import BeautifulSoup
从库中引入 BeautifulSoup 类
Beautiful Soup库采用 BeautifulSoup 对象表示一个页面
import requests
from bs4 import BeautifulSoup
r = requests.get("http://www.baidu.com")
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text)
type(soup)
'''
<class 'bs4.BeautifulSoup'>
'''
soup.head
'''
<head><meta content="text/html;charset=utf-8" http-equiv="content-type"/><meta content="IE=Edge" http-equiv="X-UA-Compatible"/><meta content="always" name="referrer"/><link href="http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/><title>百度一下,你就知道</title></head>
'''
title=soup.title
type(title)
'''
class 'bs4.element.Tag'>
'''
soup.p
'''
<p id="lh"> <a href="http://home.baidu.com">关于百度</a> <a href="http://ir.baidu.com">About Baidu</a> </p>
'''
BeautifulSoup 对象的常用属性
属性 | 描述 |
head | HTML 页面的<head>内容 |
title | HTML 页面标题,在<head>之中,由<title>标记 |
body | HTML 页面的<body>内容 |
p | HTML 页面中第一个<p>内容 |
strings | HTML 页面所有呈现在 Web 上的字符串,即标签的内容 |
stripped_strings | HTML 页面所有呈现在 Web 上的非空格字符串 |
<a class="mnav" href="http://www.nuomi.com">糯米</a>
'''
<>中的是标签的名字name,其他项是sttrs,尖括号间的内容是string
'''
>>>soup.a
<a class="mnav" herf="http://www.nuomi.com">糯米</a>
>>>soup.a.name
'a'
>>>soup.a.attrs
{'href':'http://www.nuomi.com','class':['mnav']}
>>>soup.a.string
'糯米'
>>>title.name#title变量在上段例子中已经定义
'title'
>>>title.string
'百度一下,你就知道'
>>>soup.p.contents
……
标签对象的常用属性:
属性 | 描述 |
name | 字符串,标签的名字,比如 div |
attrs | 字典,包含了原来页面 Tag 所有的属性,比如 href |
contents | 列表,这个 Tag 下所以子 Tag 的内容 |
string | 字符串,Tag 所包围的文本,网页中真实的文字 |
String 属性的返回遵循如下原则:
- 如果标签内部没有其他标签,string 属性返回其中的内容
- 如果标签内部还有其他标签,但只有一个标签,string 属性返回最里面标签的内容
- 如果标签内部有超过 1 层嵌套的标签,string 属性返回 None(空字符串)
按照条件返回标签内容:
BeautifulSoup.find_all(name,attrs,recursive,string,limit)
name:按照 Tag 标签名字检索,名字用字符串形式表示,例如,div、li。
sttrs:按照 tag 标签属性值检索,需要列出属性名称和值,采用 JSON 表示
recursive:设置查找层次,只查找当前标签下一层时使用 recursive=False
string:按照关键字检索 string 属性内容,采用 string=开始
limit:返回结果的个数,默认返回全部结果
正则表达式:
正则表达式(Regular Expression,简称regex或regexp)是一种强大的文本处理工具,用于在字符串中查找、匹配和替换特定的模式。它由普通字符(如字母、数字)和特殊字符(称为元字符)组成,能够描述复杂的字符串匹配规则。正则表达式广泛应用于编程、数据验证、文本处理等领域,是处理字符串的强大武器。
7.实例十八:中国大学排名爬虫
采用网络爬虫技术,从网页上获取中国大学排名数据
allUniv=[]
def fillUnivList(soup):
data = soup.find_all('tr')
for tr in data:
singleUniv = []
ltd = tr.find_all('td')
singleUniv.append(ltd[0].string.strip("\n "))
singleUniv.append(list(ltd[1].strings)[1].strip("\n "))
singleUniv.append(list(ltd[2].strings)[0].strip("\n "))
singleUniv.append(list(ltd[3].strings)[0].strip("\n "))
singleUniv.append(ltd[4].string.strip("\n "))
allUniv.append(singleUniv)
allUniv=[]#存储全部表格数据,二维列表
def fillUnivList(soup):
singleUniv = []
ltd = tr.find_all('td')
if len(ltd)==0:
continue
singleUniv.append(ltd[0].string.strip("\n "))
singleUniv.append(list(ltd[1].strings)[1].strip("\n "))
singleUniv.append(list(ltd[2].strings)[0].strip("\n "))
singleUniv.append(list(ltd[3].strings)[0].strip("\n "))
singleUniv.append(ltd[4].string.strip("\n "))
allUniv.append(singleUniv)data = soup.find_all('tr')
#CrawUnivRanking.py
import requests
from bs4 import BeautifulSoup
from prettytable import PrettyTable
allUniv = []
def getHTMLText(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = 'utf-8'
return r.text
except:
return ""
def fillUnivList(soup):
data = soup.find_all('tr')
for tr in data:
singleUniv = []
ltd = tr.find_all('td')
if len(ltd)==0:
continue
singleUniv.append(ltd[0].string.strip("\n "))
singleUniv.append(list(ltd[1].strings)[1].strip("\n "))
singleUniv.append(list(ltd[2].strings)[0].strip("\n "))
singleUniv.append(list(ltd[3].strings)[0].strip("\n "))
singleUniv.append(ltd[4].string.strip("\n "))
allUniv.append(singleUniv)
def printUnivList(num):
tb = PrettyTable();
tb.field_names = [" 排名 "," 学校名称 "," 省市 "," 类型 "," 总分 "]
for i in range(num):
tb.add_row(allUniv[i])
print(tb)
def main(num):
url = 'https://www.shanghairanking.cn/rankings/bcur/2023'
html = getHTMLText(url)
soup = BeautifulSoup(html, "html.parser")
fillUnivList(soup)
printUnivList(num)
main(10)
'''
+--------+------------------+--------+--------+--------+
| 排名 | 学校名称 | 省市 | 类型 | 总分 |
+--------+------------------+--------+--------+--------+
| 1 | 清华大学 | 北京 | 综合 | 1004.1 |
| 2 | 北京大学 | 北京 | 综合 | 910.5 |
| 3 | 浙江大学 | 浙江 | 综合 | 822.9 |
| 4 | 上海交通大学 | 上海 | 综合 | 778.6 |
| 5 | 复旦大学 | 上海 | 综合 | 712.4 |
| 6 | 南京大学 | 江苏 | 综合 | 676.2 |
| 7 | 中国科学技术大学 | 安徽 | 理工 | 608.6 |
| 8 | 华中科技大学 | 湖北 | 综合 | 606.2 |
| 9 | 武汉大学 | 湖北 | 综合 | 599.1 |
| 10 | 西安交通大学 | 陕西 | 综合 | 572.6 |
+--------+------------------+--------+--------+--------+
'''
8.PrettyTable 库与格式化展示
PrettyTable 库是一个在终端生成简洁格式化文本的第三方库
pip install -U prettytable
from prettytable import PrettyTable
table = PrettyTable()
table.field_names = ["City","Area (sq km)","Population"]
table.add_row(["Adelaide",1295,115829])
table.add_row(["Brisbane",5905,1857954])
table.align = "l"
print(table)
'''
+----------+--------------+------------+
| City | Area (sq km) | Population |
+----------+--------------+------------+
| Adelaide | 1295 | 115829 |
| Brisbane | 5905 | 1857954 |
+----------+--------------+------------+
'''
PrettyTable 库的主要函数:
函数 | 描述 |
PrettyTable() | 创建一个 PrettyTable 对象 |
field_names() | 设置或获取表格的字段名称,即列的名字 |
add_row() | 向表格中添加一行数据 |
add_column() | 向表格中添加一整列数据 |
align() | 设置指定列的对齐方式,左对齐"I"、居中"c"、右对齐"r" |
del_row(row_indes) | 删除指定索引的行 |
clear() | 清空表格中的所有行数据 |
sortby() | 设置排序依据的列名 |
创建表格,增加一行数据:
from prettytable import PrettyTable
table = PrettyTable(["Name","Age","City"])
table.add_row(["Alice",30,"New York"])
print(table)
'''
+-------+-----+----------+
| Name | Age | City |
+-------+-----+----------+
| Alice | 30 | New York |
+-------+-----+----------+
'''
增加新列 Occupation,设置第一行对应该列的值为 Engineer:
table.add_column("Occupation",["Engineer"])
print(table)
'''
+-------+-----+----------+------------+
| Name | Age | City | Occupation |
+-------+-----+----------+------------+
| Alice | 30 | New York | Engineer |
+-------+-----+----------+------------+
'''
设置各列对齐方式:
table.align["Name"] = "l"
table.align["Age"] = "c"
table.align["City"] = "r"
print(table)
'''
+-------+-----+----------+------------+
| Name | Age | City | Occupation |
+-------+-----+----------+------------+
| Alice | 30 | New York | Engineer |
+-------+-----+----------+------------+
'''
增加数据,设置根据年龄排序:
table.add_row(["Charlie",25,"Chicago","Artist"])
table.sortby = "Age"
print(table)
'''
+---------+-----+----------+------------+
| Name | Age | City | Occupation |
+---------+-----+----------+------------+
| Charlie | 25 | Chicago | Artist |
| Alice | 30 | New York | Engineer |
+---------+-----+----------+------------+
'''
标签:第十,标签,爬虫,信息提取,soup,HTML,append,ltd,singleUniv
From: https://blog.csdn.net/DA_CK/article/details/143415025