0%

python-爬虫

Python_爬虫

爬虫对我来说一直是听说过没做过,今天一时兴起将爬虫的课程过了一遍,整理笔记如下。Python中是提供了爬虫库的,但学习中多采用requests库用于理解爬虫的过程。这里仅对老师讲解的部分进行了记录,还有很多未曾接触了,例如动态代理、header模拟浏览器等等针对反爬虫的策略。权当一份框架型笔记即可。

[慕课课程][https://www.icourse163.org/course/BIT-1001870001]

爬虫的主要流程

  1. 请求获得对应网络信息
  2. 解析网络信息
  3. 存储需要的网络信息

Requests库介绍

用于爬虫第一步,发送HTTP请求获取对应网络信息。这里仅对Requests库做整体的介绍,具体方法的操作与技巧,可看[官方文档][https://requests.readthedocs.io/en/master/]或其他相关资料。

requests主要方法

方法 说明 与HTTP对应 HTTP说明
requests.request() 基础方法,构造一个请求
get() 获取HTML网页内容 GET 获得网页全内容
head() 获取HTML网页头信息 HEAD 仅获得网页头信息
post() 向HTML网页提交POST请求 POST 在已有资源后添加POST资源
put() 向HTML网页提交PUT请求 PUT 利用PUT资源替换原有资源
patch() 向HTML网页提交PATCH请求 PATCH 局部更新PATCH指定资源
delete() 向HTML网页提交DELETE请求 DELETE 删除资源

我们可以看到其拥有七个主要方法,并且除基础方法外均一一对应与HTTP定义。除request() 方法外,六个方法均包装了request() 方法,所以上面七个方法本质上均为request() 方法,仅为方便用户使用。

在爬虫应用中,常使用get()或head()方法,其他对于资源修改的方法,反而不常用。

这里需要简单了解HTTP协议,以便更好的理解上述内容。

HTTP协议

HTTP:Hypertext Transfer Protocol , 超文本传输协议,是一种基于”请求与响应“,无状态的应用层协议。

理解此协议可以从定义出发:1.请求与响应:说明HTTP协议的工作方式为客户端向服务器发送请求,服务器向客户端响应。2.无状态:说明HTTP协议前后无关,没有记忆信息。3.应用层:高层协议,已经是面向用户的协议了。

HTTP协议通过URL定位资源,通过对应请求(GET\HEAD\PUT…)进行相关操作。对于URL的格式这里不解释。对于请求,HTTP规定了6种请求方式,如图:

各种方法解释,图上都有,这里不详细解释。我们发现与request的请求方法是一一对应的。前两种方法用于获取数据,后四种方法用于修改数据。

Request对象与Response对象

requests库主要包含两个对象,分别是Request与Response,分别对应发送对象返回对象

Response对象属性
属性 说明
r.status_code HTTP请求的返回状态,200表示连接成功404表示失败
r.text HTTP相应内容的字符串信息,即URL对应的网页内容
r.encoding 从HTTPheader中猜测的相应内容编码方式
r.apparent_encoding 内容分析出的响应内容编码方式
r.content HTTP响应内容的二进制形式

注意点:

  1. encoding属性与apparent_encoding的区别

    encoding属性是根据HTTP头信息中是否存在charset字典来确定值,但并不是每一个网站设计都会包含此字段,默认为”ISO…”编码格式,其不一定准确。

    apparent_encoding属性,是根据文本内容分析得到的,通常具有较高的准确性。

Request异常与通用代码框架
异常 说明
requests.ConnectionError 连接错误异常,如DNS查询失败、拒绝连接
requests.HTTPError HTTP错误异常
request.URLRequired URL缺失异常
request.ConnectTimeout 连接远程服务器超时异常
request.Timeout 请求URL超时异常

请求异常可以分为以上几种,但并不需要记录,知道即可。

检测异常方法 说明
r.raise_for_status() 若果不是200,产生异常request.HTTPError

上述方法用于检测是否出现异常,常用。

一下通用代码框架可用于常见爬虫框架。

1
2
3
4
5
6
7
8
9
10
11
12
import requests
def getHTMLText(url):
try:
r = requests.get(url ,timeout = 30)
r.raise_for_status() # 状态不是200则引发一场
r.enconding = r.apparent_encoding # 替换编码格式
return r.text
except:
return "产生异常"

url = "http://www.baidu.com"
print(getHTMLText(url))

Robots协议

此协议是针对爬虫所指定的规范,哪些可以爬,哪些不能爬。通常直接放置在根目录下。例如百度”https://www.baidu.com/robots.txt".并截取如下展示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
User-agent: Baiduspider
Disallow: /baidu
Disallow: /s?
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh

User-agent: Googlebot
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/ne
  • user-agent:爬取框架/爬取人
  • Disallow:不允许爬取的内容通配符

BeautifulSoup库

BeautifulSoup可用于爬虫过程第二步,对HTML进行树结构解析,并提供了相对完备的遍历方法。

BeautifulSoup的基本使用

使用很简单,指定HTML地址与解析方式即可。

1
2
3
4
5
6
# 将纯文本文件转换为树形结构,并进行解析便是BeautifulSoup的作用
from bs4 import BeautifulSoup as bs
#第一个参数为通过requests获得的html文本
#第二个参数为指定用HTML格式解析此文本
#返回soup对象
soup = bs(r.text,"html.parser")

soup对象的基本元素

基本元素 说明
Tag 标签,基本的信息组织单元,分别用<>和</>表明开头与结尾
Name 标签名,p标签,a标签等,使用格式:Tag.name
Attributes 标签属性,字典形式组织,使用格式: Tag.attrs
NavigableString 标签内非属性字符串,使用格式:Tag.string
Comment 标签内字符串的注释部分,

由上表可知,soup内对象基本元素的调用,以Tag元素为基础进行。

  • Tag标签的调用

    直接通过标签名称调用即可

1
2
3
4
5
soup = bs("htmladress","html.parser")
#调用a标签,并获得标签对象
soup.a
#调用body标签并获得标签对象
soup.body
  • name,标签名的获得
1
2
3
4
5
soup = bs("htmladress","html.parser")
#调用a标签,并获得标签对象
tagA = soup.a
#获得a标签的标签名
tagA.name
  • attrs,标签属性的获得
1
2
3
4
5
soup = bs("htmladress","html.parser")
#调用a标签,并获得标签对象
tagA = soup.a
#获得a标签的属性字典
tagA.attrs
  • NavigableString,标签内非属性字符串的获得
1
2
3
4
5
soup = bs("htmladress","html.parser")
#调用a标签,并获得标签对象
tagA = soup.a
#获得普通字符串,可以跨层次
tagA.string
  • Comment,标签内部Comment获得,比较特殊

    并没有一个为comment的属性直接进行提取Comment,但是可通过上述的.string方式获得Comment内容,不同处在于其列别可通过type()函数进行鉴别。

1
2
3
4
5
newsoup = BeautifulSoup("<b><!-- this is comment --><p>This is not comment</p></b>")
newsoup.b.string
#获得字符,但不知是否是comment还是NavigableString
if type(newsoup.b.string) is bs4.elemnt.Comment :
print("发现Comment")

下图为熟知的HTML格式,其与BeautifulSoup基本是一一对应的。

BS的遍历

BeautifulSoup对于树的遍历方式,并没有特殊的内容,这里仅做简单的介绍,知道即可。重点还是在应用中的情形。

三种遍历方式:上行、下行、平行

  • 下行遍历方法展示
属性 说明
.content 子节点的列表,将所有子节点信息存入列表
.children 子节点的迭代类型,用于循环遍历子节点情形
.descendants 子孙节点的迭代类型,包含所有的子孙节点,用于遍历
  • 上行遍历方法展示
属性 说明
.parent 节点的父节点标签
.parents 节点的祖先节点标签,用于循环遍历
  • 平行遍历方法展示
属性 说明
.next_sibling 返回HTML文本顺序中下一个平行节点标签
.previous_sibling 返回HTML文本顺序中上一个平行节点标签
.next_siblings 迭代类型,返回HTML文本顺序中后序所有平行节点标签
.previous_siblings 迭代类型,返回HTML文本顺序中前序所有平行节点标签

注意:平行遍历指的是在同一个父节点的前提下,而不是树中所有同一层次的情况。如图:

Bs4的遍历情况总结如图:

信息标注方式与信息提取方法

信息标注方式简介

常见的信息标注方式有三种,分别是XML、JSON、YAML。HTML是XML的变种,也属于XML格式。下面简单的介绍比较三种信息标注方式。

  • XML:eXtensible Markup Language , 其构成单位是Tag标签,且标签内包含有名称属性等信息如图:
其中空元素可通过一个<>表示 , 注释通过<!-- --> 表示
1
2
3
4
#XML空元素表示
<img src="china.jpg" size="10" />
#XML注释格式
<!-- This is a comment -->

总结:XML格式共包含三种样式

1
2
3
<name> ... </name>
<name />
<!-- -->
  • JSON:JavaScript Object Notation , 有类型的键值对表示方式

总结:JSON格式包含三种样式

1
2
3
4
5
6
#普通键值对表示
"key" : "Value"
#多值键值对表示
"key" : ["Value1","Value2"]
#键值对嵌套表示
"key" : {"subkey" : "subvalue"}
  • YAML:Ain’t Markup Language , 无类型键值对

其表示各种关系情况:

1
2
3
4
5
6
7
8
9
10
11
12
#缩进表示所属关系,同Python
name:
newName : 北京理工
OldName : 延安自然科学院
# “-” 表达并列关系
name:
- 北京理工大学
- 延安自然科学院

# “|”表达整块数据
text: |
北京理工大学创立与......

总结:三种表示形式如下

1
2
3
4
5
6
7
8
9
key : value

key : #comment
- value1
- value2

key:
subkey : value

信息标注方式实例比较

XML:

1
2
3
4
5
6
7
8
9
10
11
12
13
<person>
<firstName>Tian</firstName>
<lastName>Song</lastName>
<address>
<streetAddr>中关村南大街5号</streetAddr>
<city>北京市</city>
<zipcode>100081</zipcode>
</address>
<prof>ComputerSystem</prof>
<prof>Security</prof>
</person>
<!--最早的通用标记语言,可扩展性好,但很繁琐 -->
<!-- 用于INTERNET 信息交互与传递 -->

JSON:

1
2
3
4
5
6
7
8
9
10
{
“firstName”: “Tian”,
“lastName”: “Song”,
“address”:{“streetAddr”: “中关村南大街5号” ,
“city”: “北京市” ,
“zipcode”: “100081”},
“prof”:[“ComputerSystem”, “Security”]
}
#信息有类型,适合程序处理,比XML简洁
#用于移动英语云端与节点信息通信,无注释

YAML:

1
2
3
4
5
6
7
8
9
10
11
firstName: Tian
lastName: Song
address:
streetAddr:中关村南大街5号
city:北京市
zipcode: 100081
prof:
‐Computer System
‐Security
# 信息无类型,文本信息比例最高,可读性好
# 常用于各类系统配置文件,有注释易读

信息提取的三种方法

这里的信息提取指的是提取标注信息(xml、json、yaml)内容。

  1. 完整解析信息标记,再提取关键信息,需要标记解析器,如BS4。虽然准确但是繁琐,速度慢。

  2. 无视标记形式,直接搜索关键信息。对信息文本直接查找。提取速度快,但准确性不确定。

  3. 融合方法,一般先通过第二种方式确定范围,再使用第一种方式精确处理。如图

基于Bs4库的HTML内容查找方法

由于本部分内容过于简单,更多的需要在实践中应用,故未详细说明。正则表达式也可以用于检索。

  1. 首先利用find_all()方法,无视标记形式,直接搜索关键信息。
  1. 其次解析其标签格式

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#定向页面爬取
#获得中国“最好”大学排名情况
import requests
from bs4 import BeautifulSoup
import bs4
# 1.通过requests等库获得网页内容
def getHTMLText(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
# 2.通过beautifulSoup等库解析网页内容,并获取关键信息
def fillUnivList(ulist, html):
soup = BeautifulSoup(html, "html.parser")
for tr in soup.find('tbody').children:
if isinstance(tr, bs4.element.Tag):
tds = tr('td')
ulist.append([tds[0].string, tds[1].string, tds[3].string])
# 3.可视化输出
#chr(12288)用于中文符号填充,未研究
def printUnivList(ulist, num):
tplt = "{0:^10}\t{1:{3}^10}\t{2:^10}"
print(tplt.format("排名","学校名称","总分",chr(12288)))
for i in range(num):
u=ulist[i]
print(tplt.format(u[0],u[1],u[2],chr(12288)))

def main():
uinfo = []
url = 'http://www.zuihaodaxue.com/zuihaodaxuepaiming2018.html'
html = getHTMLText(url)
fillUnivList(uinfo, html)
printUnivList(uinfo, 20) # 20 univs
main()