当前位置: 首页>移动开发>正文

爬虫学习笔记(1)

爬虫入门的学习比我想象的要简单,目前已经掌握了固定网页的定向爬取。

爬虫学习笔记(1),第1张
爬虫学习笔记(1),第2张

网页爬取

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>

标签的基本结构

爬虫学习笔记(1),第3张
基本用法
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类的基本元素
爬虫学习笔记(1),第4张

获取标签内容
任何符合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链接
思路:

  1. 搜索所有<a>标签
  2. 解析<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)))

https://www.xamrdz.com/mobile/4n41896845.html

相关文章: