Python网络爬虫与信息提取 - 嵩天
规则
requests.get()
r = requests.get(string url, timeout = n)
r 是一个
response 类,它包含如下属性
r.status_code
: 返回码r.text
: HTTP 相应内容的字符串形式r.encoding
: 从 header 中猜测的网页编码方式r.apparent_encoding
: 从内容中分析出来的编码方式r.content
:HTTP 相应内容的二进制形式
如果过 n 秒后还没有成功得到服务器的相应/从服务器读取数据,程序会 throw TimeOutError
requests
库的异常
在 request 网络连接时,我们一定要用
r.raise_for_status()
来在返回码不是200正常时,raise exception,然后用
try ... catch ...
语句保证异常被有效处理
requests.ConnectionError
: 网络连接错误异常,如DNS查询失败、拒绝连接等requests.HTTPError
: HTTP错误异常requests.URLRequired
: URL缺失异常requests.TooManyRedirects
: 超过最大重定向次数,产生重定向异requests.ConnectTimeout
: 连接远程服务器超时异常requests.Timeout
: 请求URL超时,从整个发起URL请求开始产生超时异常
HTTP 协议对资源的操作
- GET: 请求获取URL位置的资源
- HEAD: 请求获取URL位置资源的响应消息报告,即获得该资源的头部信息,节省带宽
- POST: 请求向URL位置的资源后附加新的数据,不改变现有内容,只是增加
- PUT: 请求向URL位置存储一个资源,覆盖原URL位置的资源
- PATCH: 请求局部更新URL位置的资源,即改写该处资源的部分内容 (与 PUT 相比节省带宽)
- DELETE: 请求删除URL位置存储的资源
requests.request()
r = requests.request(string method, string url, **kwargs)
method
对应上文六种方式- Optional Arguments:
params=kv
: 其中 kv 是一个dict
值,在 url 后代入几个参数,形如?k1=v1&key2=v2&...
headers = hd
:hd
是一个dict
值,定制访问服务器时的 header 字段,有的时候网站会保护自己的数据不允许爬虫访问,我们改变 headers 以后就可以伪装成浏览器data = dt, json = js
: 在post
或put
时将dt
或js
传给服务器
Robots 协议
通过这个协议对网络爬虫能爬取的东西进行限制,一般就在网络的根目录url/robots.txt
下,一个例子是京东的协议
如果你的爬虫速度非常慢,访问次数很少,访问量不大(近似人类行为),原则上可以不遵守 robots 协议
提取
BeautifulSoup
的基本元素
from bs4 import BeautifulSoup
s = BeautifulSoup(r.text, "html.parser")
returns the
r.text
restructured as html type.
s.tag
: 标签,最基本的信息组成单元,对应 html 中的一对尖括号,下文中 t = s.tagt.name
: 标签的名字,比如<p></p>
的名字是p
t.attrs
: 标签的属性,以字典形式存储。<p class="..." id="..."></p>
t.string
: 两个尖括号间的字符内容,即<p>...</p>
中的...
使用 BeautifulSoup
遍历 HTML
h = s.head
返回 soup
中的第一个
head
标签
BautifulSoup
的每个类型都带有 pretify()
函数,优化打印效果: h.prettify()
,
s.prettify()
, …
下行遍历
h.contents
: 返回一个列表,包含此 tag 下的所有儿子节点h.children
: 返回一个所有儿子节点的 iterator, 即可以使用for c in h.children: ...
遍历儿子节点h.descendant
: 返回所有子孙节点的 iterator
上行遍历
h.parent
: 返回h
的父亲标签h.parents
: 返回h
的 ascendant 的 iterator,即一直到根节点的所有节点,用来循环遍历 ascendant
平行遍历
注意树中可能存在 NavigableString
, Tag
,
Comment
种种类型,我们需要进行判断,不能假设平行遍历的下一个绝对是 tag
next_sibling
: 返回按照HTML文本顺序的下一个平行节点标签previous_sibling
: 返回按照HTML文本顺序的上一个平行节点标签next_siblings
: 返回按照HTML文本顺序的后续所有平行节点标签的 iteratorprevious_siblings
: 返回按照HTML文本顺序的前续所有平行节点标签的 iterator
信息标记的形式
XML (Extensible Markup Language): 通过标签表达所有信息;可扩展性好,但比较繁琐;用于互联网上的信息交互和表达
用一对标签表达其中的信息
<tag> ... </tag>
,如果没有内容,则只用一个标签(即一对尖括号)来表达<tag/>
.JSON (JavaScript Object Notation): 有类型的键值对;适合程序处理直接使用;用于云端和客户端的通信,一般在接口处使用
YAML (YAML Ain’t Markup Language): 无类型的键值对;文本信息比例最高,可读性高;用于系统配置文件中
使用缩进表达所属关系
1
2
3name :
oldName: 延安自然科学院
newName: 北京理工大学-
表示并列关系1
2
3name:
-延安自然科学院
-北京理工大学|
表达整块数据(可以跨越多行),#
表达注释1
2text: | #学校介绍
北京理工大学天下第一bulabulabula
使用 BeautifulSoup
提取信息
我们使用find_all
来提取HTML页面中的信息:
s.find_all(name, attrs, recursive, string, ...)
返回一个存储着查询结果的列表
name
: 对标签名称的检索字符串- 可以是一个字符串: 寻找所有 a 标签
s.find_all("a")
- 也可以是一个包含字符串的列表:寻找所有 a 标签和 b 标签
s.find_all(["a","b"])
- 可以是一个正则表达式:寻找所有名字中包含 b
的标签
s.find_all(re.compile("b"))
会返回 b 标签和 body 标签
- 可以是一个字符串: 寻找所有 a 标签
attrs
: 对标签属性值的检索字符串- 查找包含某一属性的标签:
s.find_all("p", "course")
返回所有带有 course 这个 attribute 的 p tag - 查找包含某一属性等于指定值的标签:
s.find_all(id = "link1")
返回[<a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>]
- 查找包含某一属性的标签:
recursive
: 表示是否对子孙全部检索的布尔值,默认Truestring
: 对标签中的字符串进行搜索:s.find_all(string = re.compile("python"))
返回所有包含有 python 的标签内容
简易表示方法:<tag>(..)
等价于
<tag>.find_all(..)
; soup(..)
等价于
soup.find_all(..)
实战
如果我们不需要通过分析页面架构,只是通过在 html 文件中搜索就可以得到想要的信息的话,这无疑是最简单的。这种情况可能发生于信息存在键值对中,如此搜索变得很方便
- raw string:
r'[1-9]\d{5}
转义符 escape character 不转义,表达其在字符串中的原意 - string:
'[1-9]\\d{5}'
转义符 escape character 有
Re
库的基本使用
re.search(pattern, string)
: 在 string 中搜索匹配 pattern 的第一个位置,返回 match 类型re.match(p, s)
: 从 s 的开始位置起匹配 p,返回 match 类型re.findall(p, s)
: 搜索 s ,以列表类型返回全部与 p 匹配的子串re.split(p, s, maxsplit=n)
: 将 p 按照 s 匹配结果进行分割,将匹配的部分去掉,其他部分分割成子串存在列表中;可选参数 maxsplit 最多分为 n 个部分re.sub(p, repl, s, count=n)
: 将 n 个 s 中与 p 匹配的子串替换成 repl,n如果未定义则默认替换所有
针对对于同一正则表达式的多次操作,我们可以将一个正则表达式编译为一个
pattern 类型,之后就可以多次使用针对这个类型的以上函数操作
regex = re.compile(pattern)
regex.search(s), regex.match(s), ...
Re
库的 match
对象
match 对象就是一次匹配的结果,包含许多关于这次匹配的信息
.string
: 待匹配的文本.re
: 匹配时使用的patter对象(正则表达式).pos
: 正则表达式搜索文本的开始位置 (即在 string 中搜索的范围).endpos
: 正则表达式搜索文本的结束位置.group(0)
: 获得匹配后的字符串.start()
: 匹配结果在原始字符串的开始位置.end()
: 匹配结果在原始字符串的结束位置.span()
: 返回(.start(), .end())
其他技巧
中文的打印与对齐
我们一般使用 string.format()
函数格式我们的输出,用法:theFormat.format(parameter1, parameter2, ...)
formatter string 中有几个参数:(注意必须严格遵守这个顺序)
{n}
表示使用第 n 个参数填充这个部分,不指定则按照与参数的对应顺序来{:p}
使用参数 p 填充,不指定则默认为空格{:<n}
{:^n}
{:>n}
左对齐,居中对齐,右对齐,宽度为 n{:,}
在千位用逗号分隔数字{:.n}
浮点数保留小数点后 n 位{:<type>}
可以是 d 代表整形,f 代表浮点型,e 代表科学计数法浮点型,% 代表百分号浮点型
"{0:^10}\t{1:{2}^10}".format("排名","学校名称",chr(12288))
打印 第零个参数(居中对齐宽度为10),打印
tab,打印第一个参数(用第2个参数进行居中填充宽度为10)
第二个参数是中文
"{1:x<20,.3f}".format(0,21031295.21413)
打印
‘21,031,295.214xxxxxx’
在 UTF-8 编码中,对应的中文空格字符是 chr(12288)
.