参考链接: 中国大学MOOC Python网络爬虫与信息提取
前奏
https://daxue.911cha.com/ http://www.yggk.net/pm/2016.html
The Website is the API
IDLE 自带;默认;常用;入门级 适用于:Python入门;功能简单直接;300+代码以内 Sublime Text 专为程序员开发的第三方专用编程工具,专业编程体验,多种编程风格,工具非注册免费使用 Wing 公司维护,工具收费。调试功能丰富,版本控制,版本同步,适合多人共同开发 Visual Studio & PTVS PTVS:微软维护,Win环境为主,调试功能丰富 Eclipse PyDev:开源IDE。 开发工具,需要有一定开发经验。因为针对的是JAVA,所以Python安装稍微复杂 Pycharm 社区版免费。简单,集成度高。适合较复杂工程。 Canopy 适合科学计算,数据分析。公司维护,工具收费,支持近500个第三方库。适合科学计算领域应用开发 Anaconda 开源免费。支持近800个第三方库。
Requests
Requsets库的get()方法
r = requests.get(url) # 返回一个包含服务器资源的Response 对象 包含爬虫的内容
# 构造一个向服务器请求资源的Request 对象 Python大小写敏感
requsets.get(url,params=None,**kwargs)# url:拟获取页面的url链接
#params:url中的额外参数,字典或字节流格式,可选 **kwargs:12个控制访问的参数
# get函数实现使用了request函数封装。request提供了七个方法,第一个是基础方法,后面的都是调用request这个方法
encoding:只是从Header猜出的编码,如果header不存在charset,则认为编码为ISO-8859-1。apparent_encoding:是从内容中分析的内容编码方式。
Requests异常
Timeout异常:是指从发出请求,到获得内容的整个过程中的异常。 ConnectTimeout:仅指与远程服务器连接异常
r.raise_for_status() # 如果返回的不是200,就会产生异常requests.HTTPError 利于使用try-except做异常处理
通用访问网页的框架
import requests
def getHTMLText(url):
try:
r = requests.get(urr,timeout=30)
r.raise_for_status() # 如果状态不是200,就产生异常
r.encoding = r.apparent_encoding
return r.text
except:
return "产生异常"
if __name__ == "__name__":
url = "http://www.baidu.com"
print(getHTMLText(url))
HTTP协议及Requests库方法
HTTP,Hypertext Transfer Protocol,超文本传输协议 HTTP是一个基于“请求与响应”模式的,无状态的应用层协议 HTTP协议采用URL作为定位网络资源的标识
URL 格式 http://host[:port][path]
host:合法的Internet主机域名或IP地址
port:端口号,缺省端口为80 path:请求资源的路径
HTTP URL实例:
http://www.bit.edu.cn
http://220.181.111.188/duty
HTTP URL的理解: URL是通过HTTP协议存取资源的Internet路径,一个URL对应一个数据资源 HTTP协议对资源的操作 这六个函数就是requests对应的6个函数 通过URL和命令管理资源,操作独立无状态,网络通道及服务器成为了黑盒子 PATCH和PUT的区别 假如URL位置有一组数据UserInfo,包括UserID,UserName等20个字段。 需求:用户修改了UserName, 其他不变 采用PATCH,仅向URL提交了UserName的局部更新请求 采用PUT,必须将所有20个字段一并提交到URL,未提交字段被删除 PATCH的最主要好处:节省网络带宽
r = requests.head('http://httpbin.org/get') # 可以使用很少的流量,来获取网络的头部信息
r.headers # 可以获取URL的头部信息
r.txt # 为空
r = requests.post('http://httpbin.org/post',data='ABC') #向URL POST 添加一个字符串,自动编码为data
print(r.txt)
{
'''
"data":"ABC"
"form":{},
}
payload = {'key1':'value1','key2':'value2'} #向URL POST 添加一个字典,自动编码为form(表单)
r = requests.post('http://httpbin.org/post',payload='ABC')
print(r.text)
{ ...
"form":{
'key1':'value1',
'key2':'value2'
},
}
# put() 会将原有数据覆盖掉
payload = {'key1':'value1','key2':'value2'} #向URL POST 添加一个字典,自动编码为form(表单)
r = requests.put('http://httpbin.org/put',data='ABC')
print(r.text)
{ ...
"form":{
'key1':'value1',
'key2':'value2'
},
}
Requests库主要方法解析
requests.request(method,url,**kwargs) # 基础方法
method:请求方式,对应get/put/post等7种
url:拟获取页面的url链接
** kwargs:控制访问的参数 共13个
params: 字典或字节序列,作为参数增加到url中
kv = {'key1':'value1','key2':'value2'}
r = requests.request('GET','http://python123.io/ws',params=kv)
>>> print(r.url)
http://python123.io/ws?key1=value1&key2=value2
data: 字典,字节序列或文件对象,作为Request的内容 重点用在:向服务器提供 提交资源时使用
kv = {'key1':'value1','key2':'value2'}
r = requests.request('port','http://python123.io/ws',data=kv)
body = '主体内容'
r = requests.request('port','http://python123.io/ws',data=body)
json:JSON格式的数据,作为Request的内容
kv = {'key1','value1'}
r = requests.request('port','http://python123.io/ws',json=kv)
headers:字典,HTTP定制头
hd = {'user-agent','Chrome/10'}
r = requests.request('port','http://python123.io/ws',headers=hd)
cookies:字典或CookieJar,Request中的cookie
autu:元祖,支持HTTP认证功能
files:字典类型,传输文件
fs = {'file':open('data.xls','rb')}
f = requests.request('port','http://python123.io/ws',files=fs)
timeout:设定超时时间,秒为单位
r = requests.request('GET','http://www.baidu.com',timeout=10)
proxies:字典类型,设定访问代理服务器,http代理:可以增加登录认证 https代理:这样登录百度时,使用的地址就是代理服务器的地址,可以有效的隐藏自己的源IP地址的信息,防止对爬虫的逆追踪
pxs={ 'http':'http://user:pass@10.10.10.1:1234' 'https':'https://10.10.10.1:4321'}
r = requests.request('GET','http://www.baidu.com',proxies=pxs)
allow_redirects:True/False,默认为True,重定向开关
stream:True/False,默认为True,获取内容立即下载开关
verify:True/False,默认为True,认证SSL证书开关
cert:本地SSL证书路径
r = requests.request()
r = requests.request()
get() 方法是最常用的方法,因为向服务器提交资源会有限制。对于比较大的文件,我们用head()获取资源的概略
ROBOTS 协议
网络爬虫的问题
性能骚扰
- Web服务器默认接收人类访问
- 受限于编写水平和目的,网络爬虫会为Web服务器带来巨大的资源开销
法律风险
- 服务器上的数据有产权归属
- 爬虫获取数据后牟利将带来法律风险
隐私泄露
- 网络爬虫可能具备突破简单访问控制的能力,获得被保护数据,从而泄露个人隐私
网络爬虫的限制
- 来源审查:判断User-Agent进行限制
检查来访HTTP协议头的User-Agent域,只响应浏览器或友好爬虫的访问 - 发布公告:Robots协议
告知所有爬虫网站的爬取策略,要求爬虫遵守
Robots协议
Robots Exclusion Standard 网络爬虫排除标准 作用: 网站告知网络爬虫那些页面可以抓取,那些不行 形式: 在网站根目录下的robots.txt文件
在京东的网络协议中,User-agent:EtaoSpider 代表京东不允许这个开头的爬虫爬取任何资源,认为它是恶意爬虫
Robots的使用
网络爬虫: 自动或人工识别robots.txt,再进行内容爬取 约束性: Robots协议是建议但是非约束性,网络爬虫可以不遵守,但存在法律风险
Beautiful Soup 库
BS4库的基本元素
from bs4 import BeautifulSoup # 导入了一个类叫BeautifulSoup
import requests
r = requests.get("http://python123.io/ws/demo.html")
r.text
demo = r.text
soup = BeautifulSoup(demo,'html.parser')
print(soup.prettify())
BeautifulSoup 是解析,遍历,维护 ‘标签树’的功能库
<p>...</p>:标签 Tag
<p class='title'>...</p> # p是这个标签的名称 Name 成对出现
# 这叫属性域 一般域包换0-多个域 属性是用来定义标签的特点的
# 任何一个属性,有它属性的特性和值 属性是由键和值 键值对构成的
class='title'
#Beautiful Soup 库,也叫beautifulsoup4或bs4
#预定引用方式如下,即主要用BeautifulSoup类
form bs4 import BeautifulSoup # 从bs4库中引入了一个类型 叫BeautifulSoup
import bs4
soup = BeautifulSoup('<html>data</html>','html.parser')
BeautifulSoup(mk,'html.parser') # bs4的HTML解析器 需安装 bs4库
BeautifulSoup(mk,'lxml') # lxml的HTML解析器 需安装 lxml库
BeautifulSoup(mk,'html.xml') # lxml的XML解析器 需安装 lxml库
BeautifulSoup(mk,'html5lib') # html5lib解析器 需安装 html5lib库
<p class = 'title'>...</p>
BeautifulSoup类的基本元素 | 说明 |
---|
Tag | 标签 最基本的信息组织单元,分别用<>和</>表明开头和结尾 | Name | 标签的名字,< p> … < /p>的名字是’p’,格式:.name | Attributes | 标签的属性,字典形式组织,格式:.attrs | NavigableString | 标签的非属性字符串,< > … < />中的字符串,格式:.string | Comment | 标签内字符串的注释部分,一种特殊的Comment类型 |
# Tag标签 最基本的信息组织单元,分别用<>和</>表明开头和结尾
# 任何存在与HTML语法中的标签都可以用soup.<tag>访问获得,当HTML文档中存在多个相同<tag>对应内容时,soup.<tag>返回第一个
form bs4 import BeautifulSoup
soup = BeautifulSoup(demo,'htme.parset')
soup.title # <title>This is a python demo page</title>
tag = soup.a
tag # <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>
# Tag的name(名字) 标签的名字,<p>...</p>的名字是'p',格式:<tag>.name
#每个<tag>都有自己的名字,通过<tag>.name获取,字符串类型
from bs4 import BeautifulSoup
soup = BeautifulSoup(demo,'html.parser')
soup.a.name # 'a'
soup.a.parent.name # 'p'
soup.a.parent.parent.name # 'body'
# Tag的attrs(属性) 标签的属性,字典形式组织,格式:<tag>.attrs
tag = soup.a
tag.attrs # {'href': 'http://www.icourse163.org/course/BIT-268001', 'class': ['py1'], 'id': 'link1'}
tag.attrs['class'] # ['py1']
tag.attrs['href'] # 'http://www.icourse163.org/course/BIT-268001'
type(tag.attrs) # <class 'dict'>
type(tag) # <class 'bs4.element.Tag'>
# Tag的 NavigableString 标签的非属性字符串,< > ... < />中的字符串,格式:<tag>.string
#NavigableString 可以跨越多个层次
soup.a # <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>
soup.a.string # 'Basic Python'
soup.p # '<p class="title"><b>The demo python introduces several python courses.</b></p> '
soup.p.string # 'The demo python introduces several python courses.'
type(soup.p.string) # <class 'bs4.element.NavigableString'>
# Tag的Comment 标签内字符串的注释部分,一种特殊的Comment类型
# 一种特殊的Comment类型
newsoup = BeautifulSoup("<b><! --This is a comment --></b><p>This is not a comment</p>","html.parser")
newsoup.b.string # 'This is a comment '
type(newsoup.b.string) # <class 'bs4.element.Comment'>
newsoup.p.string # 'This is not a comment'
type( newsoup.p.string) # <class 'bs4.element.NavigableString'>
基于bs4库的HTML内容遍历方法
BeautifulSoup类型是所有标签树的根节点 标签树的下行遍历
属性 | 说明 |
---|
.contents | 子节点的列表,将所有儿子节点存入列表 | .children | 子节点的迭代类型,与.contents类似,用于循环遍历儿子节点 | .descendants | 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历 |
for child in soup.body. children:
print(child) # 遍历儿子节点
for child in soup.body.descendants:
print(child) # 遍历子孙节点
标签树的上行遍历
属性 | 说明 |
---|
.parent | 节点的父亲标签 | .parents | 节点先辈标签的迭代类型,用于循环遍历先辈节点 |
遍历所有先辈节点,包括soup本身,所以要区别判断。html的父亲,就是他自己。soup的父亲是空的
soup = BeautifulSoup(demo,'html.parser')
for parent in soup.a.parents:
if parent is None: # 避免遍历到自己本身
print(parent)
else:
print(parent.name)
p
body
html
[document]
标签树的平行遍历 平行节点遍历,有可能遍历到NavigableString
属性 | 说明 |
---|
.next_sibling | 返回按照HTML文本顺序的下一个平行节点标签 | .previous_sibling | 返回按照HTML文本顺序的上一个平行节点标签 | .next_siblings | 迭代类型,返回按照HTML文本顺序的后续所有平行节点标签 | .previous_siblings | 迭代类型,返回按照HTML文本顺序的前续所有平行节点标签 |
for sibling in soup.a.next_sibling
print(sibling) # 遍历后续节点
for sibling in soup.a.previous_sibling
print(sibling) # 遍历前续节点
基于bs4库的HTML格式输出
bs4的prettify() # 会在标签文本后加换行符 /n 使用python3的程序
sour.prettify()
sour.body.prettify()
信息标记
信息类型
信息 分为 XML,JSON,YAML
# XML
# 名称Name = img 属性Attribute=src
<img src='china.jpg' size='10'> ... </img>
<img src='china.jpg' size='10' /> # 空元素的缩写形式
<! -- This is a comment,very useful --> #注释书写形式
# JSON
# 是JavaScript中一种面向对象信息的一种表达方式,在java中可以把json当作程序的一部分
# 有类型的键值对 key :value; 对信息类型的定义是键 对信息值的描述是value
"name" : "北京理工大学"
"name" : ["北京理工大学","延安自然科学院"] # 多值用,组织
# 键值对之间可以嵌套使用 {,}
"name" :{
"newName" : "北京理工大学"
"oldName" : "延安自然科学院"
}
# YAML
# 无类型的键值对 通过缩进来表达所属关系 - 表示并列的信息 #表示注释 |表示整块数据
name : 北京理工大学
name :
newname:北京理工大学
oldname:延安自然科学院
name:
-北京理工大学
-延安自然科学院
信息形式 | 优缺点 |
---|
XML | 最早的通用信息标记语言,可扩展性好,但繁琐;适合Internet上的信息交互与传递 | JSON | 信息有类型,适合程序处理(js),较XML简洁;移动应用云端和节点的信息通信,无注释 | YAML | 信息无类型,文本信息比例最高,可读性好;各类系统的配置文件,有注释易读 |
信息提取
方法一:完整解析信息的标记形式,再提取关键信息 XML JSON YAML 需要标记解析器 例如bs4库的标签树遍历 优点:信息解析准确 缺点:提取过程繁琐,速度慢 方法二:无视标记形式,直接搜索关键信息 搜素 对信息的文本查找函数即可 优点:提取过程简介,速度较快 缺点:提取结果准确性与信息内容相关 融合方法 结合形式解析与搜索方法,提取关键信息 XML JSON YAML 搜索 需要标记解析器及文本查找函数
if __name__ == "__main__" :
url = 'http://python123.io/ws/demo.html'
demo = getHTMLText(url)
soup = BeautifulSoup(demo,'html.parser')
for link in soup.find_all('a'):
print(link.get('href'))
<>.fina_all(name,attrs,recursive,string,**kwargs ) # 返回一个列表
# name: 对标签名称的检索字符串
soup.find_all('a')
soup.find_all(['a','b']) # 返含有回a 返回含有b的标签
soup = BeautifulSoup(demo,'html.parser')
for tag in soup.find_all(True):
print(tag.name)
>>> html head title body p b p a a
import re
for tag in soup.find_all(re.compile('b')):
print(tag.name)
>>> b body
# attrs 对标签属性值的检索字符串,可标准属性检索
soup.find_all(id='link1')
soup.find_all('p','course')
# recursive 是否对子孙全部检索,默认True
# string <>...</> 中字符串区域的检索字符串
# find_all 特别常用所以有简写形式
<tag>(..)等价于<tag>.find_all(..)
方法 | 说明 |
---|
<>.find() | 搜索且只返回一个结果,同find_all()参数 | <>.find_parents() | 在先辈节点中搜索,返回列表类型,同find_all()参数 | <>.find_parent() | 在先辈节点中返回一个结果,同find_all()参数 | <>.find_next_siblings() | 在后续平行节点中搜索,返回列表类型,同find_all()参数 | <>.find_next_sibling() | 在后续节点中返回一个结果,同find_all()参数 | <>.find_previous_siblings() | 在前序平行节点中搜索,返回列表类型,同find_all()参数 | <>.find_previous_sibling() | 在前序平行节点中返回一个结果,同find_all()参数 |
|