爬虫入门的学习比我想象的要简单,目前已经掌握了固定网页的定向爬取。
网页爬取
Requests库的7个主要方法
requests.request() 构造一个请求,支撑以下各方法的基础方法
requests.get() 获取HTML网页的主要方法,对应于HTTP的GET
requests.head() 获取HTML网页头信息的方法,对应于HTTP的HEAD
requests.post() 向HTML网页提交POST请求的方法,对应于HTTP的POST
requests.put() 向HTML网页提交PUT请求的方法,对应于HTTP的PUT
requests.patch() 向HTML网页提交局部修改的请求,对应于HTTP的PATCH
requests.delete() 向HTML网页提交删除请求,对应于HTTP的DELETE
get()和head()对于普通用户来说是最常用的。
requests.request()
requests.request(method, url, **kwargs)
method: 请求方式,包括7种
GET HEAD POST PUT PATCH delete OPTIONS
**kwargs: 控制访问的参数,均为可选项
#重点掌握
params: 字典或字节序列,作为参数增加到url中,对url进行修改
data: 字典、字节序列、文件对象,作为Request的内容(作为向服务器提交资源时使用)
json: JSON格式的数据,作为Request的内容,用法同data参数
headers: 字典,“定制访问某个URL的HTTP的协议头”
cookies: 字典或CookieJar,Request中的cookie。“从HTTP协议中解析cookie”
auth: 元组,支持HTTP认证功能
files: 字典,向服务器传输文件
timeout: 设定超时时间,秒为单位。请求的内容超时了还没有返回,就会返回一个timeout异常
proxies: 字典类型,设定访问代理服务器,可以增加登录认证(隐藏用户爬取网页原的IP地址信息)
#开关字段
allow_redirects: True/False, 默认为True, 重定向开关
stream: True/False, 默认为True, 获取内容立即下载开关
verify: T/F,默认为T,认证SSL证书开关
cert: 保存本地SSL证书路径
用法举例
#params
kv={"key1": "value1", "key2": "value2"}
r=requests.request("GET", "http://python123.io/ws", params=kv)
print(r.url)
https://python123.io/ws?key1=value1&key2=value2
#data
#同下文requests.post()的用法
#headers
hd={"user-agent":"chrome/10"}
r=requests.request("POST", "http://python123.io/ws", headers=hd)
##模拟chrome v10浏览器向服务器发起访问
#files
fs={"file": open("tmp.xlsx", "rb")}
r=requests.request("POST", "http://python123.io/ws", files=fs)
#proxies
pxs={'http': 'http://user:pass@10.10.10.1:1234', 'https': 'http://10.10.10.1:4321'}
r=requests.request("GET", "http://www.baidu.com", proxies=pxs)
requests.get()
requests库最常用方法
#标准用法:
#requests.get(url, params=None, **kwargs)
#r=requests.get(url)
#右边构造一个向服务器请求资源的Requests对象,此对象是Requests库内部生成的
#左边是返回的一个包含服务器所有相关资源的Response对象
r=requests.get("http://m.dict.cn/msearch.php?q=request")
r.status_code #状态码200为连接成功
r.encodeing="utf-8"
r.text
requests.head()
当资源很大时,用head()方法获得资源概要
#标准用法:
#requests.head(url, **kwargs)
r=requests.head("http://www.chinadaily.com.cn/a/201910/01/WS5d92b7a1a310cf3e3556e759.html")
r.headers
requests.post()
#标准用法:
#requests.post(url,data=None,json=None,**kwargs)
test_dict={"user_name": "hsy", "user_age": "23"}
r=requests.post("http://httpbin.org/post", data=test_dict)
print(r.text)
{
"args": {},
"data": "",
"files": {},
"form": {
"user_age": "23",
"user_name": "hsy"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "25",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0"
},
"json": null,
"origin": "115.27.203.85, 115.27.203.85",
"url": "https://httpbin.org/post"
}
向URL POST一个字典, 自动编码为form
r=requests.post("http://httpbin.org/post", data="huangsiyuan")
print(r.text)
{
"args": {},
"data": "huangsiyuan",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "11",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.22.0"
},
"json": null,
"origin": "115.27.203.85, 115.27.203.85",
"url": "https://httpbin.org/post"
}
追加字符串则是在"data"中
requests.put()
与上面类似,只是有覆盖操作
#标准用法:
requests.put(url,data=None,**kwargs)
requests.patch()
requests.patch(url,data=None,**kwargs)
requests.delete()
requests.delete(url,**kwargs)
Response对象的属性
r.status_code HTTP请求的返回状态,200表示连接成功,404(只要不是200)表示失败
r.text url对应的页面内容
r.encodeing 从HTTP header中猜测页面内容的编码方式
r.apparent_encoding 从内容文本中分析出编码方式(备选编码,如果文本中有中文,需将r.apparent_encoding赋值给r.encodeing)
r.content 页面内容的二进制形式
Requests库的异常
requests.ConnectTimeout 连接远程服务器超时(一个预定时间)异常
requests.ConnectionError 网络连接错误异常,如DNS查询失败、(服务器防火墙)拒绝连接等
requests.HTTPError HTTP错误异常
requests.URLRequired URL缺失异常
requests.TooManyRedirects 超过最大重定向次数,产生重定向异常
requests.Timeout 请求URL(到获得返回内容整个过程)超时,产生超时异常
爬取网页的通用代码框架
r.raise_for_status() 如果状态码不是200就会爆出requests.HTTPError,利用这个特性可以放在代码中检验
Python中异常处理用到try except
import 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 "产生异常"
if __name__ == "__main__":
url="http://www.chinadaily.com.cn/"
print(getHTMLText(url))
HTTP协议
url格式
#http://host[:port][path]
#host: 合法的Internet主机`域名`或`IP地址`
#port: 端口号,缺省端口为80
#path: 请求资源的内部路径
http://www.chinadaily.com.cn/a/201910/01/WS5d92b7a1a310cf3e3556e759.html
如何理解URL
URL是通过HTTP协议存取资源的Internet路径,一个URL对应一个数据资源。好比电脑上一个文件(夹)路径对应一个文件(夹)。
HTTP协议对资源的操作
GET 请求获取URL的资源
HEAD 请求获取URL位置资源的头部信息
POST 请求向URL位置的资源后附加新的数据
PUT 请求向URL位置存储一个资源,覆盖原URL位置的资源
PATCH 改变该处资源的部分内容
DELETE 请求删除URL位置存储的资源
与Requests库中的6个方法一致
关于网络爬虫
网络爬虫的尺寸
爬取网页 Requests库 占比90%
爬取网站 Scrapy库
爬取全网(为了建立搜索引擎,如百度,谷歌背后的爬虫)
网络爬虫的限制
- 来源审查:判断User-Agent进行限制
检查来访HTTP协议头的User-Agent域,只响应浏览器或友好爬虫的访问 - 发布公告:Robots协议
告知所有爬虫网站的爬取策略,要求爬虫遵守
Robots协议
作用:网站告知网络爬虫哪些页面可以爬取,哪些不行
形式:在网站根目录下的robots.txt文件
使用
- 网络爬虫:自动或人工识别robots.txt,再进行内容爬取
- 约束性:商业用途格外小心;可以不遵守但存在法律风险
类人行为可不参考Robots协议,比如一天只访问一个网页几次
网页解析
Beautiful Soup
HTML格式
例子
<html><head><title>This is a python demo page</title></head><body><p class="title"><b>The demo python introduces several python courses.</b></p><p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:<a href="http://www.icourse163.org/course/BIT-268001" class="py1" id="link1">Basic Python</a> and <a href="http://www.icourse163.org/course/BIT-1001870001" class="py2" id="link2">Advanced Python</a>.</p></body></html>
标签的基本结构
基本用法
import requests
#课程的例子
#r=requests.get("https://www.python123.io/ws/demo.html")
#在海词词典网页版上面查询单词:prologue。url如下,判断接口的什么样子。
r=requests.get("http://m.dict.cn/msearch.php?q=prologue")
r.status_code
r.text
demo=r.text #此时demo是一个html格式的信息
from bs4 import BeautifulSoup #从库中引入一个类
#在提供了demo, 解析器之后生成一个名叫soup的对象
soup=BeautifulSoup(demo,"html.parser")
print(soup.prettify())
最终可以解析成树形结构
或者直接从HTML文件开始解析
理解:HTML文档,标签树,BeautifulSoup类三者等价
BeautifulSoup库的解析器
解析器 用法 条件
bs4的HTML解析器 BeautifulSoup(mk,'html.parser') 安装bs4库
lxml的HTML解析器 BeautifulSoup(mk,'lxml') 安装lxml
lxml的XML解析器 BeautifulSoup(mk,'xml') 安装lxml
html5lib的解析器 BeautifulSoup(mk,'html5lib') 安装html5lib
BeautifulSoup类的基本元素
获取标签内容
任何符合HTML语法的标签都可以通过类似soup.tag获取
>>> soup.strong
<strong>开场白;序言;序幕</strong>
当相同名称的标签有多个时,上述用法会返回第一个
>>> soup.ol
<ol>
<li>序言</li>
<li>开场白</li>
<li>序幕</li>
<li>序诗</li>
<li>序幕性事件</li>
<li>序</li>
<li>开端</li>
<li>作开场白的演员</li>
</ol>
>>> soup.ol.li
<li>序言</li>
Name
>>> soup.strong.name
'strong'
>>> soup.strong.parent.name
'li'
属性
>>> soup.div.attrs
{'class': ['tm']}
#查看具体的value
>>> soup.div.attrs['class']
['tm']
标签内部的字符串
>>> soup.strong.string
'开场白;序言;序幕'
注释
在HTML格式中,代表注释信息
基于bs4库的HTML网页内容遍历方法
标签树的下行遍历
.contents 子节点的列表,将<tag>所有F1节点存入列表(在列表中不只有tag,还可能有"\n"等字符串)
.children 子节点的迭代类型,与.content类似,用于循环遍历F1节点
.descendants 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历
#后面两个用法类似
for child in soup.ol.children:
print(child)
标签树的上行遍历
.parent 节点的父亲标签
.parents 节点先辈标签的迭代类型,用于循环遍历先辈节点
for parent in soup.strong.parents:
if parent is None:
print(parent)
else:
print(parent.name)
标签树的平行遍历
.next_sibling 返回按照HTML文本顺序的下一个平行节点标签(可能会返回字符串,如"\n")
.previous_sibling 返回按照HTML文本顺序的上一个平行节点标签
.next_siblings 迭代类型,返回按照HTML文本顺序的后续所有平行节点标签
.previous_siblings 迭代类型,返回按照HTML文本顺序的前续所有平行节点标签
基于bs4库的HTML格式输出
prettify()方法为标签树各个层次更友好地输出
信息标记和提取
信息标记的3种方法
XML(HTML属于此类) 最早的通用信息标记语言,可扩展性好,但繁琐;Internet上的信息交互与传递
JSON 信息有类型,适合程序处理(js),较XML简洁;移动应用云端和节点的信息通信,程序对接口的处理,无注释
YAML 信息无类型,文本信息比例最高,可读性好;各类系统的配置文件,有注释易读
信息提取的一般方法
如何从已标记的信息中提取想要的部分?
方法一
完整解析信息的标记形式(3种),再提取关键信息
需要标记解析器,如:bs4库的标签树遍历
优点:准确
缺点:繁琐,耗时
方法二
无视标记形式,直接搜索关键信息
对信息的文本使用查找函数即可
优点:简洁,快
缺点:提取准确性与信息内容复杂程度相关
融合方法
需要标记解析器及文本查找函数
实例
提取HTML中所有URL链接
思路:
- 搜索所有<a>标签
- 解析<a>标签格式,提取href后的链接内容
for link in soup.find_all('a'):
print(link.get('href'))
基于bs4库的HTML内容查找方法
.find_all(name, attrs, recursive, string, **kwargs) 返回一个列表类型,存储查找的结果
name: 对标签名称的检索字符串
soup.find_all('strong') 寻找strong标签
for tag in soup.find_all(True):print(tag.name) 打印所有标签名称
attrs: 对标签属性值的检索字符串,可标注属性检索
soup.find_all('div','cl')
soup.find_all(id="dbann") #此时是精确查找
soup.find_all(id=re.compile('d')) #正则查找
recursive: 是否对子孙全部搜索,默认为True
soup.find_all('li',recursive=False)
string: <>...</>中字符串区域的检索字符串
soup.find_all(string="海词词典") #此时是精确查找
soup.find_all(string=re.compile("海词"))
综上:正则表达式+find_all()
简写形式
soup(...)等价于soup.find_all(...)
<tag>(...)等价于soup.find_all(...)
拓展方法
参数同find_all(), 不一定常用
.find() 搜索且只返回一个结果,字符串类型
.find_parents() 在先辈节点中搜索,返回列表类型
.find_parent() 在先辈节点中返回一个结果,字符串类型
.find_next_siblings() 在后续平行节点中搜索,返回列表类型
.find_next_sibling() 在后续平行节点中搜索,字符串类型
.find_previous_siblings() 在前续平行节点中搜索,返回列表类型
.find_previous_sibling() 在前续平行节点中返回一个结果,字符串类型
前面都需要接tag标签,如果是一个具体的bs对象,比如上文的soup,则相当于根节点,查找时不包括当前节点。
print()的格式化输出
print("{:^10}\t{:>6}\t{:6}\t{:^10}".format("排名","学校名称","地区","总分"))
#每一个槽从左到右可以设置哪些东西
: 引导符号
<填充> 用于填充的单个字符
<对齐> <左对齐 >右对齐 ^居中对齐
<宽度> 槽的设定输出宽度
, 数字的千位分隔符适用于整数和浮点数
<.精度> 浮点数小数部分的精度或者字符串的最大输出长度
<类型> 整数类型b,c,d,o,x,X 浮点数类型e,E,f,%
tplt="{0:^10}\t{1:{4}^10}\t{2:{4}^10}\t{3:^10}"
print(tplt.format("排名","学校名称","地区","总分",chr(12288)))