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>的名字是pt.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).